diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..ff261bad --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,9 @@ +ARG VARIANT="3.9" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} + +USER vscode + +RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.44.0" RYE_INSTALL_OPTION="--yes" bash +ENV PATH=/home/vscode/.rye/shims:$PATH + +RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..c17fdc16 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,43 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Debian", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + + "postStartCommand": "rye sync --all-features", + + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "python.pythonPath": ".venv/bin/python", + "python.defaultInterpreterPath": ".venv/bin/python", + "python.typeChecking": "basic", + "terminal.integrated.env.linux": { + "PATH": "/home/vscode/.rye/shims:${env:PATH}" + } + } + } + }, + "features": { + "ghcr.io/devcontainers/features/node:1": {} + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..ec9a4813 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,98 @@ +name: CI +on: + push: + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' + pull_request: + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' + +jobs: + lint: + timeout-minutes: 10 + name: lint + runs-on: ${{ github.repository == 'stainless-sdks/gradient-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + run: rye sync --all-features + + - name: Run lints + run: ./scripts/lint + + build: + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + timeout-minutes: 10 + name: build + permissions: + contents: read + id-token: write + runs-on: ${{ github.repository == 'stainless-sdks/gradient-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + run: rye sync --all-features + + - name: Run build + run: rye build + + - name: Get GitHub OIDC Token + if: github.repository == 'stainless-sdks/gradient-python' + id: github-oidc + uses: actions/github-script@v6 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Upload tarball + if: github.repository == 'stainless-sdks/gradient-python' + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + run: ./scripts/utils/upload-artifact.sh + + test: + timeout-minutes: 10 + name: test + runs-on: ${{ github.repository == 'stainless-sdks/gradient-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Run tests + run: ./scripts/test diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 00000000..79ee5b7d --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,31 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to PyPI in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/digitalocean/gradient-python/actions/workflows/publish-pypi.yml +name: Publish PyPI +on: + workflow_dispatch: + + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Publish to PyPI + run: | + bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.GRADIENT_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 00000000..9c8912bc --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,21 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'digitalocean/gradient-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v4 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + PYPI_TOKEN: ${{ secrets.GRADIENT_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..95ceb189 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.prism.log +_dev + +__pycache__ +.mypy_cache + +dist + +.venv +.idea + +.env +.envrc +codegen.log +Brewfile.lock.json diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..43077b24 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.9.18 diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..078b9e28 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "3.8.0" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 00000000..29f00c95 --- /dev/null +++ b/.stats.yml @@ -0,0 +1,4 @@ +configured_endpoints: 188 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/digitalocean%2Fgradient-f07d74847e620dfa26d8df40ea4680814af9bba381b3a57a7b6ed76ad49d85f8.yml +openapi_spec_hash: e3553dc2abf2afd4368b736bcc32a289 +config_hash: b28984dd49d4baf1d68572efe83ac103 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..5b010307 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.importFormat": "relative", +} diff --git a/Brewfile b/Brewfile new file mode 100644 index 00000000..492ca37b --- /dev/null +++ b/Brewfile @@ -0,0 +1,2 @@ +brew "rye" + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..212c4e40 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,128 @@ +## Setting up the environment + +### With Rye + +We use [Rye](https://rye.astral.sh/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: + +```sh +$ ./scripts/bootstrap +``` + +Or [install Rye manually](https://rye.astral.sh/guide/installation/) and run: + +```sh +$ rye sync --all-features +``` + +You can then run scripts using `rye run python script.py` or by activating the virtual environment: + +```sh +# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work +$ source .venv/bin/activate + +# now you can omit the `rye run` prefix +$ python script.py +``` + +### Without Rye + +Alternatively if you don't want to install `Rye`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command: + +```sh +$ pip install -r requirements-dev.lock +``` + +## Modifying/Adding code + +Most of the SDK is generated code. Modifications to code will be persisted between generations, but may +result in merge conflicts between manual patches and changes from the generator. The generator will never +modify the contents of the `src/gradient/lib/` and `examples/` directories. + +## Adding and running examples + +All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. + +```py +# add an example to examples/.py + +#!/usr/bin/env -S rye run python +… +``` + +```sh +$ chmod +x examples/.py +# run the example against your api +$ ./examples/.py +``` + +## Using the repository from source + +If you’d like to use the repository from source, you can either install from git or link to a cloned repository: + +To install via git: + +```sh +$ pip install git+ssh://git@github.com/digitalocean/gradient-python.git +``` + +Alternatively, you can build from source and install the wheel file: + +Building this package will create two files in the `dist/` directory, a `.tar.gz` containing the source files and a `.whl` that can be used to install the package efficiently. + +To create a distributable version of the library, all you have to do is run this command: + +```sh +$ rye build +# or +$ python -m build +``` + +Then to install: + +```sh +$ pip install ./path-to-wheel-file.whl +``` + +## Running tests + +Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. + +```sh +# you will need npm installed +$ npx prism mock path/to/your/openapi.yml +``` + +```sh +$ ./scripts/test +``` + +## Linting and formatting + +This repository uses [ruff](https://github.com/astral-sh/ruff) and +[black](https://github.com/psf/black) to format the code in the repository. + +To lint: + +```sh +$ ./scripts/lint +``` + +To format and fix all ruff issues automatically: + +```sh +$ ./scripts/format +``` + +## Publishing and releases + +Changes made to this repository via the automated release PR pipeline should publish to PyPI automatically. If +the changes aren't made through the automated pipeline, you may want to make releases manually. + +### Publish with a GitHub workflow + +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/digitalocean/gradient-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. + +### Publish manually + +If you need to manually release a package, you can run the `bin/publish-pypi` script with a `PYPI_TOKEN` set on +the environment. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..656d8887 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Gradient + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index b53f1288..d9bc6f1b 100644 --- a/README.md +++ b/README.md @@ -1 +1,495 @@ -# digitalocean-genai-sdk-python \ No newline at end of file +# Gradient Python API library + + +[![PyPI version](https://img.shields.io/pypi/v/gradient.svg?label=pypi%20(stable))](https://pypi.org/project/gradient/) + +The Gradient Python library provides convenient access to the Gradient REST API from any Python 3.9+ +application. The library includes type definitions for all request params and response fields, +and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). + +It is generated with [Stainless](https://www.stainless.com/). + +## Documentation + +The REST API documentation can be found on [developers.digitalocean.com](https://developers.digitalocean.com/documentation/v2/). The full API of this library can be found in [api.md](api.md). + +## Installation + +```sh +# install from PyPI +pip install gradient +``` + +## Usage + +The full API of this library can be found in [api.md](api.md). + +```python +import os +from gradient import Gradient + +client = Gradient( + model_access_key=os.environ.get( + "GRADIENT_MODEL_ACCESS_KEY" + ), # This is the default and can be omitted +) + +completion = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", +) +print(completion.choices) +``` + +While you can provide a `access_token` keyword argument, +we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) +to add `DIGITALOCEAN_ACCESS_TOKEN="My Access Token"` to your `.env` file +so that your Access Token is not stored in source control. + +## Async usage + +Simply import `AsyncGradient` instead of `Gradient` and use `await` with each API call: + +```python +import os +import asyncio +from gradient import AsyncGradient + +client = AsyncGradient( + model_access_key=os.environ.get( + "GRADIENT_MODEL_ACCESS_KEY" + ), # This is the default and can be omitted +) + + +async def main() -> None: + completion = await client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", + ) + print(completion.choices) + + +asyncio.run(main()) +``` + +Functionality between the synchronous and asynchronous clients is otherwise identical. + +### With aiohttp + +By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. + +You can enable this by installing `aiohttp`: + +```sh +# install from PyPI +pip install gradient[aiohttp] +``` + +Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: + +```python +import asyncio +from gradient import DefaultAioHttpClient +from gradient import AsyncGradient + + +async def main() -> None: + async with AsyncGradient( + model_access_key="My Model Access Key", + http_client=DefaultAioHttpClient(), + ) as client: + completion = await client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", + ) + print(completion.choices) + + +asyncio.run(main()) +``` + +## Streaming responses + +We provide support for streaming responses using Server Side Events (SSE). + +```python +from gradient import Gradient + +client = Gradient() + +stream = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", + stream=True, +) +for completion in stream: + print(completion.choices) +``` + +The async client uses the exact same interface. + +```python +from gradient import AsyncGradient + +client = AsyncGradient() + +stream = await client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", + stream=True, +) +async for completion in stream: + print(completion.choices) +``` + +## Using types + +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: + +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` + +Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. + +## Nested params + +Nested parameters are dictionaries, typed using `TypedDict`, for example: + +```python +from gradient import Gradient + +client = Gradient() + +completion = client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream_options={}, +) +print(completion.stream_options) +``` + +## Handling errors + +When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `gradient.APIConnectionError` is raised. + +When the API returns a non-success status code (that is, 4xx or 5xx +response), a subclass of `gradient.APIStatusError` is raised, containing `status_code` and `response` properties. + +All errors inherit from `gradient.APIError`. + +```python +import gradient +from gradient import Gradient + +client = Gradient() + +try: + client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", + ) +except gradient.APIConnectionError as e: + print("The server could not be reached") + print(e.__cause__) # an underlying Exception, likely raised within httpx. +except gradient.RateLimitError as e: + print("A 429 status code was received; we should back off a bit.") +except gradient.APIStatusError as e: + print("Another non-200-range status code was received") + print(e.status_code) + print(e.response) +``` + +Error codes are as follows: + +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | + +### Retries + +Certain errors are automatically retried 2 times by default, with a short exponential backoff. +Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, +429 Rate Limit, and >=500 Internal errors are all retried by default. + +You can use the `max_retries` option to configure or disable retry settings: + +```python +from gradient import Gradient + +# Configure the default for all requests: +client = Gradient( + # default is 2 + max_retries=0, +) + +# Or, configure per-request: +client.with_options(max_retries=5).chat.completions.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", +) +``` + +### Timeouts + +By default requests time out after 1 minute. You can configure this with a `timeout` option, +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: + +```python +from gradient import Gradient + +# Configure the default for all requests: +client = Gradient( + # 20 seconds (default is 1 minute) + timeout=20.0, +) + +# More granular control: +client = Gradient( + timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0), +) + +# Override per-request: +client.with_options(timeout=5.0).chat.completions.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", +) +``` + +On timeout, an `APITimeoutError` is thrown. + +Note that requests that time out are [retried twice by default](#retries). + +## Advanced + +### Logging + +We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. + +You can enable logging by setting the environment variable `GRADIENT_LOG` to `info`. + +```shell +$ export GRADIENT_LOG=info +``` + +Or to `debug` for more verbose logging. + +### How to tell whether `None` means `null` or missing + +In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: + +```py +if response.my_field is None: + if 'my_field' not in response.model_fields_set: + print('Got json like {}, without a "my_field" key present at all.') + else: + print('Got json like {"my_field": null}.') +``` + +### Accessing raw response data (e.g. headers) + +The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g., + +```py +from gradient import Gradient + +client = Gradient() +response = client.chat.completions.with_raw_response.create( + messages=[{ + "role": "user", + "content": "What is the capital of France?", + }], + model="llama3.3-70b-instruct", +) +print(response.headers.get('X-My-Header')) + +completion = response.parse() # get the object that `chat.completions.create()` would have returned +print(completion.choices) +``` + +These methods return an [`APIResponse`](https://github.com/digitalocean/gradient-python/tree/main/src/gradient/_response.py) object. + +The async client returns an [`AsyncAPIResponse`](https://github.com/digitalocean/gradient-python/tree/main/src/gradient/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. + +#### `.with_streaming_response` + +The above interface eagerly reads the full response body when you make the request, which may not always be what you want. + +To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. + +```python +with client.chat.completions.with_streaming_response.create( + messages=[ + { + "role": "user", + "content": "What is the capital of France?", + } + ], + model="llama3.3-70b-instruct", +) as response: + print(response.headers.get("X-My-Header")) + + for line in response.iter_lines(): + print(line) +``` + +The context manager is required so that the response will reliably be closed. + +### Making custom/undocumented requests + +This library is typed for convenient access to the documented API. + +If you need to access undocumented endpoints, params, or response properties, the library can still be used. + +#### Undocumented endpoints + +To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other +http verbs. Options on the client will be respected (such as retries) when making this request. + +```py +import httpx + +response = client.post( + "/foo", + cast_to=httpx.Response, + body={"my_param": True}, +) + +print(response.headers.get("x-foo")) +``` + +#### Undocumented request params + +If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request +options. + +#### Undocumented response properties + +To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You +can also get all the extra fields on the Pydantic model as a dict with +[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra). + +### Configuring the HTTP client + +You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: + +- Support for [proxies](https://www.python-httpx.org/advanced/proxies/) +- Custom [transports](https://www.python-httpx.org/advanced/transports/) +- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality + +```python +import httpx +from gradient import Gradient, DefaultHttpxClient + +client = Gradient( + # Or use the `GRADIENT_BASE_URL` env var + base_url="http://my.test.server.example.com:8083", + http_client=DefaultHttpxClient( + proxy="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +You can also customize the client on a per-request basis by using `with_options()`: + +```python +client.with_options(http_client=DefaultHttpxClient(...)) +``` + +### Managing HTTP resources + +By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. + +```py +from gradient import Gradient + +with Gradient() as client: + # make requests here + ... + +# HTTP client is now closed +``` + +## Versioning + +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: + +1. Changes that only affect static types, without breaking runtime behavior. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +3. Changes that we do not expect to impact the vast majority of users in practice. + +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. + +We are keen for your feedback; please open an [issue](https://www.github.com/digitalocean/gradient-python/issues) with questions, bugs, or suggestions. + +### Determining the installed version + +If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version. + +You can determine the version that is being used at runtime with: + +```py +import gradient +print(gradient.__version__) +``` + +## Requirements + +Python 3.9 or higher. + +## Contributing + +See [the contributing documentation](./CONTRIBUTING.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..fe1c055c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,23 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainless.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by Gradient, please follow the respective company's security reporting guidelines. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/api.md b/api.md new file mode 100644 index 00000000..e32fae32 --- /dev/null +++ b/api.md @@ -0,0 +1,1029 @@ +# Shared Types + +```python +from gradient.types import ( + Action, + ActionLink, + APILinks, + APIMeta, + BackwardLinks, + ChatCompletionChunk, + ChatCompletionTokenLogprob, + CompletionUsage, + DiskInfo, + Droplet, + DropletNextBackupWindow, + FirewallRuleTarget, + ForwardLinks, + GarbageCollection, + GPUInfo, + Image, + ImageGenCompletedEvent, + ImageGenPartialImageEvent, + ImageGenStreamEvent, + Kernel, + MetaProperties, + NetworkV4, + NetworkV6, + PageLinks, + Region, + Size, + Snapshots, + Subscription, + SubscriptionTierBase, + VpcPeering, +) +``` + +# Agents + +Types: + +```python +from gradient.types import ( + APIAgent, + APIAgentAPIKeyInfo, + APIAgentModel, + APIAnthropicAPIKeyInfo, + APIDeploymentVisibility, + APIOpenAIAPIKeyInfo, + APIRetrievalMethod, + APIWorkspace, + AgentCreateResponse, + AgentRetrieveResponse, + AgentUpdateResponse, + AgentListResponse, + AgentDeleteResponse, + AgentRetrieveUsageResponse, + AgentUpdateStatusResponse, +) +``` + +Methods: + +- client.agents.create(\*\*params) -> AgentCreateResponse +- client.agents.retrieve(uuid) -> AgentRetrieveResponse +- client.agents.update(path_uuid, \*\*params) -> AgentUpdateResponse +- client.agents.list(\*\*params) -> AgentListResponse +- client.agents.delete(uuid) -> AgentDeleteResponse +- client.agents.retrieve_usage(uuid, \*\*params) -> AgentRetrieveUsageResponse +- client.agents.update_status(path_uuid, \*\*params) -> AgentUpdateStatusResponse + +## APIKeys + +Types: + +```python +from gradient.types.agents import ( + APIKeyCreateResponse, + APIKeyUpdateResponse, + APIKeyListResponse, + APIKeyDeleteResponse, + APIKeyRegenerateResponse, +) +``` + +Methods: + +- client.agents.api_keys.create(path_agent_uuid, \*\*params) -> APIKeyCreateResponse +- client.agents.api_keys.update(path_api_key_uuid, \*, path_agent_uuid, \*\*params) -> APIKeyUpdateResponse +- client.agents.api_keys.list(agent_uuid, \*\*params) -> APIKeyListResponse +- client.agents.api_keys.delete(api_key_uuid, \*, agent_uuid) -> APIKeyDeleteResponse +- client.agents.api_keys.regenerate(api_key_uuid, \*, agent_uuid) -> APIKeyRegenerateResponse + +## Chat + +### Completions + +Types: + +```python +from gradient.types.agents.chat import CompletionCreateResponse +``` + +Methods: + +- client.agents.chat.completions.create(\*\*params) -> CompletionCreateResponse + +## EvaluationMetrics + +Types: + +```python +from gradient.types.agents import EvaluationMetricListResponse, EvaluationMetricListRegionsResponse +``` + +Methods: + +- client.agents.evaluation_metrics.list() -> EvaluationMetricListResponse +- client.agents.evaluation_metrics.list_regions(\*\*params) -> EvaluationMetricListRegionsResponse + +### Workspaces + +Types: + +```python +from gradient.types.agents.evaluation_metrics import ( + WorkspaceCreateResponse, + WorkspaceRetrieveResponse, + WorkspaceUpdateResponse, + WorkspaceListResponse, + WorkspaceDeleteResponse, + WorkspaceListEvaluationTestCasesResponse, +) +``` + +Methods: + +- client.agents.evaluation_metrics.workspaces.create(\*\*params) -> WorkspaceCreateResponse +- client.agents.evaluation_metrics.workspaces.retrieve(workspace_uuid) -> WorkspaceRetrieveResponse +- client.agents.evaluation_metrics.workspaces.update(path_workspace_uuid, \*\*params) -> WorkspaceUpdateResponse +- client.agents.evaluation_metrics.workspaces.list() -> WorkspaceListResponse +- client.agents.evaluation_metrics.workspaces.delete(workspace_uuid) -> WorkspaceDeleteResponse +- client.agents.evaluation_metrics.workspaces.list_evaluation_test_cases(workspace_uuid) -> WorkspaceListEvaluationTestCasesResponse + +#### Agents + +Types: + +```python +from gradient.types.agents.evaluation_metrics.workspaces import AgentListResponse, AgentMoveResponse +``` + +Methods: + +- client.agents.evaluation_metrics.workspaces.agents.list(workspace_uuid, \*\*params) -> AgentListResponse +- client.agents.evaluation_metrics.workspaces.agents.move(path_workspace_uuid, \*\*params) -> AgentMoveResponse + +### Anthropic + +#### Keys + +Types: + +```python +from gradient.types.agents.evaluation_metrics.anthropic import ( + KeyCreateResponse, + KeyRetrieveResponse, + KeyUpdateResponse, + KeyListResponse, + KeyDeleteResponse, + KeyListAgentsResponse, +) +``` + +Methods: + +- client.agents.evaluation_metrics.anthropic.keys.create(\*\*params) -> KeyCreateResponse +- client.agents.evaluation_metrics.anthropic.keys.retrieve(api_key_uuid) -> KeyRetrieveResponse +- client.agents.evaluation_metrics.anthropic.keys.update(path_api_key_uuid, \*\*params) -> KeyUpdateResponse +- client.agents.evaluation_metrics.anthropic.keys.list(\*\*params) -> KeyListResponse +- client.agents.evaluation_metrics.anthropic.keys.delete(api_key_uuid) -> KeyDeleteResponse +- client.agents.evaluation_metrics.anthropic.keys.list_agents(uuid, \*\*params) -> KeyListAgentsResponse + +### OpenAI + +#### Keys + +Types: + +```python +from gradient.types.agents.evaluation_metrics.openai import ( + KeyCreateResponse, + KeyRetrieveResponse, + KeyUpdateResponse, + KeyListResponse, + KeyDeleteResponse, + KeyListAgentsResponse, +) +``` + +Methods: + +- client.agents.evaluation_metrics.openai.keys.create(\*\*params) -> KeyCreateResponse +- client.agents.evaluation_metrics.openai.keys.retrieve(api_key_uuid) -> KeyRetrieveResponse +- client.agents.evaluation_metrics.openai.keys.update(path_api_key_uuid, \*\*params) -> KeyUpdateResponse +- client.agents.evaluation_metrics.openai.keys.list(\*\*params) -> KeyListResponse +- client.agents.evaluation_metrics.openai.keys.delete(api_key_uuid) -> KeyDeleteResponse +- client.agents.evaluation_metrics.openai.keys.list_agents(uuid, \*\*params) -> KeyListAgentsResponse + +### Oauth2 + +Types: + +```python +from gradient.types.agents.evaluation_metrics import Oauth2GenerateURLResponse +``` + +Methods: + +- client.agents.evaluation_metrics.oauth2.generate_url(\*\*params) -> Oauth2GenerateURLResponse + +#### Dropbox + +Types: + +```python +from gradient.types.agents.evaluation_metrics.oauth2 import DropboxCreateTokensResponse +``` + +Methods: + +- client.agents.evaluation_metrics.oauth2.dropbox.create_tokens(\*\*params) -> DropboxCreateTokensResponse + +### ScheduledIndexing + +Types: + +```python +from gradient.types.agents.evaluation_metrics import ( + ScheduledIndexingCreateResponse, + ScheduledIndexingRetrieveResponse, + ScheduledIndexingDeleteResponse, +) +``` + +Methods: + +- client.agents.evaluation_metrics.scheduled_indexing.create(\*\*params) -> ScheduledIndexingCreateResponse +- client.agents.evaluation_metrics.scheduled_indexing.retrieve(knowledge_base_uuid) -> ScheduledIndexingRetrieveResponse +- client.agents.evaluation_metrics.scheduled_indexing.delete(uuid) -> ScheduledIndexingDeleteResponse + +## EvaluationRuns + +Types: + +```python +from gradient.types.agents import ( + APIEvaluationMetric, + APIEvaluationMetricResult, + APIEvaluationPrompt, + APIEvaluationRun, + EvaluationRunCreateResponse, + EvaluationRunRetrieveResponse, + EvaluationRunListResultsResponse, + EvaluationRunRetrieveResultsResponse, +) +``` + +Methods: + +- client.agents.evaluation_runs.create(\*\*params) -> EvaluationRunCreateResponse +- client.agents.evaluation_runs.retrieve(evaluation_run_uuid) -> EvaluationRunRetrieveResponse +- client.agents.evaluation_runs.list_results(evaluation_run_uuid, \*\*params) -> EvaluationRunListResultsResponse +- client.agents.evaluation_runs.retrieve_results(prompt_id, \*, evaluation_run_uuid) -> EvaluationRunRetrieveResultsResponse + +## EvaluationTestCases + +Types: + +```python +from gradient.types.agents import ( + APIEvaluationTestCase, + APIStarMetric, + EvaluationTestCaseCreateResponse, + EvaluationTestCaseRetrieveResponse, + EvaluationTestCaseUpdateResponse, + EvaluationTestCaseListResponse, + EvaluationTestCaseListEvaluationRunsResponse, +) +``` + +Methods: + +- client.agents.evaluation_test_cases.create(\*\*params) -> EvaluationTestCaseCreateResponse +- client.agents.evaluation_test_cases.retrieve(test_case_uuid, \*\*params) -> EvaluationTestCaseRetrieveResponse +- client.agents.evaluation_test_cases.update(path_test_case_uuid, \*\*params) -> EvaluationTestCaseUpdateResponse +- client.agents.evaluation_test_cases.list() -> EvaluationTestCaseListResponse +- client.agents.evaluation_test_cases.list_evaluation_runs(evaluation_test_case_uuid, \*\*params) -> EvaluationTestCaseListEvaluationRunsResponse + +## EvaluationDatasets + +Types: + +```python +from gradient.types.agents import ( + EvaluationDatasetCreateResponse, + EvaluationDatasetCreateFileUploadPresignedURLsResponse, +) +``` + +Methods: + +- client.agents.evaluation_datasets.create(\*\*params) -> EvaluationDatasetCreateResponse +- client.agents.evaluation_datasets.create_file_upload_presigned_urls(\*\*params) -> EvaluationDatasetCreateFileUploadPresignedURLsResponse + +## Functions + +Types: + +```python +from gradient.types.agents import ( + FunctionCreateResponse, + FunctionUpdateResponse, + FunctionDeleteResponse, +) +``` + +Methods: + +- client.agents.functions.create(path_agent_uuid, \*\*params) -> FunctionCreateResponse +- client.agents.functions.update(path_function_uuid, \*, path_agent_uuid, \*\*params) -> FunctionUpdateResponse +- client.agents.functions.delete(function_uuid, \*, agent_uuid) -> FunctionDeleteResponse + +## Versions + +Types: + +```python +from gradient.types.agents import VersionUpdateResponse, VersionListResponse +``` + +Methods: + +- client.agents.versions.update(path_uuid, \*\*params) -> VersionUpdateResponse +- client.agents.versions.list(uuid, \*\*params) -> VersionListResponse + +## KnowledgeBases + +Types: + +```python +from gradient.types.agents import APILinkKnowledgeBaseOutput, KnowledgeBaseDetachResponse +``` + +Methods: + +- client.agents.knowledge_bases.attach(agent_uuid) -> APILinkKnowledgeBaseOutput +- client.agents.knowledge_bases.attach_single(knowledge_base_uuid, \*, agent_uuid) -> APILinkKnowledgeBaseOutput +- client.agents.knowledge_bases.detach(knowledge_base_uuid, \*, agent_uuid) -> KnowledgeBaseDetachResponse + +## Routes + +Types: + +```python +from gradient.types.agents import ( + RouteUpdateResponse, + RouteDeleteResponse, + RouteAddResponse, + RouteViewResponse, +) +``` + +Methods: + +- client.agents.routes.update(path_child_agent_uuid, \*, path_parent_agent_uuid, \*\*params) -> RouteUpdateResponse +- client.agents.routes.delete(child_agent_uuid, \*, parent_agent_uuid) -> RouteDeleteResponse +- client.agents.routes.add(path_child_agent_uuid, \*, path_parent_agent_uuid, \*\*params) -> RouteAddResponse +- client.agents.routes.view(uuid) -> RouteViewResponse + +# Chat + +## Completions + +Types: + +```python +from gradient.types.chat import CompletionCreateResponse +``` + +Methods: + +- client.chat.completions.create(\*\*params) -> CompletionCreateResponse + +# Images + +Types: + +```python +from gradient.types import ImageGenerateResponse +``` + +Methods: + +- client.images.generate(\*\*params) -> ImageGenerateResponse + +# GPUDroplets + +Types: + +```python +from gradient.types import ( + DropletBackupPolicy, + GPUDropletCreateResponse, + GPUDropletRetrieveResponse, + GPUDropletListResponse, + GPUDropletListFirewallsResponse, + GPUDropletListKernelsResponse, + GPUDropletListNeighborsResponse, + GPUDropletListSnapshotsResponse, +) +``` + +Methods: + +- client.gpu_droplets.create(\*\*params) -> GPUDropletCreateResponse +- client.gpu_droplets.retrieve(droplet_id) -> GPUDropletRetrieveResponse +- client.gpu_droplets.list(\*\*params) -> GPUDropletListResponse +- client.gpu_droplets.delete(droplet_id) -> None +- client.gpu_droplets.delete_by_tag(\*\*params) -> None +- client.gpu_droplets.list_firewalls(droplet_id, \*\*params) -> GPUDropletListFirewallsResponse +- client.gpu_droplets.list_kernels(droplet_id, \*\*params) -> GPUDropletListKernelsResponse +- client.gpu_droplets.list_neighbors(droplet_id) -> GPUDropletListNeighborsResponse +- client.gpu_droplets.list_snapshots(droplet_id, \*\*params) -> GPUDropletListSnapshotsResponse + +## Backups + +Types: + +```python +from gradient.types.gpu_droplets import ( + BackupListResponse, + BackupListPoliciesResponse, + BackupListSupportedPoliciesResponse, + BackupRetrievePolicyResponse, +) +``` + +Methods: + +- client.gpu_droplets.backups.list(droplet_id, \*\*params) -> BackupListResponse +- client.gpu_droplets.backups.list_policies(\*\*params) -> BackupListPoliciesResponse +- client.gpu_droplets.backups.list_supported_policies() -> BackupListSupportedPoliciesResponse +- client.gpu_droplets.backups.retrieve_policy(droplet_id) -> BackupRetrievePolicyResponse + +## Actions + +Types: + +```python +from gradient.types.gpu_droplets import ( + ActionRetrieveResponse, + ActionListResponse, + ActionBulkInitiateResponse, + ActionInitiateResponse, +) +``` + +Methods: + +- client.gpu_droplets.actions.retrieve(action_id, \*, droplet_id) -> ActionRetrieveResponse +- client.gpu_droplets.actions.list(droplet_id, \*\*params) -> ActionListResponse +- client.gpu_droplets.actions.bulk_initiate(\*\*params) -> ActionBulkInitiateResponse +- client.gpu_droplets.actions.initiate(droplet_id, \*\*params) -> ActionInitiateResponse + +## DestroyWithAssociatedResources + +Types: + +```python +from gradient.types.gpu_droplets import ( + AssociatedResource, + DestroyedAssociatedResource, + DestroyWithAssociatedResourceListResponse, + DestroyWithAssociatedResourceCheckStatusResponse, +) +``` + +Methods: + +- client.gpu_droplets.destroy_with_associated_resources.list(droplet_id) -> DestroyWithAssociatedResourceListResponse +- client.gpu_droplets.destroy_with_associated_resources.check_status(droplet_id) -> DestroyWithAssociatedResourceCheckStatusResponse +- client.gpu_droplets.destroy_with_associated_resources.delete_dangerous(droplet_id) -> None +- client.gpu_droplets.destroy_with_associated_resources.delete_selective(droplet_id, \*\*params) -> None +- client.gpu_droplets.destroy_with_associated_resources.retry(droplet_id) -> None + +## Autoscale + +Types: + +```python +from gradient.types.gpu_droplets import ( + AutoscalePool, + AutoscalePoolDropletTemplate, + AutoscalePoolDynamicConfig, + AutoscalePoolStaticConfig, + CurrentUtilization, + AutoscaleCreateResponse, + AutoscaleRetrieveResponse, + AutoscaleUpdateResponse, + AutoscaleListResponse, + AutoscaleListHistoryResponse, + AutoscaleListMembersResponse, +) +``` + +Methods: + +- client.gpu_droplets.autoscale.create(\*\*params) -> AutoscaleCreateResponse +- client.gpu_droplets.autoscale.retrieve(autoscale_pool_id) -> AutoscaleRetrieveResponse +- client.gpu_droplets.autoscale.update(autoscale_pool_id, \*\*params) -> AutoscaleUpdateResponse +- client.gpu_droplets.autoscale.list(\*\*params) -> AutoscaleListResponse +- client.gpu_droplets.autoscale.delete(autoscale_pool_id) -> None +- client.gpu_droplets.autoscale.delete_dangerous(autoscale_pool_id) -> None +- client.gpu_droplets.autoscale.list_history(autoscale_pool_id, \*\*params) -> AutoscaleListHistoryResponse +- client.gpu_droplets.autoscale.list_members(autoscale_pool_id, \*\*params) -> AutoscaleListMembersResponse + +## Firewalls + +Types: + +```python +from gradient.types.gpu_droplets import ( + Firewall, + FirewallCreateResponse, + FirewallRetrieveResponse, + FirewallUpdateResponse, + FirewallListResponse, +) +``` + +Methods: + +- client.gpu_droplets.firewalls.create(\*\*params) -> FirewallCreateResponse +- client.gpu_droplets.firewalls.retrieve(firewall_id) -> FirewallRetrieveResponse +- client.gpu_droplets.firewalls.update(firewall_id, \*\*params) -> FirewallUpdateResponse +- client.gpu_droplets.firewalls.list(\*\*params) -> FirewallListResponse +- client.gpu_droplets.firewalls.delete(firewall_id) -> None + +### Droplets + +Methods: + +- client.gpu_droplets.firewalls.droplets.add(firewall_id, \*\*params) -> None +- client.gpu_droplets.firewalls.droplets.remove(firewall_id, \*\*params) -> None + +### Tags + +Methods: + +- client.gpu_droplets.firewalls.tags.add(firewall_id, \*\*params) -> None +- client.gpu_droplets.firewalls.tags.remove(firewall_id, \*\*params) -> None + +### Rules + +Methods: + +- client.gpu_droplets.firewalls.rules.add(firewall_id, \*\*params) -> None +- client.gpu_droplets.firewalls.rules.remove(firewall_id, \*\*params) -> None + +## FloatingIPs + +Types: + +```python +from gradient.types.gpu_droplets import ( + FloatingIP, + FloatingIPCreateResponse, + FloatingIPRetrieveResponse, + FloatingIPListResponse, +) +``` + +Methods: + +- client.gpu_droplets.floating_ips.create(\*\*params) -> FloatingIPCreateResponse +- client.gpu_droplets.floating_ips.retrieve(floating_ip) -> FloatingIPRetrieveResponse +- client.gpu_droplets.floating_ips.list(\*\*params) -> FloatingIPListResponse +- client.gpu_droplets.floating_ips.delete(floating_ip) -> None + +### Actions + +Types: + +```python +from gradient.types.gpu_droplets.floating_ips import ( + ActionCreateResponse, + ActionRetrieveResponse, + ActionListResponse, +) +``` + +Methods: + +- client.gpu_droplets.floating_ips.actions.create(floating_ip, \*\*params) -> ActionCreateResponse +- client.gpu_droplets.floating_ips.actions.retrieve(action_id, \*, floating_ip) -> ActionRetrieveResponse +- client.gpu_droplets.floating_ips.actions.list(floating_ip) -> ActionListResponse + +## Images + +Types: + +```python +from gradient.types.gpu_droplets import ( + ImageCreateResponse, + ImageRetrieveResponse, + ImageUpdateResponse, + ImageListResponse, +) +``` + +Methods: + +- client.gpu_droplets.images.create(\*\*params) -> ImageCreateResponse +- client.gpu_droplets.images.retrieve(image_id) -> ImageRetrieveResponse +- client.gpu_droplets.images.update(image_id, \*\*params) -> ImageUpdateResponse +- client.gpu_droplets.images.list(\*\*params) -> ImageListResponse +- client.gpu_droplets.images.delete(image_id) -> None + +### Actions + +Types: + +```python +from gradient.types.gpu_droplets.images import ActionListResponse +``` + +Methods: + +- client.gpu_droplets.images.actions.create(image_id, \*\*params) -> Action +- client.gpu_droplets.images.actions.retrieve(action_id, \*, image_id) -> Action +- client.gpu_droplets.images.actions.list(image_id) -> ActionListResponse + +## LoadBalancers + +Types: + +```python +from gradient.types.gpu_droplets import ( + Domains, + ForwardingRule, + GlbSettings, + HealthCheck, + LbFirewall, + LoadBalancer, + StickySessions, + LoadBalancerCreateResponse, + LoadBalancerRetrieveResponse, + LoadBalancerUpdateResponse, + LoadBalancerListResponse, +) +``` + +Methods: + +- client.gpu_droplets.load_balancers.create(\*\*params) -> LoadBalancerCreateResponse +- client.gpu_droplets.load_balancers.retrieve(lb_id) -> LoadBalancerRetrieveResponse +- client.gpu_droplets.load_balancers.update(lb_id, \*\*params) -> LoadBalancerUpdateResponse +- client.gpu_droplets.load_balancers.list(\*\*params) -> LoadBalancerListResponse +- client.gpu_droplets.load_balancers.delete(lb_id) -> None +- client.gpu_droplets.load_balancers.delete_cache(lb_id) -> None + +### Droplets + +Methods: + +- client.gpu_droplets.load_balancers.droplets.add(lb_id, \*\*params) -> None +- client.gpu_droplets.load_balancers.droplets.remove(lb_id, \*\*params) -> None + +### ForwardingRules + +Methods: + +- client.gpu_droplets.load_balancers.forwarding_rules.add(lb_id, \*\*params) -> None +- client.gpu_droplets.load_balancers.forwarding_rules.remove(lb_id, \*\*params) -> None + +## Sizes + +Types: + +```python +from gradient.types.gpu_droplets import SizeListResponse +``` + +Methods: + +- client.gpu_droplets.sizes.list(\*\*params) -> SizeListResponse + +## Snapshots + +Types: + +```python +from gradient.types.gpu_droplets import SnapshotRetrieveResponse, SnapshotListResponse +``` + +Methods: + +- client.gpu_droplets.snapshots.retrieve(snapshot_id) -> SnapshotRetrieveResponse +- client.gpu_droplets.snapshots.list(\*\*params) -> SnapshotListResponse +- client.gpu_droplets.snapshots.delete(snapshot_id) -> None + +## Volumes + +Types: + +```python +from gradient.types.gpu_droplets import ( + VolumeCreateResponse, + VolumeRetrieveResponse, + VolumeListResponse, +) +``` + +Methods: + +- client.gpu_droplets.volumes.create(\*\*params) -> VolumeCreateResponse +- client.gpu_droplets.volumes.retrieve(volume_id) -> VolumeRetrieveResponse +- client.gpu_droplets.volumes.list(\*\*params) -> VolumeListResponse +- client.gpu_droplets.volumes.delete(volume_id) -> None +- client.gpu_droplets.volumes.delete_by_name(\*\*params) -> None + +### Actions + +Types: + +```python +from gradient.types.gpu_droplets.volumes import ( + VolumeAction, + ActionRetrieveResponse, + ActionListResponse, + ActionInitiateByIDResponse, + ActionInitiateByNameResponse, +) +``` + +Methods: + +- client.gpu_droplets.volumes.actions.retrieve(action_id, \*, volume_id, \*\*params) -> ActionRetrieveResponse +- client.gpu_droplets.volumes.actions.list(volume_id, \*\*params) -> ActionListResponse +- client.gpu_droplets.volumes.actions.initiate_by_id(volume_id, \*\*params) -> ActionInitiateByIDResponse +- client.gpu_droplets.volumes.actions.initiate_by_name(\*\*params) -> ActionInitiateByNameResponse + +### Snapshots + +Types: + +```python +from gradient.types.gpu_droplets.volumes import ( + SnapshotCreateResponse, + SnapshotRetrieveResponse, + SnapshotListResponse, +) +``` + +Methods: + +- client.gpu_droplets.volumes.snapshots.create(volume_id, \*\*params) -> SnapshotCreateResponse +- client.gpu_droplets.volumes.snapshots.retrieve(snapshot_id) -> SnapshotRetrieveResponse +- client.gpu_droplets.volumes.snapshots.list(volume_id, \*\*params) -> SnapshotListResponse +- client.gpu_droplets.volumes.snapshots.delete(snapshot_id) -> None + +## Account + +### Keys + +Types: + +```python +from gradient.types.gpu_droplets.account import ( + SSHKeys, + KeyCreateResponse, + KeyRetrieveResponse, + KeyUpdateResponse, + KeyListResponse, +) +``` + +Methods: + +- client.gpu_droplets.account.keys.create(\*\*params) -> KeyCreateResponse +- client.gpu_droplets.account.keys.retrieve(ssh_key_identifier) -> KeyRetrieveResponse +- client.gpu_droplets.account.keys.update(ssh_key_identifier, \*\*params) -> KeyUpdateResponse +- client.gpu_droplets.account.keys.list(\*\*params) -> KeyListResponse +- client.gpu_droplets.account.keys.delete(ssh_key_identifier) -> None + +# Inference + +## APIKeys + +Types: + +```python +from gradient.types.inference import ( + APIModelAPIKeyInfo, + APIKeyCreateResponse, + APIKeyUpdateResponse, + APIKeyListResponse, + APIKeyDeleteResponse, + APIKeyUpdateRegenerateResponse, +) +``` + +Methods: + +- client.inference.api_keys.create(\*\*params) -> APIKeyCreateResponse +- client.inference.api_keys.update(path_api_key_uuid, \*\*params) -> APIKeyUpdateResponse +- client.inference.api_keys.list(\*\*params) -> APIKeyListResponse +- client.inference.api_keys.delete(api_key_uuid) -> APIKeyDeleteResponse +- client.inference.api_keys.update_regenerate(api_key_uuid) -> APIKeyUpdateRegenerateResponse + +# KnowledgeBases + +Types: + +```python +from gradient.types import ( + APIKnowledgeBase, + KnowledgeBaseCreateResponse, + KnowledgeBaseRetrieveResponse, + KnowledgeBaseUpdateResponse, + KnowledgeBaseListResponse, + KnowledgeBaseDeleteResponse, + KnowledgeBaseListIndexingJobsResponse, +) +``` + +Methods: + +- client.knowledge_bases.create(\*\*params) -> KnowledgeBaseCreateResponse +- client.knowledge_bases.retrieve(uuid) -> KnowledgeBaseRetrieveResponse +- client.knowledge_bases.update(path_uuid, \*\*params) -> KnowledgeBaseUpdateResponse +- client.knowledge_bases.list(\*\*params) -> KnowledgeBaseListResponse +- client.knowledge_bases.delete(uuid) -> KnowledgeBaseDeleteResponse +- client.knowledge_bases.list_indexing_jobs(knowledge_base_uuid) -> KnowledgeBaseListIndexingJobsResponse + +## DataSources + +Types: + +```python +from gradient.types.knowledge_bases import ( + APIFileUploadDataSource, + APIKnowledgeBaseDataSource, + APISpacesDataSource, + APIWebCrawlerDataSource, + AwsDataSource, + DataSourceCreateResponse, + DataSourceListResponse, + DataSourceDeleteResponse, + DataSourceCreatePresignedURLsResponse, +) +``` + +Methods: + +- client.knowledge_bases.data_sources.create(path_knowledge_base_uuid, \*\*params) -> DataSourceCreateResponse +- client.knowledge_bases.data_sources.list(knowledge_base_uuid, \*\*params) -> DataSourceListResponse +- client.knowledge_bases.data_sources.delete(data_source_uuid, \*, knowledge_base_uuid) -> DataSourceDeleteResponse +- client.knowledge_bases.data_sources.create_presigned_urls(\*\*params) -> DataSourceCreatePresignedURLsResponse + +## IndexingJobs + +Types: + +```python +from gradient.types.knowledge_bases import ( + APIIndexedDataSource, + APIIndexingJob, + IndexingJobCreateResponse, + IndexingJobRetrieveResponse, + IndexingJobListResponse, + IndexingJobRetrieveDataSourcesResponse, + IndexingJobRetrieveSignedURLResponse, + IndexingJobUpdateCancelResponse, +) +``` + +Methods: + +- client.knowledge_bases.indexing_jobs.create(\*\*params) -> IndexingJobCreateResponse +- client.knowledge_bases.indexing_jobs.retrieve(uuid) -> IndexingJobRetrieveResponse +- client.knowledge_bases.indexing_jobs.list(\*\*params) -> IndexingJobListResponse +- client.knowledge_bases.indexing_jobs.retrieve_data_sources(indexing_job_uuid) -> IndexingJobRetrieveDataSourcesResponse +- client.knowledge_bases.indexing_jobs.retrieve_signed_url(indexing_job_uuid) -> IndexingJobRetrieveSignedURLResponse +- client.knowledge_bases.indexing_jobs.update_cancel(path_uuid, \*\*params) -> IndexingJobUpdateCancelResponse + +# Models + +Types: + +```python +from gradient.types import APIAgreement, APIModel, APIModelVersion, ModelListResponse +``` + +Methods: + +- client.models.list(\*\*params) -> ModelListResponse + +## Providers + +### Anthropic + +Types: + +```python +from gradient.types.models.providers import ( + AnthropicCreateResponse, + AnthropicRetrieveResponse, + AnthropicUpdateResponse, + AnthropicListResponse, + AnthropicDeleteResponse, + AnthropicListAgentsResponse, +) +``` + +Methods: + +- client.models.providers.anthropic.create(\*\*params) -> AnthropicCreateResponse +- client.models.providers.anthropic.retrieve(api_key_uuid) -> AnthropicRetrieveResponse +- client.models.providers.anthropic.update(path_api_key_uuid, \*\*params) -> AnthropicUpdateResponse +- client.models.providers.anthropic.list(\*\*params) -> AnthropicListResponse +- client.models.providers.anthropic.delete(api_key_uuid) -> AnthropicDeleteResponse +- client.models.providers.anthropic.list_agents(uuid, \*\*params) -> AnthropicListAgentsResponse + +### OpenAI + +Types: + +```python +from gradient.types.models.providers import ( + OpenAICreateResponse, + OpenAIRetrieveResponse, + OpenAIUpdateResponse, + OpenAIListResponse, + OpenAIDeleteResponse, + OpenAIRetrieveAgentsResponse, +) +``` + +Methods: + +- client.models.providers.openai.create(\*\*params) -> OpenAICreateResponse +- client.models.providers.openai.retrieve(api_key_uuid) -> OpenAIRetrieveResponse +- client.models.providers.openai.update(path_api_key_uuid, \*\*params) -> OpenAIUpdateResponse +- client.models.providers.openai.list(\*\*params) -> OpenAIListResponse +- client.models.providers.openai.delete(api_key_uuid) -> OpenAIDeleteResponse +- client.models.providers.openai.retrieve_agents(uuid, \*\*params) -> OpenAIRetrieveAgentsResponse + +# Regions + +Types: + +```python +from gradient.types import RegionListResponse +``` + +Methods: + +- client.regions.list(\*\*params) -> RegionListResponse + +# Databases + +## SchemaRegistry + +### Config + +Types: + +```python +from gradient.types.databases.schema_registry import ( + ConfigRetrieveResponse, + ConfigUpdateResponse, + ConfigRetrieveSubjectResponse, + ConfigUpdateSubjectResponse, +) +``` + +Methods: + +- client.databases.schema_registry.config.retrieve(database_cluster_uuid) -> ConfigRetrieveResponse +- client.databases.schema_registry.config.update(database_cluster_uuid, \*\*params) -> ConfigUpdateResponse +- client.databases.schema_registry.config.retrieve_subject(subject_name, \*, database_cluster_uuid) -> ConfigRetrieveSubjectResponse +- client.databases.schema_registry.config.update_subject(subject_name, \*, database_cluster_uuid, \*\*params) -> ConfigUpdateSubjectResponse + +# Nfs + +Types: + +```python +from gradient.types import ( + NfCreateResponse, + NfRetrieveResponse, + NfListResponse, + NfInitiateActionResponse, +) +``` + +Methods: + +- client.nfs.create(\*\*params) -> NfCreateResponse +- client.nfs.retrieve(nfs_id, \*\*params) -> NfRetrieveResponse +- client.nfs.list(\*\*params) -> NfListResponse +- client.nfs.delete(nfs_id, \*\*params) -> None +- client.nfs.initiate_action(nfs_id, \*\*params) -> NfInitiateActionResponse + +## Snapshots + +Types: + +```python +from gradient.types.nfs import SnapshotRetrieveResponse, SnapshotListResponse +``` + +Methods: + +- client.nfs.snapshots.retrieve(nfs_snapshot_id, \*\*params) -> SnapshotRetrieveResponse +- client.nfs.snapshots.list(\*\*params) -> SnapshotListResponse +- client.nfs.snapshots.delete(nfs_snapshot_id, \*\*params) -> None diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 00000000..b845b0f4 --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${PYPI_TOKEN}" ]; then + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/bin/publish-pypi b/bin/publish-pypi new file mode 100644 index 00000000..826054e9 --- /dev/null +++ b/bin/publish-pypi @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux +mkdir -p dist +rye build --clean +rye publish --yes --token=$PYPI_TOKEN diff --git a/examples/.keep b/examples/.keep new file mode 100644 index 00000000..d8c73e93 --- /dev/null +++ b/examples/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store example files demonstrating usage of this SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 00000000..53bca7ff --- /dev/null +++ b/noxfile.py @@ -0,0 +1,9 @@ +import nox + + +@nox.session(reuse_venv=True, name="test-pydantic-v1") +def test_pydantic_v1(session: nox.Session) -> None: + session.install("-r", "requirements-dev.lock") + session.install("pydantic<2") + + session.run("pytest", "--showlocals", "--ignore=tests/functional", *session.posargs) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..b1014da9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,267 @@ +[project] +name = "gradient" +version = "3.8.0" +description = "The official Python library for the Gradient API" +dynamic = ["readme"] +license = "Apache-2.0" +authors = [ +{ name = "Gradient", email = "" }, +] +dependencies = [ + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", + "typing-extensions>=4.10, <5", + "anyio>=3.5.0, <5", + "distro>=1.7.0, <2", + "sniffio", +] +requires-python = ">= 3.9" +classifiers = [ + "Typing :: Typed", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: Apache Software License" +] + +[project.urls] +Homepage = "https://github.com/digitalocean/gradient-python" +Repository = "https://github.com/digitalocean/gradient-python" + +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] + +[tool.rye] +managed = true +# version pins are in requirements-dev.lock +dev-dependencies = [ + "pyright==1.1.399", + "mypy", + "respx", + "pytest", + "pytest-asyncio", + "ruff", + "time-machine", + "nox", + "dirty-equals>=0.6.0", + "importlib-metadata>=6.7.0", + "rich>=13.7.1", + "pytest-xdist>=3.6.1", +] + +[tool.rye.scripts] +format = { chain = [ + "format:ruff", + "format:docs", + "fix:ruff", + # run formatting again to fix any inconsistencies when imports are stripped + "format:ruff", +]} +"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" +"format:ruff" = "ruff format" + +"lint" = { chain = [ + "check:ruff", + "typecheck", + "check:importable", +]} +"check:ruff" = "ruff check ." +"fix:ruff" = "ruff check --fix ." + +"check:importable" = "python -c 'import gradient'" + +typecheck = { chain = [ + "typecheck:pyright", + "typecheck:mypy" +]} +"typecheck:pyright" = "pyright" +"typecheck:verify-types" = "pyright --verifytypes gradient --ignoreexternal" +"typecheck:mypy" = "mypy ." + +[build-system] +requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[tool.hatch.build] +include = [ + "src/*" +] + +[tool.hatch.build.targets.wheel] +packages = ["src/gradient"] + +[tool.hatch.build.targets.sdist] +# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc) +include = [ + "/*.toml", + "/*.json", + "/*.lock", + "/*.md", + "/mypy.ini", + "/noxfile.py", + "bin/*", + "examples/*", + "src/*", + "tests/*", +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] +path = "README.md" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] +# replace relative links with absolute links +pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' +replacement = '[\1](https://github.com/digitalocean/gradient-python/tree/main/\g<2>)' + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--tb=short -n auto" +xfail_strict = true +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +filterwarnings = [ + "error" +] + +[tool.pyright] +# this enables practically every flag given by pyright. +# there are a couple of flags that are still disabled by +# default in strict mode as they are experimental and niche. +typeCheckingMode = "strict" +pythonVersion = "3.9" + +exclude = [ + "_dev", + ".venv", + ".nox", + ".git", +] + +reportImplicitOverride = true +reportOverlappingOverload = false + +reportImportCycles = false +reportPrivateUsage = false + +[tool.mypy] +pretty = true +show_error_codes = true + +# Exclude _files.py because mypy isn't smart enough to apply +# the correct type narrowing and as this is an internal module +# it's fine to just use Pyright. +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ['src/gradient/_files.py', '_dev/.*.py', 'tests/.*'] + +strict_equality = true +implicit_reexport = true +check_untyped_defs = true +no_implicit_optional = true + +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true + +# Turn these options off as it could cause conflicts +# with the Pyright options. +warn_unused_ignores = false +warn_redundant_casts = false + +disallow_any_generics = true +disallow_untyped_defs = true +disallow_untyped_calls = true +disallow_subclassing_any = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +cache_fine_grained = true + +# By default, mypy reports an error if you assign a value to the result +# of a function call that doesn't return anything. We do this in our test +# cases: +# ``` +# result = ... +# assert result is None +# ``` +# Changing this codegen to make mypy happy would increase complexity +# and would not be worth it. +disable_error_code = "func-returns-value,overload-cannot-match" + +# https://github.com/python/mypy/issues/12162 +[[tool.mypy.overrides]] +module = "black.files.*" +ignore_errors = true +ignore_missing_imports = true + + +[tool.ruff] +line-length = 120 +output-format = "grouped" +target-version = "py38" + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint] +select = [ + # isort + "I", + # bugbear rules + "B", + # remove unused imports + "F401", + # check for missing future annotations + "FA102", + # bare except statements + "E722", + # unused arguments + "ARG", + # print statements + "T201", + "T203", + # misuse of typing.TYPE_CHECKING + "TC004", + # import rules + "TID251", +] +ignore = [ + # mutable defaults + "B006", +] +unfixable = [ + # disable auto fix for print statements + "T201", + "T203", +] + +extend-safe-fixes = ["FA102"] + +[tool.ruff.lint.flake8-tidy-imports.banned-api] +"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" + +[tool.ruff.lint.isort] +length-sort = true +length-sort-straight = true +combine-as-imports = true +extra-standard-library = ["typing_extensions"] +known-first-party = ["gradient", "tests"] + +[tool.ruff.lint.per-file-ignores] +"bin/**.py" = ["T201", "T203"] +"scripts/**.py" = ["T201", "T203"] +"tests/**.py" = ["T201", "T203"] +"examples/**.py" = ["T201", "T203"] diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000..0b0d1705 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,66 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "python", + "extra-files": [ + "src/gradient/_version.py" + ] +} \ No newline at end of file diff --git a/requirements-dev.lock b/requirements-dev.lock new file mode 100644 index 00000000..e5307af8 --- /dev/null +++ b/requirements-dev.lock @@ -0,0 +1,137 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: true +# with-sources: false +# generate-hashes: false +# universal: false + +-e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via gradient + # via httpx-aiohttp +aiosignal==1.3.2 + # via aiohttp +annotated-types==0.6.0 + # via pydantic +anyio==4.4.0 + # via gradient + # via httpx +argcomplete==3.1.2 + # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp +certifi==2023.7.22 + # via httpcore + # via httpx +colorlog==6.7.0 + # via nox +dirty-equals==0.6.0 +distlib==0.3.7 + # via virtualenv +distro==1.8.0 + # via gradient +exceptiongroup==1.2.2 + # via anyio + # via pytest +execnet==2.1.1 + # via pytest-xdist +filelock==3.12.4 + # via virtualenv +frozenlist==1.6.2 + # via aiohttp + # via aiosignal +h11==0.16.0 + # via httpcore +httpcore==1.0.9 + # via httpx +httpx==0.28.1 + # via gradient + # via httpx-aiohttp + # via respx +httpx-aiohttp==0.1.9 + # via gradient +idna==3.4 + # via anyio + # via httpx + # via yarl +importlib-metadata==7.0.0 +iniconfig==2.0.0 + # via pytest +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py +multidict==6.4.4 + # via aiohttp + # via yarl +mypy==1.14.1 +mypy-extensions==1.0.0 + # via mypy +nodeenv==1.8.0 + # via pyright +nox==2023.4.22 +packaging==23.2 + # via nox + # via pytest +platformdirs==3.11.0 + # via virtualenv +pluggy==1.5.0 + # via pytest +propcache==0.3.1 + # via aiohttp + # via yarl +pydantic==2.11.9 + # via gradient +pydantic-core==2.33.2 + # via pydantic +pygments==2.18.0 + # via rich +pyright==1.1.399 +pytest==8.3.3 + # via pytest-asyncio + # via pytest-xdist +pytest-asyncio==0.24.0 +pytest-xdist==3.7.0 +python-dateutil==2.8.2 + # via time-machine +pytz==2023.3.post1 + # via dirty-equals +respx==0.22.0 +rich==13.7.1 +ruff==0.9.4 +setuptools==68.2.2 + # via nodeenv +six==1.16.0 + # via python-dateutil +sniffio==1.3.0 + # via anyio + # via gradient +time-machine==2.9.0 +tomli==2.0.2 + # via mypy + # via pytest +typing-extensions==4.12.2 + # via anyio + # via gradient + # via multidict + # via mypy + # via pydantic + # via pydantic-core + # via pyright + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic +virtualenv==20.24.5 + # via nox +yarl==1.20.0 + # via aiohttp +zipp==3.17.0 + # via importlib-metadata diff --git a/requirements.lock b/requirements.lock new file mode 100644 index 00000000..8c60e6c5 --- /dev/null +++ b/requirements.lock @@ -0,0 +1,75 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: true +# with-sources: false +# generate-hashes: false +# universal: false + +-e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via gradient + # via httpx-aiohttp +aiosignal==1.3.2 + # via aiohttp +annotated-types==0.6.0 + # via pydantic +anyio==4.4.0 + # via gradient + # via httpx +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp +certifi==2023.7.22 + # via httpcore + # via httpx +distro==1.8.0 + # via gradient +exceptiongroup==1.2.2 + # via anyio +frozenlist==1.6.2 + # via aiohttp + # via aiosignal +h11==0.16.0 + # via httpcore +httpcore==1.0.9 + # via httpx +httpx==0.28.1 + # via gradient + # via httpx-aiohttp +httpx-aiohttp==0.1.9 + # via gradient +idna==3.4 + # via anyio + # via httpx + # via yarl +multidict==6.4.4 + # via aiohttp + # via yarl +propcache==0.3.1 + # via aiohttp + # via yarl +pydantic==2.11.9 + # via gradient +pydantic-core==2.33.2 + # via pydantic +sniffio==1.3.0 + # via anyio + # via gradient +typing-extensions==4.12.2 + # via anyio + # via gradient + # via multidict + # via pydantic + # via pydantic-core + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic +yarl==1.20.0 + # via aiohttp diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 00000000..b430fee3 --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then + brew bundle check >/dev/null 2>&1 || { + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo + } +fi + +echo "==> Installing Python dependencies…" + +# experimental uv support makes installations significantly faster +rye config --set-bool behavior.use-uv=true + +rye sync --all-features diff --git a/scripts/format b/scripts/format new file mode 100755 index 00000000..667ec2d7 --- /dev/null +++ b/scripts/format @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running formatters" +rye run format diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 00000000..9ccb6ae5 --- /dev/null +++ b/scripts/lint @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running lints" +rye run lint + +echo "==> Making sure it imports" +rye run python -c 'import gradient' diff --git a/scripts/mock b/scripts/mock new file mode 100755 index 00000000..0b28f6ea --- /dev/null +++ b/scripts/mock @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [[ -n "$1" && "$1" != '--'* ]]; then + URL="$1" + shift +else + URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" +fi + +# Check if the URL is empty +if [ -z "$URL" ]; then + echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" + exit 1 +fi + +echo "==> Starting mock server with URL ${URL}" + +# Run prism mock on the given spec +if [ "$1" == "--daemon" ]; then + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + + # Wait for server to come online + echo -n "Waiting for server" + while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + echo -n "." + sleep 0.1 + done + + if grep -q "✖ fatal" ".prism.log"; then + cat .prism.log + exit 1 + fi + + echo +else + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" +fi diff --git a/scripts/test b/scripts/test new file mode 100755 index 00000000..dbeda2d2 --- /dev/null +++ b/scripts/test @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +function prism_is_running() { + curl --silent "http://localhost:4010" >/dev/null 2>&1 +} + +kill_server_on_port() { + pids=$(lsof -t -i tcp:"$1" || echo "") + if [ "$pids" != "" ]; then + kill "$pids" + echo "Stopped $pids." + fi +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if ! is_overriding_api_base_url && ! prism_is_running ; then + # When we exit this script, make sure to kill the background mock server process + trap 'kill_server_on_port 4010' EXIT + + # Start the dev server + ./scripts/mock --daemon +fi + +if is_overriding_api_base_url ; then + echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" + echo +elif ! prism_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "To run the server, pass in the path or url of your OpenAPI" + echo -e "spec to the prism command:" + echo + echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +else + echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo +fi + +export DEFER_PYDANTIC_BUILD=false + +echo "==> Running tests" +rye run pytest "$@" + +echo "==> Running Pydantic v1 tests" +rye run nox -s test-pydantic-v1 -- "$@" diff --git a/scripts/utils/ruffen-docs.py b/scripts/utils/ruffen-docs.py new file mode 100644 index 00000000..0cf2bd2f --- /dev/null +++ b/scripts/utils/ruffen-docs.py @@ -0,0 +1,167 @@ +# fork of https://github.com/asottile/blacken-docs adapted for ruff +from __future__ import annotations + +import re +import sys +import argparse +import textwrap +import contextlib +import subprocess +from typing import Match, Optional, Sequence, Generator, NamedTuple, cast + +MD_RE = re.compile( + r"(?P^(?P *)```\s*python\n)" r"(?P.*?)" r"(?P^(?P=indent)```\s*$)", + re.DOTALL | re.MULTILINE, +) +MD_PYCON_RE = re.compile( + r"(?P^(?P *)```\s*pycon\n)" r"(?P.*?)" r"(?P^(?P=indent)```.*$)", + re.DOTALL | re.MULTILINE, +) +PYCON_PREFIX = ">>> " +PYCON_CONTINUATION_PREFIX = "..." +PYCON_CONTINUATION_RE = re.compile( + rf"^{re.escape(PYCON_CONTINUATION_PREFIX)}( |$)", +) +DEFAULT_LINE_LENGTH = 100 + + +class CodeBlockError(NamedTuple): + offset: int + exc: Exception + + +def format_str( + src: str, +) -> tuple[str, Sequence[CodeBlockError]]: + errors: list[CodeBlockError] = [] + + @contextlib.contextmanager + def _collect_error(match: Match[str]) -> Generator[None, None, None]: + try: + yield + except Exception as e: + errors.append(CodeBlockError(match.start(), e)) + + def _md_match(match: Match[str]) -> str: + code = textwrap.dedent(match["code"]) + with _collect_error(match): + code = format_code_block(code) + code = textwrap.indent(code, match["indent"]) + return f"{match['before']}{code}{match['after']}" + + def _pycon_match(match: Match[str]) -> str: + code = "" + fragment = cast(Optional[str], None) + + def finish_fragment() -> None: + nonlocal code + nonlocal fragment + + if fragment is not None: + with _collect_error(match): + fragment = format_code_block(fragment) + fragment_lines = fragment.splitlines() + code += f"{PYCON_PREFIX}{fragment_lines[0]}\n" + for line in fragment_lines[1:]: + # Skip blank lines to handle Black adding a blank above + # functions within blocks. A blank line would end the REPL + # continuation prompt. + # + # >>> if True: + # ... def f(): + # ... pass + # ... + if line: + code += f"{PYCON_CONTINUATION_PREFIX} {line}\n" + if fragment_lines[-1].startswith(" "): + code += f"{PYCON_CONTINUATION_PREFIX}\n" + fragment = None + + indentation = None + for line in match["code"].splitlines(): + orig_line, line = line, line.lstrip() + if indentation is None and line: + indentation = len(orig_line) - len(line) + continuation_match = PYCON_CONTINUATION_RE.match(line) + if continuation_match and fragment is not None: + fragment += line[continuation_match.end() :] + "\n" + else: + finish_fragment() + if line.startswith(PYCON_PREFIX): + fragment = line[len(PYCON_PREFIX) :] + "\n" + else: + code += orig_line[indentation:] + "\n" + finish_fragment() + return code + + def _md_pycon_match(match: Match[str]) -> str: + code = _pycon_match(match) + code = textwrap.indent(code, match["indent"]) + return f"{match['before']}{code}{match['after']}" + + src = MD_RE.sub(_md_match, src) + src = MD_PYCON_RE.sub(_md_pycon_match, src) + return src, errors + + +def format_code_block(code: str) -> str: + return subprocess.check_output( + [ + sys.executable, + "-m", + "ruff", + "format", + "--stdin-filename=script.py", + f"--line-length={DEFAULT_LINE_LENGTH}", + ], + encoding="utf-8", + input=code, + ) + + +def format_file( + filename: str, + skip_errors: bool, +) -> int: + with open(filename, encoding="UTF-8") as f: + contents = f.read() + new_contents, errors = format_str(contents) + for error in errors: + lineno = contents[: error.offset].count("\n") + 1 + print(f"{filename}:{lineno}: code block parse error {error.exc}") + if errors and not skip_errors: + return 1 + if contents != new_contents: + print(f"{filename}: Rewriting...") + with open(filename, "w", encoding="UTF-8") as f: + f.write(new_contents) + return 0 + else: + return 0 + + +def main(argv: Sequence[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "-l", + "--line-length", + type=int, + default=DEFAULT_LINE_LENGTH, + ) + parser.add_argument( + "-S", + "--skip-string-normalization", + action="store_true", + ) + parser.add_argument("-E", "--skip-errors", action="store_true") + parser.add_argument("filenames", nargs="*") + args = parser.parse_args(argv) + + retv = 0 + for filename in args.filenames: + retv |= format_file(filename, skip_errors=args.skip_errors) + return retv + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 00000000..d93584b2 --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -exuo pipefail + +FILENAME=$(basename dist/*.whl) + +RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + +SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url') + +if [[ "$SIGNED_URL" == "null" ]]; then + echo -e "\033[31mFailed to get signed URL.\033[0m" + exit 1 +fi + +UPLOAD_RESPONSE=$(curl -v -X PUT \ + -H "Content-Type: binary/octet-stream" \ + --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1) + +if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then + echo -e "\033[32mUploaded build to Stainless storage.\033[0m" + echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/gradient-python/$SHA/$FILENAME'\033[0m" +else + echo -e "\033[31mFailed to upload artifact.\033[0m" + exit 1 +fi diff --git a/src/digitalocean_genai_sdk/lib/.keep b/src/digitalocean_genai_sdk/lib/.keep new file mode 100644 index 00000000..5e2c99fd --- /dev/null +++ b/src/digitalocean_genai_sdk/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/do_gradientai/lib/.keep b/src/do_gradientai/lib/.keep new file mode 100644 index 00000000..5e2c99fd --- /dev/null +++ b/src/do_gradientai/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/gradient/__init__.py b/src/gradient/__init__.py new file mode 100644 index 00000000..a67cd2a7 --- /dev/null +++ b/src/gradient/__init__.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import typing as _t + +from . import types +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given +from ._utils import file_from_path +from ._client import ( + Client, + Stream, + Timeout, + Gradient, + Transport, + AsyncClient, + AsyncStream, + AsyncGradient, + RequestOptions, +) +from ._models import BaseModel +from ._version import __title__, __version__ +from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse +from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS +from ._exceptions import ( + APIError, + ConflictError, + GradientError, + NotFoundError, + APIStatusError, + RateLimitError, + APITimeoutError, + BadRequestError, + APIConnectionError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + UnprocessableEntityError, + APIResponseValidationError, +) +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient +from ._utils._logs import setup_logging as _setup_logging + +__all__ = [ + "types", + "__version__", + "__title__", + "NoneType", + "Transport", + "ProxiesTypes", + "NotGiven", + "NOT_GIVEN", + "not_given", + "Omit", + "omit", + "GradientError", + "APIError", + "APIStatusError", + "APITimeoutError", + "APIConnectionError", + "APIResponseValidationError", + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", + "Timeout", + "RequestOptions", + "Client", + "AsyncClient", + "Stream", + "AsyncStream", + "Gradient", + "AsyncGradient", + "file_from_path", + "BaseModel", + "DEFAULT_TIMEOUT", + "DEFAULT_MAX_RETRIES", + "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", +] + +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + +_setup_logging() + +# Update the __module__ attribute for exported symbols so that +# error messages point to this module instead of the module +# it was originally defined in, e.g. +# gradient._exceptions.NotFoundError -> gradient.NotFoundError +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + try: + __locals[__name].__module__ = "gradient" + except (TypeError, AttributeError): + # Some of our exported symbols are builtins which we can't set attributes for. + pass diff --git a/src/gradient/_base_client.py b/src/gradient/_base_client.py new file mode 100644 index 00000000..7d92e5bc --- /dev/null +++ b/src/gradient/_base_client.py @@ -0,0 +1,1995 @@ +from __future__ import annotations + +import sys +import json +import time +import uuid +import email +import asyncio +import inspect +import logging +import platform +import email.utils +from types import TracebackType +from random import random +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Type, + Union, + Generic, + Mapping, + TypeVar, + Iterable, + Iterator, + Optional, + Generator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Literal, override, get_origin + +import anyio +import httpx +import distro +import pydantic +from httpx import URL +from pydantic import PrivateAttr + +from . import _exceptions +from ._qs import Querystring +from ._files import to_httpx_files, async_to_httpx_files +from ._types import ( + Body, + Omit, + Query, + Headers, + Timeout, + NotGiven, + ResponseT, + AnyMapping, + PostParser, + RequestFiles, + HttpxSendArgs, + RequestOptions, + HttpxRequestFiles, + ModelBuilderProtocol, + not_given, +) +from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping +from ._compat import PYDANTIC_V1, model_copy, model_dump +from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type +from ._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + extract_response_type, +) +from ._constants import ( + DEFAULT_TIMEOUT, + MAX_RETRY_DELAY, + DEFAULT_MAX_RETRIES, + INITIAL_RETRY_DELAY, + RAW_RESPONSE_HEADER, + OVERRIDE_CAST_TO_HEADER, + DEFAULT_CONNECTION_LIMITS, +) +from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder +from ._exceptions import ( + APIStatusError, + APITimeoutError, + APIConnectionError, + APIResponseValidationError, +) + +log: logging.Logger = logging.getLogger(__name__) + +# TODO: make base page type vars covariant +SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") +AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") + + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) + +_StreamT = TypeVar("_StreamT", bound=Stream[Any]) +_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) + +if TYPE_CHECKING: + from httpx._config import ( + DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] + ) + + HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG +else: + try: + from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + except ImportError: + # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 + HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) + + +class PageInfo: + """Stores the necessary information to build the request to retrieve the next page. + + Either `url` or `params` must be set. + """ + + url: URL | NotGiven + params: Query | NotGiven + json: Body | NotGiven + + @overload + def __init__( + self, + *, + url: URL, + ) -> None: ... + + @overload + def __init__( + self, + *, + params: Query, + ) -> None: ... + + @overload + def __init__( + self, + *, + json: Body, + ) -> None: ... + + def __init__( + self, + *, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, + ) -> None: + self.url = url + self.json = json + self.params = params + + @override + def __repr__(self) -> str: + if self.url: + return f"{self.__class__.__name__}(url={self.url})" + if self.json: + return f"{self.__class__.__name__}(json={self.json})" + return f"{self.__class__.__name__}(params={self.params})" + + +class BasePage(GenericModel, Generic[_T]): + """ + Defines the core interface for pagination. + + Type Args: + ModelT: The pydantic model that represents an item in the response. + + Methods: + has_next_page(): Check if there is another page available + next_page_info(): Get the necessary information to make a request for the next page + """ + + _options: FinalRequestOptions = PrivateAttr() + _model: Type[_T] = PrivateAttr() + + def has_next_page(self) -> bool: + items = self._get_page_items() + if not items: + return False + return self.next_page_info() is not None + + def next_page_info(self) -> Optional[PageInfo]: ... + + def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] + ... + + def _params_from_url(self, url: URL) -> httpx.QueryParams: + # TODO: do we have to preprocess params here? + return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params) + + def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: + options = model_copy(self._options) + options._strip_raw_response_header() + + if not isinstance(info.params, NotGiven): + options.params = {**options.params, **info.params} + return options + + if not isinstance(info.url, NotGiven): + params = self._params_from_url(info.url) + url = info.url.copy_with(params=params) + options.params = dict(url.params) + options.url = str(url) + return options + + if not isinstance(info.json, NotGiven): + if not is_mapping(info.json): + raise TypeError("Pagination is only supported with mappings") + + if not options.json_data: + options.json_data = {**info.json} + else: + if not is_mapping(options.json_data): + raise TypeError("Pagination is only supported with mappings") + + options.json_data = {**options.json_data, **info.json} + return options + + raise ValueError("Unexpected PageInfo state") + + +class BaseSyncPage(BasePage[_T], Generic[_T]): + _client: SyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + client: SyncAPIClient, + model: Type[_T], + options: FinalRequestOptions, + ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + + self._model = model + self._client = client + self._options = options + + # Pydantic uses a custom `__iter__` method to support casting BaseModels + # to dictionaries. e.g. dict(model). + # As we want to support `for item in page`, this is inherently incompatible + # with the default pydantic behaviour. It is not possible to support both + # use cases at once. Fortunately, this is not a big deal as all other pydantic + # methods should continue to work as expected as there is an alternative method + # to cast a model to a dictionary, model.dict(), which is used internally + # by pydantic. + def __iter__(self) -> Iterator[_T]: # type: ignore + for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = page.get_next_page() + else: + return + + def get_next_page(self: SyncPageT) -> SyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return self._client._request_api_list(self._model, page=self.__class__, options=options) + + +class AsyncPaginator(Generic[_T, AsyncPageT]): + def __init__( + self, + client: AsyncAPIClient, + options: FinalRequestOptions, + page_cls: Type[AsyncPageT], + model: Type[_T], + ) -> None: + self._model = model + self._client = client + self._options = options + self._page_cls = page_cls + + def __await__(self) -> Generator[Any, None, AsyncPageT]: + return self._get_page().__await__() + + async def _get_page(self) -> AsyncPageT: + def _parser(resp: AsyncPageT) -> AsyncPageT: + resp._set_private_attributes( + model=self._model, + options=self._options, + client=self._client, + ) + return resp + + self._options.post_parser = _parser + + return await self._client.request(self._page_cls, self._options) + + async def __aiter__(self) -> AsyncIterator[_T]: + # https://github.com/microsoft/pyright/issues/3464 + page = cast( + AsyncPageT, + await self, # type: ignore + ) + async for item in page: + yield item + + +class BaseAsyncPage(BasePage[_T], Generic[_T]): + _client: AsyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + model: Type[_T], + client: AsyncAPIClient, + options: FinalRequestOptions, + ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + + self._model = model + self._client = client + self._options = options + + async def __aiter__(self) -> AsyncIterator[_T]: + async for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = await page.get_next_page() + else: + return + + async def get_next_page(self: AsyncPageT) -> AsyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return await self._client._request_api_list(self._model, page=self.__class__, options=options) + + +_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) +_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) + + +class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): + _client: _HttpxClientT + _version: str + _base_url: URL + max_retries: int + timeout: Union[float, Timeout, None] + _strict_response_validation: bool + _idempotency_header: str | None + _default_stream_cls: type[_DefaultStreamT] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None = DEFAULT_TIMEOUT, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + self._version = version + self._base_url = self._enforce_trailing_slash(URL(base_url)) + self.max_retries = max_retries + self.timeout = timeout + self._custom_headers = custom_headers or {} + self._custom_query = custom_query or {} + self._strict_response_validation = _strict_response_validation + self._idempotency_header = None + self._platform: Platform | None = None + + if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] + raise TypeError( + "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `gradient.DEFAULT_MAX_RETRIES`" + ) + + def _enforce_trailing_slash(self, url: URL) -> URL: + if url.raw_path.endswith(b"/"): + return url + return url.copy_with(raw_path=url.raw_path + b"/") + + def _make_status_error_from_response( + self, + response: httpx.Response, + ) -> APIStatusError: + if response.is_closed and not response.is_stream_consumed: + # We can't read the response body as it has been closed + # before it was read. This can happen if an event hook + # raises a status error. + body = None + err_msg = f"Error code: {response.status_code}" + else: + err_text = response.text.strip() + body = err_text + + try: + body = json.loads(err_text) + err_msg = f"Error code: {response.status_code} - {body}" + except Exception: + err_msg = err_text or f"Error code: {response.status_code}" + + return self._make_status_error(err_msg, body=body, response=response) + + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> _exceptions.APIStatusError: + raise NotImplementedError() + + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: + custom_headers = options.headers or {} + headers_dict = _merge_mappings(self.default_headers, custom_headers) + self._validate_headers(headers_dict, custom_headers) + + # headers are case-insensitive while dictionaries are not. + headers = httpx.Headers(headers_dict) + + idempotency_header = self._idempotency_header + if idempotency_header and options.idempotency_key and idempotency_header not in headers: + headers[idempotency_header] = options.idempotency_key + + # Don't set these headers if they were already set or removed by the caller. We check + # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: + headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) + + return headers + + def _prepare_url(self, url: str) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + # Copied from httpx's `_merge_url` method. + merge_url = URL(url) + if merge_url.is_relative_url: + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + + return merge_url + + def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: + return SSEDecoder() + + def _build_request( + self, + options: FinalRequestOptions, + *, + retries_taken: int = 0, + ) -> httpx.Request: + if log.isEnabledFor(logging.DEBUG): + log.debug("Request options: %s", model_dump(options, exclude_unset=True)) + + kwargs: dict[str, Any] = {} + + json_data = options.json_data + if options.extra_json is not None: + if json_data is None: + json_data = cast(Body, options.extra_json) + elif is_mapping(json_data): + json_data = _merge_mappings(json_data, options.extra_json) + else: + raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") + + headers = self._build_headers(options, retries_taken=retries_taken) + params = _merge_mappings(self.default_query, options.params) + content_type = headers.get("Content-Type") + files = options.files + + # If the given Content-Type header is multipart/form-data then it + # has to be removed so that httpx can generate the header with + # additional information for us as it has to be in this form + # for the server to be able to correctly parse the request: + # multipart/form-data; boundary=---abc-- + if content_type is not None and content_type.startswith("multipart/form-data"): + if "boundary" not in content_type: + # only remove the header if the boundary hasn't been explicitly set + # as the caller doesn't want httpx to come up with their own boundary + headers.pop("Content-Type") + + # As we are now sending multipart/form-data instead of application/json + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding + if json_data: + if not is_dict(json_data): + raise TypeError( + f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." + ) + kwargs["data"] = self._serialize_multipartform(json_data) + + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + + prepared_url = self._prepare_url(options.url) + if "_" in prepared_url.host: + # work around https://github.com/encode/httpx/discussions/2880 + kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + + is_body_allowed = options.method.lower() != "get" + + if is_body_allowed: + if isinstance(json_data, bytes): + kwargs["content"] = json_data + else: + kwargs["json"] = json_data if is_given(json_data) else None + kwargs["files"] = files + else: + headers.pop("Content-Type", None) + kwargs.pop("data", None) + + # TODO: report this error to httpx + return self._client.build_request( # pyright: ignore[reportUnknownMemberType] + headers=headers, + timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, + method=options.method, + url=prepared_url, + # the `Query` type that we use is incompatible with qs' + # `Params` type as it needs to be typed as `Mapping[str, object]` + # so that passing a `TypedDict` doesn't cause an error. + # https://github.com/microsoft/pyright/issues/3526#event-6715453066 + params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, + **kwargs, + ) + + def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: + items = self.qs.stringify_items( + # TODO: type ignore is required as stringify_items is well typed but we can't be + # well typed without heavy validation. + data, # type: ignore + array_format="brackets", + ) + serialized: dict[str, object] = {} + for key, value in items: + existing = serialized.get(key) + + if not existing: + serialized[key] = value + continue + + # If a value has already been set for this key then that + # means we're sending data like `array[]=[1, 2, 3]` and we + # need to tell httpx that we want to send multiple values with + # the same key which is done by using a list or a tuple. + # + # Note: 2d arrays should never result in the same key at both + # levels so it's safe to assume that if the value is a list, + # it was because we changed it to be a list. + if is_list(existing): + existing.append(value) + else: + serialized[key] = [existing, value] + + return serialized + + def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: + if not is_given(options.headers): + return cast_to + + # make a copy of the headers so we don't mutate user-input + headers = dict(options.headers) + + # we internally support defining a temporary header to override the + # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` + # see _response.py for implementation details + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) + if is_given(override_cast_to): + options.headers = headers + return cast(Type[ResponseT], override_cast_to) + + return cast_to + + def _should_stream_response_body(self, request: httpx.Request) -> bool: + return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] + + def _process_response_data( + self, + *, + data: object, + cast_to: type[ResponseT], + response: httpx.Response, + ) -> ResponseT: + if data is None: + return cast(ResponseT, None) + + if cast_to is object: + return cast(ResponseT, data) + + try: + if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): + return cast(ResponseT, cast_to.build(response=response, data=data)) + + if self._strict_response_validation: + return cast(ResponseT, validate_type(type_=cast_to, value=data)) + + return cast(ResponseT, construct_type(type_=cast_to, value=data)) + except pydantic.ValidationError as err: + raise APIResponseValidationError(response=response, body=data) from err + + @property + def qs(self) -> Querystring: + return Querystring() + + @property + def custom_auth(self) -> httpx.Auth | None: + return None + + @property + def auth_headers(self) -> dict[str, str]: + return {} + + @property + def default_headers(self) -> dict[str, str | Omit]: + return { + "Accept": "application/json", + "Content-Type": "application/json", + "User-Agent": self.user_agent, + **self.platform_headers(), + **self.auth_headers, + **self._custom_headers, + } + + @property + def default_query(self) -> dict[str, object]: + return { + **self._custom_query, + } + + def _validate_headers( + self, + headers: Headers, # noqa: ARG002 + custom_headers: Headers, # noqa: ARG002 + ) -> None: + """Validate the given default headers and custom headers. + + Does nothing by default. + """ + return + + @property + def user_agent(self) -> str: + return f"{self.__class__.__name__}/Python {self._version}" + + @property + def base_url(self) -> URL: + return self._base_url + + @base_url.setter + def base_url(self, url: URL | str) -> None: + self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) + + def platform_headers(self) -> Dict[str, str]: + # the actual implementation is in a separate `lru_cache` decorated + # function because adding `lru_cache` to methods will leak memory + # https://github.com/python/cpython/issues/88476 + return platform_headers(self._version, platform=self._platform) + + def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: + """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. + + About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax + """ + if response_headers is None: + return None + + # First, try the non-standard `retry-after-ms` header for milliseconds, + # which is more precise than integer-seconds `retry-after` + try: + retry_ms_header = response_headers.get("retry-after-ms", None) + return float(retry_ms_header) / 1000 + except (TypeError, ValueError): + pass + + # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats). + retry_header = response_headers.get("retry-after") + try: + # note: the spec indicates that this should only ever be an integer + # but if someone sends a float there's no reason for us to not respect it + return float(retry_header) + except (TypeError, ValueError): + pass + + # Last, try parsing `retry-after` as a date. + retry_date_tuple = email.utils.parsedate_tz(retry_header) + if retry_date_tuple is None: + return None + + retry_date = email.utils.mktime_tz(retry_date_tuple) + return float(retry_date - time.time()) + + def _calculate_retry_timeout( + self, + remaining_retries: int, + options: FinalRequestOptions, + response_headers: Optional[httpx.Headers] = None, + ) -> float: + max_retries = options.get_max_retries(self.max_retries) + + # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + retry_after = self._parse_retry_after_header(response_headers) + if retry_after is not None and 0 < retry_after <= 60: + return retry_after + + # Also cap retry count to 1000 to avoid any potential overflows with `pow` + nb_retries = min(max_retries - remaining_retries, 1000) + + # Apply exponential backoff, but not more than the max. + sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) + + # Apply some jitter, plus-or-minus half a second. + jitter = 1 - 0.25 * random() + timeout = sleep_seconds * jitter + return timeout if timeout >= 0 else 0 + + def _should_retry(self, response: httpx.Response) -> bool: + # Note: this is not a standard header + should_retry_header = response.headers.get("x-should-retry") + + # If the server explicitly says whether or not to retry, obey. + if should_retry_header == "true": + log.debug("Retrying as header `x-should-retry` is set to `true`") + return True + if should_retry_header == "false": + log.debug("Not retrying as header `x-should-retry` is set to `false`") + return False + + # Retry on request timeouts. + if response.status_code == 408: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on lock timeouts. + if response.status_code == 409: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on rate limits. + if response.status_code == 429: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry internal errors. + if response.status_code >= 500: + log.debug("Retrying due to status code %i", response.status_code) + return True + + log.debug("Not retrying") + return False + + def _idempotency_key(self) -> str: + return f"stainless-python-retry-{uuid.uuid4()}" + + +class _DefaultHttpxClient(httpx.Client): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultHttpxClient = httpx.Client + """An alias to `httpx.Client` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.Client` will result in httpx's defaults being used, not ours. + """ +else: + DefaultHttpxClient = _DefaultHttpxClient + + +class SyncHttpxClientWrapper(DefaultHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + self.close() + except Exception: + pass + + +class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): + _client: httpx.Client + _default_stream_cls: type[Stream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.Client | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + _strict_response_validation: bool, + ) -> None: + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" + ) + + super().__init__( + version=version, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + base_url=base_url, + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or SyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + # If an error is thrown while constructing a client, self._client + # may not be present + if hasattr(self, "_client"): + self._client.close() + + def __enter__(self: _T) -> _T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: Type[_StreamT], + ) -> _StreamT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: Type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + cast_to = self._maybe_override_cast_to(cast_to, options) + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() + + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) + + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = self._prepare_options(options) + + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + + response = None + try: + response = self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + err.response.close() + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + break + + assert response is not None, "could not resolve response (should never happen)" + return self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + time.sleep(timeout) + + def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + origin = get_origin(cast_to) or cast_to + + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): + if not issubclass(origin, APIResponse): + raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + ResponseT, + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = APIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return api_response.parse() + + def _request_api_list( + self, + model: Type[object], + page: Type[SyncPageT], + options: FinalRequestOptions, + ) -> SyncPageT: + def _parser(resp: SyncPageT) -> SyncPageT: + resp._set_private_attributes( + client=self, + model=model, + options=options, + ) + return resp + + options.post_parser = _parser + + return self.request(page, options, stream=False) + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + # cast is required because mypy complains about returning Any even though + # it understands the type variables + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, files=to_httpx_files(files), **options + ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + return self.request(cast_to, opts) + + def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + return self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[object], + page: Type[SyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> SyncPageT: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +class _DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultAsyncHttpxClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.AsyncClient` will result in httpx's defaults being used, not ours. + """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" +else: + DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient + + +class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + # TODO(someday): support non asyncio runtimes here + asyncio.get_running_loop().create_task(self.aclose()) + except Exception: + pass + + +class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): + _client: httpx.AsyncClient + _default_stream_cls: type[AsyncStream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.AsyncClient | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" + ) + + super().__init__( + version=version, + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or AsyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + async def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + await self._client.aclose() + + async def __aenter__(self: _T) -> _T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + async def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + if self._platform is None: + # `get_platform` can make blocking IO calls so we + # execute it earlier while we are in an async context + self._platform = await asyncify(get_platform)() + + cast_to = self._maybe_override_cast_to(cast_to, options) + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() + + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) + + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = await self._prepare_options(options) + + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + await self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + + response = None + try: + response = await self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + await err.response.aclose() + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + break + + assert response is not None, "could not resolve response (should never happen)" + return await self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + async def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + await anyio.sleep(timeout) + + async def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + origin = get_origin(cast_to) or cast_to + + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): + if not issubclass(origin, AsyncAPIResponse): + raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + "ResponseT", + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = AsyncAPIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return await api_response.parse() + + def _request_api_list( + self, + model: Type[_T], + page: Type[AsyncPageT], + options: FinalRequestOptions, + ) -> AsyncPaginator[_T, AsyncPageT]: + return AsyncPaginator(client=self, options=options, page_cls=page, model=model) + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + async def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + return await self.request(cast_to, opts) + + async def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts) + + async def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + return await self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[_T], + page: Type[AsyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> AsyncPaginator[_T, AsyncPageT]: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +def make_request_options( + *, + query: Query | None = None, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + idempotency_key: str | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, +) -> RequestOptions: + """Create a dict of type RequestOptions without keys of NotGiven values.""" + options: RequestOptions = {} + if extra_headers is not None: + options["headers"] = extra_headers + + if extra_body is not None: + options["extra_json"] = cast(AnyMapping, extra_body) + + if query is not None: + options["params"] = query + + if extra_query is not None: + options["params"] = {**options.get("params", {}), **extra_query} + + if not isinstance(timeout, NotGiven): + options["timeout"] = timeout + + if idempotency_key is not None: + options["idempotency_key"] = idempotency_key + + if is_given(post_parser): + # internal + options["post_parser"] = post_parser # type: ignore + + return options + + +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + +class OtherPlatform: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"Other:{self.name}" + + +Platform = Union[ + OtherPlatform, + Literal[ + "MacOS", + "Linux", + "Windows", + "FreeBSD", + "OpenBSD", + "iOS", + "Android", + "Unknown", + ], +] + + +def get_platform() -> Platform: + try: + system = platform.system().lower() + platform_name = platform.platform().lower() + except Exception: + return "Unknown" + + if "iphone" in platform_name or "ipad" in platform_name: + # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 + # system is Darwin and platform_name is a string like: + # - Darwin-21.6.0-iPhone12,1-64bit + # - Darwin-21.6.0-iPad7,11-64bit + return "iOS" + + if system == "darwin": + return "MacOS" + + if system == "windows": + return "Windows" + + if "android" in platform_name: + # Tested using Pydroid 3 + # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' + return "Android" + + if system == "linux": + # https://distro.readthedocs.io/en/latest/#distro.id + distro_id = distro.id() + if distro_id == "freebsd": + return "FreeBSD" + + if distro_id == "openbsd": + return "OpenBSD" + + return "Linux" + + if platform_name: + return OtherPlatform(platform_name) + + return "Unknown" + + +@lru_cache(maxsize=None) +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(platform or get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), + } + + +class OtherArch: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"other:{self.name}" + + +Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] + + +def get_python_runtime() -> str: + try: + return platform.python_implementation() + except Exception: + return "unknown" + + +def get_python_version() -> str: + try: + return platform.python_version() + except Exception: + return "unknown" + + +def get_architecture() -> Arch: + try: + machine = platform.machine().lower() + except Exception: + return "unknown" + + if machine in ("arm64", "aarch64"): + return "arm64" + + # TODO: untested + if machine == "arm": + return "arm" + + if machine == "x86_64": + return "x64" + + # TODO: untested + if sys.maxsize <= 2**32: + return "x32" + + if machine: + return OtherArch(machine) + + return "unknown" + + +def _merge_mappings( + obj1: Mapping[_T_co, Union[_T, Omit]], + obj2: Mapping[_T_co, Union[_T, Omit]], +) -> Dict[_T_co, _T]: + """Merge two mappings of the same type, removing any values that are instances of `Omit`. + + In cases with duplicate keys the second mapping takes precedence. + """ + merged = {**obj1, **obj2} + return {key: value for key, value in merged.items() if not isinstance(value, Omit)} diff --git a/src/gradient/_client.py b/src/gradient/_client.py new file mode 100644 index 00000000..6734b4b6 --- /dev/null +++ b/src/gradient/_client.py @@ -0,0 +1,954 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import TYPE_CHECKING, Any, Mapping +from typing_extensions import Self, override + +import httpx + +from . import _exceptions +from ._qs import Querystring +from ._types import ( + Omit, + Headers, + Timeout, + NotGiven, + Transport, + ProxiesTypes, + RequestOptions, + not_given, +) +from ._utils import is_given, get_async_library +from ._compat import cached_property +from ._version import __version__ +from ._streaming import Stream as Stream, AsyncStream as AsyncStream +from ._exceptions import APIStatusError +from ._base_client import ( + DEFAULT_MAX_RETRIES, + SyncAPIClient, + AsyncAPIClient, +) + +if TYPE_CHECKING: + from .resources import ( + nfs, + chat, + agents, + images, + models, + regions, + databases, + inference, + gpu_droplets, + knowledge_bases, + ) + from .resources.images import ImagesResource, AsyncImagesResource + from .resources.nfs.nfs import NfsResource, AsyncNfsResource + from .resources.regions import RegionsResource, AsyncRegionsResource + from .resources.chat.chat import ChatResource, AsyncChatResource + from .resources.agents.agents import AgentsResource, AsyncAgentsResource + from .resources.models.models import ModelsResource, AsyncModelsResource + from .resources.databases.databases import DatabasesResource, AsyncDatabasesResource + from .resources.inference.inference import InferenceResource, AsyncInferenceResource + from .resources.gpu_droplets.gpu_droplets import GPUDropletsResource, AsyncGPUDropletsResource + from .resources.knowledge_bases.knowledge_bases import KnowledgeBasesResource, AsyncKnowledgeBasesResource + +__all__ = [ + "Timeout", + "Transport", + "ProxiesTypes", + "RequestOptions", + "Gradient", + "AsyncGradient", + "Client", + "AsyncClient", +] + + +class Gradient(SyncAPIClient): + # client options + access_token: str | None + model_access_key: str | None + agent_access_key: str | None + agent_endpoint: str | None + inference_endpoint: str | None + + def __init__( + self, + *, + access_token: str | None = None, + model_access_key: str | None = None, + agent_access_key: str | None = None, + agent_endpoint: str | None = None, + inference_endpoint: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + http_client: httpx.Client | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new synchronous Gradient client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `access_token` from `DIGITALOCEAN_ACCESS_TOKEN` + - `model_access_key` from `GRADIENT_MODEL_ACCESS_KEY` + - `agent_access_key` from `GRADIENT_AGENT_ACCESS_KEY` + - `agent_endpoint` from `GRADIENT_AGENT_ENDPOINT` + - `inference_endpoint` from `GRADIENT_INFERENCE_ENDPOINT` + """ + if access_token is None: + access_token = os.environ.get("DIGITALOCEAN_ACCESS_TOKEN") + self.access_token = access_token + + if model_access_key is None: + model_access_key = os.environ.get("GRADIENT_MODEL_ACCESS_KEY") + self.model_access_key = model_access_key + + if agent_access_key is None: + agent_access_key = os.environ.get("GRADIENT_AGENT_ACCESS_KEY") + self.agent_access_key = agent_access_key + + if agent_endpoint is None: + agent_endpoint = os.environ.get("GRADIENT_AGENT_ENDPOINT") + self.agent_endpoint = agent_endpoint + + if inference_endpoint is None: + inference_endpoint = os.environ.get("GRADIENT_INFERENCE_ENDPOINT") or "inference.do-ai.run" + self.inference_endpoint = inference_endpoint + + if base_url is None: + base_url = os.environ.get("GRADIENT_BASE_URL") + self._base_url_overridden = base_url is not None + if base_url is None: + base_url = f"https://api.digitalocean.com" + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self._default_stream_cls = Stream + + @cached_property + def agents(self) -> AgentsResource: + from .resources.agents import AgentsResource + + return AgentsResource(self) + + @cached_property + def chat(self) -> ChatResource: + from .resources.chat import ChatResource + + return ChatResource(self) + + @cached_property + def images(self) -> ImagesResource: + from .resources.images import ImagesResource + + return ImagesResource(self) + + @cached_property + def gpu_droplets(self) -> GPUDropletsResource: + from .resources.gpu_droplets import GPUDropletsResource + + return GPUDropletsResource(self) + + @cached_property + def inference(self) -> InferenceResource: + from .resources.inference import InferenceResource + + return InferenceResource(self) + + @cached_property + def knowledge_bases(self) -> KnowledgeBasesResource: + from .resources.knowledge_bases import KnowledgeBasesResource + + return KnowledgeBasesResource(self) + + @cached_property + def models(self) -> ModelsResource: + from .resources.models import ModelsResource + + return ModelsResource(self) + + @cached_property + def regions(self) -> RegionsResource: + from .resources.regions import RegionsResource + + return RegionsResource(self) + + @cached_property + def databases(self) -> DatabasesResource: + from .resources.databases import DatabasesResource + + return DatabasesResource(self) + + @cached_property + def nfs(self) -> NfsResource: + from .resources.nfs import NfsResource + + return NfsResource(self) + + @cached_property + def with_raw_response(self) -> GradientWithRawResponse: + return GradientWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GradientWithStreamedResponse: + return GradientWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + return {**self._bearer_auth, **self._model_access_key, **self._agent_access_key} + + @property + def _bearer_auth(self) -> dict[str, str]: + access_token = self.access_token + if access_token is None: + return {} + return {"Authorization": f"Bearer {access_token}"} + + @property + def _model_access_key(self) -> dict[str, str]: + model_access_key = self.model_access_key + if model_access_key is None: + return {} + return {"Authorization": f"Bearer {model_access_key}"} + + @property + def _agent_access_key(self) -> dict[str, str]: + agent_access_key = self.agent_access_key + if agent_access_key is None: + return {} + return {"Authorization": f"Bearer {agent_access_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": "false", + **self._custom_headers, + } + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if self.access_token and headers.get("Authorization"): + return + if isinstance(custom_headers.get("Authorization"), Omit): + return + + if self.model_access_key and headers.get("Authorization"): + return + if isinstance(custom_headers.get("Authorization"), Omit): + return + + if self.agent_access_key and headers.get("Authorization"): + return + if isinstance(custom_headers.get("Authorization"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected one of access_token, model_access_key or agent_access_key to be set. Or for one of the `Authorization`, `Authorization` or `Authorization` headers to be explicitly omitted"' + ) + + def copy( + self, + *, + access_token: str | None = None, + model_access_key: str | None = None, + agent_access_key: str | None = None, + agent_endpoint: str | None = None, + inference_endpoint: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.Client | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + client = self.__class__( + access_token=access_token or self.access_token, + model_access_key=model_access_key or self.model_access_key, + agent_access_key=agent_access_key or self.agent_access_key, + agent_endpoint=agent_endpoint or self.agent_endpoint, + inference_endpoint=inference_endpoint or self.inference_endpoint, + base_url=base_url or self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + client._base_url_overridden = self._base_url_overridden or base_url is not None + return client + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class AsyncGradient(AsyncAPIClient): + # client options + access_token: str | None + model_access_key: str | None + agent_access_key: str | None + agent_endpoint: str | None + inference_endpoint: str | None + + def __init__( + self, + *, + access_token: str | None = None, + model_access_key: str | None = None, + agent_access_key: str | None = None, + agent_endpoint: str | None = None, + inference_endpoint: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + http_client: httpx.AsyncClient | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new async AsyncGradient client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `access_token` from `DIGITALOCEAN_ACCESS_TOKEN` + - `model_access_key` from `GRADIENT_MODEL_ACCESS_KEY` + - `agent_access_key` from `GRADIENT_AGENT_ACCESS_KEY` + - `agent_endpoint` from `GRADIENT_AGENT_ENDPOINT` + - `inference_endpoint` from `GRADIENT_INFERENCE_ENDPOINT` + """ + if access_token is None: + access_token = os.environ.get("DIGITALOCEAN_ACCESS_TOKEN") + self.access_token = access_token + + if model_access_key is None: + model_access_key = os.environ.get("GRADIENT_MODEL_ACCESS_KEY") + self.model_access_key = model_access_key + + if agent_access_key is None: + agent_access_key = os.environ.get("GRADIENT_AGENT_ACCESS_KEY") + self.agent_access_key = agent_access_key + + if agent_endpoint is None: + agent_endpoint = os.environ.get("GRADIENT_AGENT_ENDPOINT") + self.agent_endpoint = agent_endpoint + + if inference_endpoint is None: + inference_endpoint = os.environ.get("GRADIENT_INFERENCE_ENDPOINT") or "inference.do-ai.run" + self.inference_endpoint = inference_endpoint + + if base_url is None: + base_url = os.environ.get("GRADIENT_BASE_URL") + self._base_url_overridden = base_url is not None + if base_url is None: + base_url = f"https://api.digitalocean.com" + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self._default_stream_cls = AsyncStream + + @cached_property + def agents(self) -> AsyncAgentsResource: + from .resources.agents import AsyncAgentsResource + + return AsyncAgentsResource(self) + + @cached_property + def chat(self) -> AsyncChatResource: + from .resources.chat import AsyncChatResource + + return AsyncChatResource(self) + + @cached_property + def images(self) -> AsyncImagesResource: + from .resources.images import AsyncImagesResource + + return AsyncImagesResource(self) + + @cached_property + def gpu_droplets(self) -> AsyncGPUDropletsResource: + from .resources.gpu_droplets import AsyncGPUDropletsResource + + return AsyncGPUDropletsResource(self) + + @cached_property + def inference(self) -> AsyncInferenceResource: + from .resources.inference import AsyncInferenceResource + + return AsyncInferenceResource(self) + + @cached_property + def knowledge_bases(self) -> AsyncKnowledgeBasesResource: + from .resources.knowledge_bases import AsyncKnowledgeBasesResource + + return AsyncKnowledgeBasesResource(self) + + @cached_property + def models(self) -> AsyncModelsResource: + from .resources.models import AsyncModelsResource + + return AsyncModelsResource(self) + + @cached_property + def regions(self) -> AsyncRegionsResource: + from .resources.regions import AsyncRegionsResource + + return AsyncRegionsResource(self) + + @cached_property + def databases(self) -> AsyncDatabasesResource: + from .resources.databases import AsyncDatabasesResource + + return AsyncDatabasesResource(self) + + @cached_property + def nfs(self) -> AsyncNfsResource: + from .resources.nfs import AsyncNfsResource + + return AsyncNfsResource(self) + + @cached_property + def with_raw_response(self) -> AsyncGradientWithRawResponse: + return AsyncGradientWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGradientWithStreamedResponse: + return AsyncGradientWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + return {**self._bearer_auth, **self._model_access_key, **self._agent_access_key} + + @property + def _bearer_auth(self) -> dict[str, str]: + access_token = self.access_token + if access_token is None: + return {} + return {"Authorization": f"Bearer {access_token}"} + + @property + def _model_access_key(self) -> dict[str, str]: + model_access_key = self.model_access_key + if model_access_key is None: + return {} + return {"Authorization": f"Bearer {model_access_key}"} + + @property + def _agent_access_key(self) -> dict[str, str]: + agent_access_key = self.agent_access_key + if agent_access_key is None: + return {} + return {"Authorization": f"Bearer {agent_access_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": f"async:{get_async_library()}", + **self._custom_headers, + } + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if self.access_token and headers.get("Authorization"): + return + if isinstance(custom_headers.get("Authorization"), Omit): + return + + if self.model_access_key and headers.get("Authorization"): + return + if isinstance(custom_headers.get("Authorization"), Omit): + return + + if self.agent_access_key and headers.get("Authorization"): + return + if isinstance(custom_headers.get("Authorization"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected one of access_token, model_access_key or agent_access_key to be set. Or for one of the `Authorization`, `Authorization` or `Authorization` headers to be explicitly omitted"' + ) + + def copy( + self, + *, + access_token: str | None = None, + model_access_key: str | None = None, + agent_access_key: str | None = None, + agent_endpoint: str | None = None, + inference_endpoint: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.AsyncClient | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + client = self.__class__( + access_token=access_token or self.access_token, + model_access_key=model_access_key or self.model_access_key, + agent_access_key=agent_access_key or self.agent_access_key, + agent_endpoint=agent_endpoint or self.agent_endpoint, + inference_endpoint=inference_endpoint or self.inference_endpoint, + base_url=base_url or self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + client._base_url_overridden = self._base_url_overridden or base_url is not None + return client + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class GradientWithRawResponse: + _client: Gradient + + def __init__(self, client: Gradient) -> None: + self._client = client + + @cached_property + def agents(self) -> agents.AgentsResourceWithRawResponse: + from .resources.agents import AgentsResourceWithRawResponse + + return AgentsResourceWithRawResponse(self._client.agents) + + @cached_property + def chat(self) -> chat.ChatResourceWithRawResponse: + from .resources.chat import ChatResourceWithRawResponse + + return ChatResourceWithRawResponse(self._client.chat) + + @cached_property + def images(self) -> images.ImagesResourceWithRawResponse: + from .resources.images import ImagesResourceWithRawResponse + + return ImagesResourceWithRawResponse(self._client.images) + + @cached_property + def gpu_droplets(self) -> gpu_droplets.GPUDropletsResourceWithRawResponse: + from .resources.gpu_droplets import GPUDropletsResourceWithRawResponse + + return GPUDropletsResourceWithRawResponse(self._client.gpu_droplets) + + @cached_property + def inference(self) -> inference.InferenceResourceWithRawResponse: + from .resources.inference import InferenceResourceWithRawResponse + + return InferenceResourceWithRawResponse(self._client.inference) + + @cached_property + def knowledge_bases(self) -> knowledge_bases.KnowledgeBasesResourceWithRawResponse: + from .resources.knowledge_bases import KnowledgeBasesResourceWithRawResponse + + return KnowledgeBasesResourceWithRawResponse(self._client.knowledge_bases) + + @cached_property + def models(self) -> models.ModelsResourceWithRawResponse: + from .resources.models import ModelsResourceWithRawResponse + + return ModelsResourceWithRawResponse(self._client.models) + + @cached_property + def regions(self) -> regions.RegionsResourceWithRawResponse: + from .resources.regions import RegionsResourceWithRawResponse + + return RegionsResourceWithRawResponse(self._client.regions) + + @cached_property + def databases(self) -> databases.DatabasesResourceWithRawResponse: + from .resources.databases import DatabasesResourceWithRawResponse + + return DatabasesResourceWithRawResponse(self._client.databases) + + @cached_property + def nfs(self) -> nfs.NfsResourceWithRawResponse: + from .resources.nfs import NfsResourceWithRawResponse + + return NfsResourceWithRawResponse(self._client.nfs) + + +class AsyncGradientWithRawResponse: + _client: AsyncGradient + + def __init__(self, client: AsyncGradient) -> None: + self._client = client + + @cached_property + def agents(self) -> agents.AsyncAgentsResourceWithRawResponse: + from .resources.agents import AsyncAgentsResourceWithRawResponse + + return AsyncAgentsResourceWithRawResponse(self._client.agents) + + @cached_property + def chat(self) -> chat.AsyncChatResourceWithRawResponse: + from .resources.chat import AsyncChatResourceWithRawResponse + + return AsyncChatResourceWithRawResponse(self._client.chat) + + @cached_property + def images(self) -> images.AsyncImagesResourceWithRawResponse: + from .resources.images import AsyncImagesResourceWithRawResponse + + return AsyncImagesResourceWithRawResponse(self._client.images) + + @cached_property + def gpu_droplets(self) -> gpu_droplets.AsyncGPUDropletsResourceWithRawResponse: + from .resources.gpu_droplets import AsyncGPUDropletsResourceWithRawResponse + + return AsyncGPUDropletsResourceWithRawResponse(self._client.gpu_droplets) + + @cached_property + def inference(self) -> inference.AsyncInferenceResourceWithRawResponse: + from .resources.inference import AsyncInferenceResourceWithRawResponse + + return AsyncInferenceResourceWithRawResponse(self._client.inference) + + @cached_property + def knowledge_bases(self) -> knowledge_bases.AsyncKnowledgeBasesResourceWithRawResponse: + from .resources.knowledge_bases import AsyncKnowledgeBasesResourceWithRawResponse + + return AsyncKnowledgeBasesResourceWithRawResponse(self._client.knowledge_bases) + + @cached_property + def models(self) -> models.AsyncModelsResourceWithRawResponse: + from .resources.models import AsyncModelsResourceWithRawResponse + + return AsyncModelsResourceWithRawResponse(self._client.models) + + @cached_property + def regions(self) -> regions.AsyncRegionsResourceWithRawResponse: + from .resources.regions import AsyncRegionsResourceWithRawResponse + + return AsyncRegionsResourceWithRawResponse(self._client.regions) + + @cached_property + def databases(self) -> databases.AsyncDatabasesResourceWithRawResponse: + from .resources.databases import AsyncDatabasesResourceWithRawResponse + + return AsyncDatabasesResourceWithRawResponse(self._client.databases) + + @cached_property + def nfs(self) -> nfs.AsyncNfsResourceWithRawResponse: + from .resources.nfs import AsyncNfsResourceWithRawResponse + + return AsyncNfsResourceWithRawResponse(self._client.nfs) + + +class GradientWithStreamedResponse: + _client: Gradient + + def __init__(self, client: Gradient) -> None: + self._client = client + + @cached_property + def agents(self) -> agents.AgentsResourceWithStreamingResponse: + from .resources.agents import AgentsResourceWithStreamingResponse + + return AgentsResourceWithStreamingResponse(self._client.agents) + + @cached_property + def chat(self) -> chat.ChatResourceWithStreamingResponse: + from .resources.chat import ChatResourceWithStreamingResponse + + return ChatResourceWithStreamingResponse(self._client.chat) + + @cached_property + def images(self) -> images.ImagesResourceWithStreamingResponse: + from .resources.images import ImagesResourceWithStreamingResponse + + return ImagesResourceWithStreamingResponse(self._client.images) + + @cached_property + def gpu_droplets(self) -> gpu_droplets.GPUDropletsResourceWithStreamingResponse: + from .resources.gpu_droplets import GPUDropletsResourceWithStreamingResponse + + return GPUDropletsResourceWithStreamingResponse(self._client.gpu_droplets) + + @cached_property + def inference(self) -> inference.InferenceResourceWithStreamingResponse: + from .resources.inference import InferenceResourceWithStreamingResponse + + return InferenceResourceWithStreamingResponse(self._client.inference) + + @cached_property + def knowledge_bases(self) -> knowledge_bases.KnowledgeBasesResourceWithStreamingResponse: + from .resources.knowledge_bases import KnowledgeBasesResourceWithStreamingResponse + + return KnowledgeBasesResourceWithStreamingResponse(self._client.knowledge_bases) + + @cached_property + def models(self) -> models.ModelsResourceWithStreamingResponse: + from .resources.models import ModelsResourceWithStreamingResponse + + return ModelsResourceWithStreamingResponse(self._client.models) + + @cached_property + def regions(self) -> regions.RegionsResourceWithStreamingResponse: + from .resources.regions import RegionsResourceWithStreamingResponse + + return RegionsResourceWithStreamingResponse(self._client.regions) + + @cached_property + def databases(self) -> databases.DatabasesResourceWithStreamingResponse: + from .resources.databases import DatabasesResourceWithStreamingResponse + + return DatabasesResourceWithStreamingResponse(self._client.databases) + + @cached_property + def nfs(self) -> nfs.NfsResourceWithStreamingResponse: + from .resources.nfs import NfsResourceWithStreamingResponse + + return NfsResourceWithStreamingResponse(self._client.nfs) + + +class AsyncGradientWithStreamedResponse: + _client: AsyncGradient + + def __init__(self, client: AsyncGradient) -> None: + self._client = client + + @cached_property + def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse: + from .resources.agents import AsyncAgentsResourceWithStreamingResponse + + return AsyncAgentsResourceWithStreamingResponse(self._client.agents) + + @cached_property + def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: + from .resources.chat import AsyncChatResourceWithStreamingResponse + + return AsyncChatResourceWithStreamingResponse(self._client.chat) + + @cached_property + def images(self) -> images.AsyncImagesResourceWithStreamingResponse: + from .resources.images import AsyncImagesResourceWithStreamingResponse + + return AsyncImagesResourceWithStreamingResponse(self._client.images) + + @cached_property + def gpu_droplets(self) -> gpu_droplets.AsyncGPUDropletsResourceWithStreamingResponse: + from .resources.gpu_droplets import AsyncGPUDropletsResourceWithStreamingResponse + + return AsyncGPUDropletsResourceWithStreamingResponse(self._client.gpu_droplets) + + @cached_property + def inference(self) -> inference.AsyncInferenceResourceWithStreamingResponse: + from .resources.inference import AsyncInferenceResourceWithStreamingResponse + + return AsyncInferenceResourceWithStreamingResponse(self._client.inference) + + @cached_property + def knowledge_bases(self) -> knowledge_bases.AsyncKnowledgeBasesResourceWithStreamingResponse: + from .resources.knowledge_bases import AsyncKnowledgeBasesResourceWithStreamingResponse + + return AsyncKnowledgeBasesResourceWithStreamingResponse(self._client.knowledge_bases) + + @cached_property + def models(self) -> models.AsyncModelsResourceWithStreamingResponse: + from .resources.models import AsyncModelsResourceWithStreamingResponse + + return AsyncModelsResourceWithStreamingResponse(self._client.models) + + @cached_property + def regions(self) -> regions.AsyncRegionsResourceWithStreamingResponse: + from .resources.regions import AsyncRegionsResourceWithStreamingResponse + + return AsyncRegionsResourceWithStreamingResponse(self._client.regions) + + @cached_property + def databases(self) -> databases.AsyncDatabasesResourceWithStreamingResponse: + from .resources.databases import AsyncDatabasesResourceWithStreamingResponse + + return AsyncDatabasesResourceWithStreamingResponse(self._client.databases) + + @cached_property + def nfs(self) -> nfs.AsyncNfsResourceWithStreamingResponse: + from .resources.nfs import AsyncNfsResourceWithStreamingResponse + + return AsyncNfsResourceWithStreamingResponse(self._client.nfs) + + +Client = Gradient + +AsyncClient = AsyncGradient diff --git a/src/gradient/_compat.py b/src/gradient/_compat.py new file mode 100644 index 00000000..bdef67f0 --- /dev/null +++ b/src/gradient/_compat.py @@ -0,0 +1,219 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload +from datetime import date, datetime +from typing_extensions import Self, Literal + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import IncEx, StrBytesIntFloat + +_T = TypeVar("_T") +_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) + +# --------------- Pydantic v2, v3 compatibility --------------- + +# Pyright incorrectly reports some of our functions as overriding a method when they don't +# pyright: reportIncompatibleMethodOverride=false + +PYDANTIC_V1 = pydantic.VERSION.startswith("1.") + +if TYPE_CHECKING: + + def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 + ... + + def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 + ... + + def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 + ... + + def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 + ... + + def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 + ... + + def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 + ... + + def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 + ... + +else: + # v1 re-exports + if PYDANTIC_V1: + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + else: + from ._utils import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + parse_date as parse_date, + is_typeddict as is_typeddict, + parse_datetime as parse_datetime, + is_literal_type as is_literal_type, + ) + + +# refactored config +if TYPE_CHECKING: + from pydantic import ConfigDict as ConfigDict +else: + if PYDANTIC_V1: + # TODO: provide an error message here? + ConfigDict = None + else: + from pydantic import ConfigDict as ConfigDict + + +# renamed methods / properties +def parse_obj(model: type[_ModelT], value: object) -> _ModelT: + if PYDANTIC_V1: + return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + else: + return model.model_validate(value) + + +def field_is_required(field: FieldInfo) -> bool: + if PYDANTIC_V1: + return field.required # type: ignore + return field.is_required() + + +def field_get_default(field: FieldInfo) -> Any: + value = field.get_default() + if PYDANTIC_V1: + return value + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + + +def field_outer_type(field: FieldInfo) -> Any: + if PYDANTIC_V1: + return field.outer_type_ # type: ignore + return field.annotation + + +def get_model_config(model: type[pydantic.BaseModel]) -> Any: + if PYDANTIC_V1: + return model.__config__ # type: ignore + return model.model_config + + +def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: + if PYDANTIC_V1: + return model.__fields__ # type: ignore + return model.model_fields + + +def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: + if PYDANTIC_V1: + return model.copy(deep=deep) # type: ignore + return model.model_copy(deep=deep) + + +def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: + if PYDANTIC_V1: + return model.json(indent=indent) # type: ignore + return model.model_dump_json(indent=indent) + + +def model_dump( + model: pydantic.BaseModel, + *, + exclude: IncEx | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + warnings: bool = True, + mode: Literal["json", "python"] = "python", +) -> dict[str, Any]: + if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + return model.model_dump( + mode=mode, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + # warnings are not supported in Pydantic v1 + warnings=True if PYDANTIC_V1 else warnings, + ) + return cast( + "dict[str, Any]", + model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + ), + ) + + +def model_parse(model: type[_ModelT], data: Any) -> _ModelT: + if PYDANTIC_V1: + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + return model.model_validate(data) + + +# generic models +if TYPE_CHECKING: + + class GenericModel(pydantic.BaseModel): ... + +else: + if PYDANTIC_V1: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + else: + # there no longer needs to be a distinction in v2 but + # we still have to create our own subclass to avoid + # inconsistent MRO ordering errors + class GenericModel(pydantic.BaseModel): ... + + +# cached properties +if TYPE_CHECKING: + cached_property = property + + # we define a separate type (copied from typeshed) + # that represents that `cached_property` is `set`able + # at runtime, which differs from `@property`. + # + # this is a separate type as editors likely special case + # `@property` and we don't want to cause issues just to have + # more helpful internal types. + + class typed_cached_property(Generic[_T]): + func: Callable[[Any], _T] + attrname: str | None + + def __init__(self, func: Callable[[Any], _T]) -> None: ... + + @overload + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... + + @overload + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... + + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: + raise NotImplementedError() + + def __set_name__(self, owner: type[Any], name: str) -> None: ... + + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: ... +else: + from functools import cached_property as cached_property + + typed_cached_property = cached_property diff --git a/src/gradient/_constants.py b/src/gradient/_constants.py new file mode 100644 index 00000000..6ddf2c71 --- /dev/null +++ b/src/gradient/_constants.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import httpx + +RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" +OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" + +# default timeout is 1 minute +DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0) +DEFAULT_MAX_RETRIES = 2 +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) + +INITIAL_RETRY_DELAY = 0.5 +MAX_RETRY_DELAY = 8.0 diff --git a/src/gradient/_exceptions.py b/src/gradient/_exceptions.py new file mode 100644 index 00000000..5db08573 --- /dev/null +++ b/src/gradient/_exceptions.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +__all__ = [ + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", +] + + +class GradientError(Exception): + pass + + +class APIError(GradientError): + message: str + request: httpx.Request + + body: object | None + """The API response body. + + If the API responded with a valid JSON structure then this property will be the + decoded result. + + If it isn't a valid JSON structure then this will be the raw response. + + If there was no response associated with this error then it will be `None`. + """ + + def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: # noqa: ARG002 + super().__init__(message) + self.request = request + self.message = message + self.body = body + + +class APIResponseValidationError(APIError): + response: httpx.Response + status_code: int + + def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None: + super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIStatusError(APIError): + """Raised when an API response has a status code of 4xx or 5xx.""" + + response: httpx.Response + status_code: int + + def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: + super().__init__(message, response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIConnectionError(APIError): + def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: + super().__init__(message, request, body=None) + + +class APITimeoutError(APIConnectionError): + def __init__(self, request: httpx.Request) -> None: + super().__init__(message="Request timed out.", request=request) + + +class BadRequestError(APIStatusError): + status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] + + +class AuthenticationError(APIStatusError): + status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] + + +class PermissionDeniedError(APIStatusError): + status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] + + +class NotFoundError(APIStatusError): + status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] + + +class ConflictError(APIStatusError): + status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] + + +class UnprocessableEntityError(APIStatusError): + status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] + + +class RateLimitError(APIStatusError): + status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] + + +class InternalServerError(APIStatusError): + pass diff --git a/src/gradient/_files.py b/src/gradient/_files.py new file mode 100644 index 00000000..cc14c14f --- /dev/null +++ b/src/gradient/_files.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +import io +import os +import pathlib +from typing import overload +from typing_extensions import TypeGuard + +import anyio + +from ._types import ( + FileTypes, + FileContent, + RequestFiles, + HttpxFileTypes, + Base64FileInput, + HttpxFileContent, + HttpxRequestFiles, +) +from ._utils import is_tuple_t, is_mapping_t, is_sequence_t + + +def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: + return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + +def is_file_content(obj: object) -> TypeGuard[FileContent]: + return ( + isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + ) + + +def assert_is_file_content(obj: object, *, key: str | None = None) -> None: + if not is_file_content(obj): + prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" + raise RuntimeError( + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead." + ) from None + + +@overload +def to_httpx_files(files: None) -> None: ... + + +@overload +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: _transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, _transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +def _transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = pathlib.Path(file) + return (path.name, path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +def read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return pathlib.Path(file).read_bytes() + return file + + +@overload +async def async_to_httpx_files(files: None) -> None: ... + + +@overload +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: await _async_transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, await _async_transform_file(file)) for key, file in files] + else: + raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = anyio.Path(file) + return (path.name, await path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], await async_read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +async def async_read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return await anyio.Path(file).read_bytes() + + return file diff --git a/src/gradient/_models.py b/src/gradient/_models.py new file mode 100644 index 00000000..ca9500b2 --- /dev/null +++ b/src/gradient/_models.py @@ -0,0 +1,857 @@ +from __future__ import annotations + +import os +import inspect +import weakref +from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast +from datetime import date, datetime +from typing_extensions import ( + List, + Unpack, + Literal, + ClassVar, + Protocol, + Required, + ParamSpec, + TypedDict, + TypeGuard, + final, + override, + runtime_checkable, +) + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import ( + Body, + IncEx, + Query, + ModelT, + Headers, + Timeout, + NotGiven, + AnyMapping, + HttpxRequestFiles, +) +from ._utils import ( + PropertyInfo, + is_list, + is_given, + json_safe, + lru_cache, + is_mapping, + parse_date, + coerce_boolean, + parse_datetime, + strip_not_given, + extract_type_arg, + is_annotated_type, + is_type_alias_type, + strip_annotated_type, +) +from ._compat import ( + PYDANTIC_V1, + ConfigDict, + GenericModel as BaseGenericModel, + get_args, + is_union, + parse_obj, + get_origin, + is_literal_type, + get_model_config, + get_model_fields, + field_get_default, +) +from ._constants import RAW_RESPONSE_HEADER + +if TYPE_CHECKING: + from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema + +__all__ = ["BaseModel", "GenericModel"] + +_T = TypeVar("_T") +_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") + +P = ParamSpec("P") + + +@runtime_checkable +class _ConfigProtocol(Protocol): + allow_population_by_field_name: bool + + +class BaseModel(pydantic.BaseModel): + if PYDANTIC_V1: + + @property + @override + def model_fields_set(self) -> set[str]: + # a forwards-compat shim for pydantic v2 + return self.__fields_set__ # type: ignore + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + extra: Any = pydantic.Extra.allow # type: ignore + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) + + def to_dict( + self, + *, + mode: Literal["json", "python"] = "python", + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> dict[str, object]: + """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + mode: + If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. + If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` + + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. + """ + return self.model_dump( + mode=mode, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + def to_json( + self, + *, + indent: int | None = 2, + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> str: + """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. + """ + return self.model_dump_json( + indent=indent, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + @override + def __str__(self) -> str: + # mypy complains about an invalid self arg + return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] + + # Override the 'construct' method in a way that supports recursive parsing without validation. + # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. + @classmethod + @override + def construct( # pyright: ignore[reportIncompatibleMethodOverride] + __cls: Type[ModelT], + _fields_set: set[str] | None = None, + **values: object, + ) -> ModelT: + m = __cls.__new__(__cls) + fields_values: dict[str, object] = {} + + config = get_model_config(__cls) + populate_by_name = ( + config.allow_population_by_field_name + if isinstance(config, _ConfigProtocol) + else config.get("populate_by_name") + ) + + if _fields_set is None: + _fields_set = set() + + model_fields = get_model_fields(__cls) + for name, field in model_fields.items(): + key = field.alias + if key is None or (key not in values and populate_by_name): + key = name + + if key in values: + fields_values[name] = _construct_field(value=values[key], field=field, key=key) + _fields_set.add(name) + else: + fields_values[name] = field_get_default(field) + + extra_field_type = _get_extra_fields_type(__cls) + + _extra = {} + for key, value in values.items(): + if key not in model_fields: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + + if PYDANTIC_V1: + _fields_set.add(key) + fields_values[key] = parsed + else: + _extra[key] = parsed + + object.__setattr__(m, "__dict__", fields_values) + + if PYDANTIC_V1: + # init_private_attributes() does not exist in v2 + m._init_private_attributes() # type: ignore + + # copied from Pydantic v1's `construct()` method + object.__setattr__(m, "__fields_set__", _fields_set) + else: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) + + return m + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + # because the type signatures are technically different + # although not in practice + model_construct = construct + + if PYDANTIC_V1: + # we define aliases for some of the new pydantic v2 methods so + # that we can just document these methods without having to specify + # a specific pydantic version as some users may not know which + # pydantic version they are currently using + + @override + def model_dump( + self, + *, + mode: Literal["json", "python"] | str = "python", + include: IncEx | None = None, + exclude: IncEx | None = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> dict[str, Any]: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump + + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + Args: + mode: The mode in which `to_python` should run. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. + by_alias: Whether to use the field's alias in the dictionary key if defined. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + + Returns: + A dictionary representation of the model. + """ + if mode not in {"json", "python"}: + raise ValueError("mode must be either 'json' or 'python'") + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") + dumped = super().dict( # pyright: ignore[reportDeprecated] + include=include, + exclude=exclude, + by_alias=by_alias if by_alias is not None else False, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped + + @override + def model_dump_json( + self, + *, + indent: int | None = None, + ensure_ascii: bool = False, + include: IncEx | None = None, + exclude: IncEx | None = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> str: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json + + Generates a JSON representation of the model using Pydantic's `to_json` method. + + Args: + indent: Indentation to use in the JSON output. If None is passed, the output will be compact. + include: Field(s) to include in the JSON output. Can take either a string or set of strings. + exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. + by_alias: Whether to serialize using field aliases. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to use serialization/deserialization between JSON and class instance. + warnings: Whether to show any warnings that occurred during serialization. + + Returns: + A JSON string representation of the model. + """ + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") + return super().json( # type: ignore[reportDeprecated] + indent=indent, + include=include, + exclude=exclude, + by_alias=by_alias if by_alias is not None else False, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + +def _construct_field(value: object, field: FieldInfo, key: str) -> object: + if value is None: + return field_get_default(field) + + if PYDANTIC_V1: + type_ = cast(type, field.outer_type_) # type: ignore + else: + type_ = field.annotation # type: ignore + + if type_ is None: + raise RuntimeError(f"Unexpected field type is None for {key}") + + return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) + + +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if PYDANTIC_V1: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None + + +def is_basemodel(type_: type) -> bool: + """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" + if is_union(type_): + for variant in get_args(type_): + if is_basemodel(variant): + return True + + return False + + return is_basemodel_type(type_) + + +def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: + origin = get_origin(type_) or type_ + if not inspect.isclass(origin): + return False + return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) + + +def build( + base_model_cls: Callable[P, _BaseModelT], + *args: P.args, + **kwargs: P.kwargs, +) -> _BaseModelT: + """Construct a BaseModel class without validation. + + This is useful for cases where you need to instantiate a `BaseModel` + from an API response as this provides type-safe params which isn't supported + by helpers like `construct_type()`. + + ```py + build(MyModel, my_field_a="foo", my_field_b=123) + ``` + """ + if args: + raise TypeError( + "Received positional arguments which are not supported; Keyword arguments must be used instead", + ) + + return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) + + +def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: + """Loose coercion to the expected type with construction of nested values. + + Note: the returned value from this function is not guaranteed to match the + given type. + """ + return cast(_T, construct_type(value=value, type_=type_)) + + +def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: + """Loose coercion to the expected type with construction of nested values. + + If the given value does not match the expected type then it is returned as-is. + """ + + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + + # we allow `object` as the input type because otherwise, passing things like + # `Literal['value']` will be reported as a type error by type checkers + type_ = cast("type[object]", type_) + if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] + type_ = type_.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if metadata is not None and len(metadata) > 0: + meta: tuple[Any, ...] = tuple(metadata) + elif is_annotated_type(type_): + meta = get_args(type_)[1:] + type_ = extract_type_arg(type_, 0) + else: + meta = tuple() + + # we need to use the origin class for any types that are subscripted generics + # e.g. Dict[str, object] + origin = get_origin(type_) or type_ + args = get_args(type_) + + if is_union(origin): + try: + return validate_type(type_=cast("type[object]", original_type or type_), value=value) + except Exception: + pass + + # if the type is a discriminated union then we want to construct the right variant + # in the union, even if the data doesn't match exactly, otherwise we'd break code + # that relies on the constructed class types, e.g. + # + # class FooType: + # kind: Literal['foo'] + # value: str + # + # class BarType: + # kind: Literal['bar'] + # value: int + # + # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then + # we'd end up constructing `FooType` when it should be `BarType`. + discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) + if discriminator and is_mapping(value): + variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) + if variant_value and isinstance(variant_value, str): + variant_type = discriminator.mapping.get(variant_value) + if variant_type: + return construct_type(type_=variant_type, value=value) + + # if the data is not valid, use the first variant that doesn't fail while deserializing + for variant in args: + try: + return construct_type(value=value, type_=variant) + except Exception: + continue + + raise RuntimeError(f"Could not convert data into a valid instance of {type_}") + + if origin == dict: + if not is_mapping(value): + return value + + _, items_type = get_args(type_) # Dict[_, items_type] + return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} + + if ( + not is_literal_type(type_) + and inspect.isclass(origin) + and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) + ): + if is_list(value): + return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] + + if is_mapping(value): + if issubclass(type_, BaseModel): + return type_.construct(**value) # type: ignore[arg-type] + + return cast(Any, type_).construct(**value) + + if origin == list: + if not is_list(value): + return value + + inner_type = args[0] # List[inner_type] + return [construct_type(value=entry, type_=inner_type) for entry in value] + + if origin == float: + if isinstance(value, int): + coerced = float(value) + if coerced != value: + return value + return coerced + + return value + + if type_ == datetime: + try: + return parse_datetime(value) # type: ignore + except Exception: + return value + + if type_ == date: + try: + return parse_date(value) # type: ignore + except Exception: + return value + + return value + + +@runtime_checkable +class CachedDiscriminatorType(Protocol): + __discriminator__: DiscriminatorDetails + + +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + +class DiscriminatorDetails: + field_name: str + """The name of the discriminator field in the variant class, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] + ``` + + Will result in field_name='type' + """ + + field_alias_from: str | None + """The name of the discriminator field in the API response, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] = Field(alias='type_from_api') + ``` + + Will result in field_alias_from='type_from_api' + """ + + mapping: dict[str, type] + """Mapping of discriminator value to variant type, e.g. + + {'foo': FooVariant, 'bar': BarVariant} + """ + + def __init__( + self, + *, + mapping: dict[str, type], + discriminator_field: str, + discriminator_alias: str | None, + ) -> None: + self.mapping = mapping + self.field_name = discriminator_field + self.field_alias_from = discriminator_alias + + +def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached + + discriminator_field_name: str | None = None + + for annotation in meta_annotations: + if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: + discriminator_field_name = annotation.discriminator + break + + if not discriminator_field_name: + return None + + mapping: dict[str, type] = {} + discriminator_alias: str | None = None + + for variant in get_args(union): + variant = strip_annotated_type(variant) + if is_basemodel_type(variant): + if PYDANTIC_V1: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field_info.alias + + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): + for entry in get_args(annotation): + if isinstance(entry, str): + mapping[entry] = variant + else: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field.get("serialization_alias") + + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: + if isinstance(entry, str): + mapping[entry] = variant + + if not mapping: + return None + + details = DiscriminatorDetails( + mapping=mapping, + discriminator_field=discriminator_field_name, + discriminator_alias=discriminator_alias, + ) + DISCRIMINATOR_CACHE.setdefault(union, details) + return details + + +def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: + schema = model.__pydantic_core_schema__ + if schema["type"] == "definitions": + schema = schema["schema"] + + if schema["type"] != "model": + return None + + schema = cast("ModelSchema", schema) + fields_schema = schema["schema"] + if fields_schema["type"] != "model-fields": + return None + + fields_schema = cast("ModelFieldsSchema", fields_schema) + field = fields_schema["fields"].get(field_name) + if not field: + return None + + return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] + + +def validate_type(*, type_: type[_T], value: object) -> _T: + """Strict validation that the given value matches the expected type""" + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + return cast(_T, parse_obj(type_, value)) + + return cast(_T, _validate_non_model_type(type_=type_, value=value)) + + +def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: + """Add a pydantic config for the given type. + + Note: this is a no-op on Pydantic v1. + """ + setattr(typ, "__pydantic_config__", config) # noqa: B010 + + +# our use of subclassing here causes weirdness for type checkers, +# so we just pretend that we don't subclass +if TYPE_CHECKING: + GenericModel = BaseModel +else: + + class GenericModel(BaseGenericModel, BaseModel): + pass + + +if not PYDANTIC_V1: + from pydantic import TypeAdapter as _TypeAdapter + + _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) + + if TYPE_CHECKING: + from pydantic import TypeAdapter + else: + TypeAdapter = _CachedTypeAdapter + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + return TypeAdapter(type_).validate_python(value) + +elif not TYPE_CHECKING: # TODO: condition is weird + + class RootModel(GenericModel, Generic[_T]): + """Used as a placeholder to easily convert runtime types to a Pydantic format + to provide validation. + + For example: + ```py + validated = RootModel[int](__root__="5").__root__ + # validated: 5 + ``` + """ + + __root__: _T + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + model = _create_pydantic_model(type_).validate(value) + return cast(_T, model.__root__) + + def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: + return RootModel[type_] # type: ignore + + +class FinalRequestOptionsInput(TypedDict, total=False): + method: Required[str] + url: Required[str] + params: Query + headers: Headers + max_retries: int + timeout: float | Timeout | None + files: HttpxRequestFiles | None + idempotency_key: str + json_data: Body + extra_json: AnyMapping + follow_redirects: bool + + +@final +class FinalRequestOptions(pydantic.BaseModel): + method: str + url: str + params: Query = {} + headers: Union[Headers, NotGiven] = NotGiven() + max_retries: Union[int, NotGiven] = NotGiven() + timeout: Union[float, Timeout, None, NotGiven] = NotGiven() + files: Union[HttpxRequestFiles, None] = None + idempotency_key: Union[str, None] = None + post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + follow_redirects: Union[bool, None] = None + + # It should be noted that we cannot use `json` here as that would override + # a BaseModel method in an incompatible fashion. + json_data: Union[Body, None] = None + extra_json: Union[AnyMapping, None] = None + + if PYDANTIC_V1: + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) + + def get_max_retries(self, max_retries: int) -> int: + if isinstance(self.max_retries, NotGiven): + return max_retries + return self.max_retries + + def _strip_raw_response_header(self) -> None: + if not is_given(self.headers): + return + + if self.headers.get(RAW_RESPONSE_HEADER): + self.headers = {**self.headers} + self.headers.pop(RAW_RESPONSE_HEADER) + + # override the `construct` method so that we can run custom transformations. + # this is necessary as we don't want to do any actual runtime type checking + # (which means we can't use validators) but we do want to ensure that `NotGiven` + # values are not present + # + # type ignore required because we're adding explicit types to `**values` + @classmethod + def construct( # type: ignore + cls, + _fields_set: set[str] | None = None, + **values: Unpack[FinalRequestOptionsInput], + ) -> FinalRequestOptions: + kwargs: dict[str, Any] = { + # we unconditionally call `strip_not_given` on any value + # as it will just ignore any non-mapping types + key: strip_not_given(value) + for key, value in values.items() + } + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + model_construct = construct diff --git a/src/gradient/_qs.py b/src/gradient/_qs.py new file mode 100644 index 00000000..ada6fd3f --- /dev/null +++ b/src/gradient/_qs.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +from typing import Any, List, Tuple, Union, Mapping, TypeVar +from urllib.parse import parse_qs, urlencode +from typing_extensions import Literal, get_args + +from ._types import NotGiven, not_given +from ._utils import flatten + +_T = TypeVar("_T") + + +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + +PrimitiveData = Union[str, int, float, bool, None] +# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] +# https://github.com/microsoft/pyright/issues/3555 +Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] +Params = Mapping[str, Data] + + +class Querystring: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + *, + array_format: ArrayFormat = "repeat", + nested_format: NestedFormat = "brackets", + ) -> None: + self.array_format = array_format + self.nested_format = nested_format + + def parse(self, query: str) -> Mapping[str, object]: + # Note: custom format syntax is not supported yet + return parse_qs(query) + + def stringify( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> str: + return urlencode( + self.stringify_items( + params, + array_format=array_format, + nested_format=nested_format, + ) + ) + + def stringify_items( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> list[tuple[str, str]]: + opts = Options( + qs=self, + array_format=array_format, + nested_format=nested_format, + ) + return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) + + def _stringify_item( + self, + key: str, + value: Data, + opts: Options, + ) -> list[tuple[str, str]]: + if isinstance(value, Mapping): + items: list[tuple[str, str]] = [] + nested_format = opts.nested_format + for subkey, subvalue in value.items(): + items.extend( + self._stringify_item( + # TODO: error if unknown format + f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", + subvalue, + opts, + ) + ) + return items + + if isinstance(value, (list, tuple)): + array_format = opts.array_format + if array_format == "comma": + return [ + ( + key, + ",".join(self._primitive_value_to_str(item) for item in value if item is not None), + ), + ] + elif array_format == "repeat": + items = [] + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + elif array_format == "indices": + raise NotImplementedError("The array indices format is not supported yet") + elif array_format == "brackets": + items = [] + key = key + "[]" + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + else: + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + serialised = self._primitive_value_to_str(value) + if not serialised: + return [] + return [(key, serialised)] + + def _primitive_value_to_str(self, value: PrimitiveData) -> str: + # copied from httpx + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +_qs = Querystring() +parse = _qs.parse +stringify = _qs.stringify +stringify_items = _qs.stringify_items + + +class Options: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + qs: Querystring = _qs, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> None: + self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format + self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/gradient/_resource.py b/src/gradient/_resource.py new file mode 100644 index 00000000..f2bb6c14 --- /dev/null +++ b/src/gradient/_resource.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import time +from typing import TYPE_CHECKING + +import anyio + +if TYPE_CHECKING: + from ._client import Gradient, AsyncGradient + + +class SyncAPIResource: + _client: Gradient + + def __init__(self, client: Gradient) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + def _sleep(self, seconds: float) -> None: + time.sleep(seconds) + + +class AsyncAPIResource: + _client: AsyncGradient + + def __init__(self, client: AsyncGradient) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + async def _sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) diff --git a/src/gradient/_response.py b/src/gradient/_response.py new file mode 100644 index 00000000..4702edaf --- /dev/null +++ b/src/gradient/_response.py @@ -0,0 +1,830 @@ +from __future__ import annotations + +import os +import inspect +import logging +import datetime +import functools +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Awaitable, ParamSpec, override, get_origin + +import anyio +import httpx +import pydantic + +from ._types import NoneType +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base +from ._models import BaseModel, is_basemodel +from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type +from ._exceptions import GradientError, APIResponseValidationError + +if TYPE_CHECKING: + from ._models import FinalRequestOptions + from ._base_client import BaseClient + + +P = ParamSpec("P") +R = TypeVar("R") +_T = TypeVar("_T") +_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") +_AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") + +log: logging.Logger = logging.getLogger(__name__) + + +class BaseAPIResponse(Generic[R]): + _cast_to: type[R] + _client: BaseClient[Any, Any] + _parsed_by_type: dict[type[Any], Any] + _is_sse_stream: bool + _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None + _options: FinalRequestOptions + + http_response: httpx.Response + + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + + def __init__( + self, + *, + raw: httpx.Response, + cast_to: type[R], + client: BaseClient[Any, Any], + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + options: FinalRequestOptions, + retries_taken: int = 0, + ) -> None: + self._cast_to = cast_to + self._client = client + self._parsed_by_type = {} + self._is_sse_stream = stream + self._stream_cls = stream_cls + self._options = options + self.http_response = raw + self.retries_taken = retries_taken + + @property + def headers(self) -> httpx.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx.Request: + """Returns the httpx Request instance associated with the current response.""" + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def url(self) -> httpx.URL: + """Returns the URL for which the request was made.""" + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def elapsed(self) -> datetime.timedelta: + """The time taken for the complete request/response cycle to complete.""" + return self.http_response.elapsed + + @property + def is_closed(self) -> bool: + """Whether or not the response body has been closed. + + If this is False then there is response data that has not been read yet. + You must either fully consume the response body or call `.close()` + before discarding the response to prevent resource leaks. + """ + return self.http_response.is_closed + + @override + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" + ) + + def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + + origin = get_origin(cast_to) or cast_to + + if self._is_sse_stream: + if to: + if not is_stream_class_type(to): + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") + + return cast( + _T, + to( + cast_to=extract_stream_chunk_type( + to, + failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", + ), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + if self._stream_cls: + return cast( + R, + self._stream_cls( + cast_to=extract_stream_chunk_type(self._stream_cls), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) + if stream_cls is None: + raise MissingStreamClassError() + + return cast( + R, + stream_cls( + cast_to=cast_to, + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + if cast_to is NoneType: + return cast(R, None) + + response = self.http_response + if cast_to == str: + return cast(R, response.text) + + if cast_to == bytes: + return cast(R, response.content) + + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + + if cast_to == bool: + return cast(R, response.text.lower() == "true") + + if origin == APIResponse: + raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + + if inspect.isclass(origin) and issubclass(origin, httpx.Response): + # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response + # and pass that class to our request functions. We cannot change the variance to be either + # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct + # the response class ourselves but that is something that should be supported directly in httpx + # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. + if cast_to != httpx.Response: + raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") + return cast(R, response) + + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): + raise TypeError("Pydantic models must subclass our base model type, e.g. `from gradient import BaseModel`") + + if ( + cast_to is not object + and not origin is list + and not origin is dict + and not origin is Union + and not issubclass(origin, BaseModel) + ): + raise RuntimeError( + f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." + ) + + # split is required to handle cases where additional information is included + # in the response, e.g. application/json; charset=utf-8 + content_type, *_ = response.headers.get("content-type", "*").split(";") + if not content_type.endswith("json"): + if is_basemodel(cast_to): + try: + data = response.json() + except Exception as exc: + log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) + else: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + if self._client._strict_response_validation: + raise APIResponseValidationError( + response=response, + message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", + body=response.text, + ) + + # If the API responds with content that isn't JSON then we just return + # the (decoded) text without performing any parsing so that you can still + # handle the response however you need to. + return response.text # type: ignore + + data = response.json() + + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + +class APIResponse(BaseAPIResponse[R]): + @overload + def parse(self, *, to: type[_T]) -> _T: ... + + @overload + def parse(self) -> R: ... + + def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from gradient import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `int` + - `float` + - `httpx.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed_by_type[cache_key] = parsed + return parsed + + def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return self.http_response.read() + except httpx.StreamConsumed as exc: + # The default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message. + raise StreamAlreadyConsumed() from exc + + def text(self) -> str: + """Read and decode the response content into a string.""" + self.read() + return self.http_response.text + + def json(self) -> object: + """Read and decode the JSON response content.""" + self.read() + return self.http_response.json() + + def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.http_response.close() + + def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + for chunk in self.http_response.iter_bytes(chunk_size): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + for chunk in self.http_response.iter_text(chunk_size): + yield chunk + + def iter_lines(self) -> Iterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + for chunk in self.http_response.iter_lines(): + yield chunk + + +class AsyncAPIResponse(BaseAPIResponse[R]): + @overload + async def parse(self, *, to: type[_T]) -> _T: ... + + @overload + async def parse(self) -> R: ... + + async def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from gradient import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `httpx.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + await self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed_by_type[cache_key] = parsed + return parsed + + async def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return await self.http_response.aread() + except httpx.StreamConsumed as exc: + # the default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message + raise StreamAlreadyConsumed() from exc + + async def text(self) -> str: + """Read and decode the response content into a string.""" + await self.read() + return self.http_response.text + + async def json(self) -> object: + """Read and decode the JSON response content.""" + await self.read() + return self.http_response.json() + + async def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.http_response.aclose() + + async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + async for chunk in self.http_response.aiter_bytes(chunk_size): + yield chunk + + async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + async for chunk in self.http_response.aiter_text(chunk_size): + yield chunk + + async def iter_lines(self) -> AsyncIterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + async for chunk in self.http_response.aiter_lines(): + yield chunk + + +class BinaryAPIResponse(APIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(): + f.write(data) + + +class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + async def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(): + await f.write(data) + + +class StreamedBinaryAPIResponse(APIResponse[bytes]): + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(chunk_size): + f.write(data) + + +class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]): + async def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(chunk_size): + await f.write(data) + + +class MissingStreamClassError(TypeError): + def __init__(self) -> None: + super().__init__( + "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `gradient._streaming` for reference", + ) + + +class StreamAlreadyConsumed(GradientError): + """ + Attempted to read or stream content, but the content has already + been streamed. + + This can happen if you use a method like `.iter_lines()` and then attempt + to read th entire response body afterwards, e.g. + + ```py + response = await client.post(...) + async for line in response.iter_lines(): + ... # do something with `line` + + content = await response.read() + # ^ error + ``` + + If you want this behaviour you'll need to either manually accumulate the response + content or call `await response.read()` before iterating over the stream. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. " + "This could be due to attempting to stream the response " + "content more than once." + "\n\n" + "You can fix this by manually accumulating the response content while streaming " + "or by calling `.read()` before starting to stream." + ) + super().__init__(message) + + +class ResponseContextManager(Generic[_APIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, request_func: Callable[[], _APIResponseT]) -> None: + self._request_func = request_func + self.__response: _APIResponseT | None = None + + def __enter__(self) -> _APIResponseT: + self.__response = self._request_func() + return self.__response + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + self.__response.close() + + +class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None: + self._api_request = api_request + self.__response: _AsyncAPIResponseT | None = None + + async def __aenter__(self) -> _AsyncAPIResponseT: + self.__response = await self._api_request + return self.__response + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + await self.__response.close() + + +def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request)) + + return wrapped + + +def async_to_streamed_response_wrapper( + func: Callable[P, Awaitable[R]], +) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) + + return wrapped + + +def to_custom_streamed_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, ResponseContextManager[_APIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request)) + + return wrapped + + +def async_to_custom_streamed_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) + + return wrapped + + +def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(APIResponse[R], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(AsyncAPIResponse[R], await func(*args, **kwargs)) + + return wrapped + + +def to_custom_raw_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, _APIResponseT]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(_APIResponseT, func(*args, **kwargs)) + + return wrapped + + +def async_to_custom_raw_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, Awaitable[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs)) + + return wrapped + + +def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: + """Given a type like `APIResponse[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(APIResponse[bytes]): + ... + + extract_response_type(MyResponse) -> bytes + ``` + """ + return extract_type_var_from_base( + typ, + generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), + index=0, + ) diff --git a/src/gradient/_streaming.py b/src/gradient/_streaming.py new file mode 100644 index 00000000..df2a5870 --- /dev/null +++ b/src/gradient/_streaming.py @@ -0,0 +1,368 @@ +# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py +from __future__ import annotations + +import json +import inspect +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable + +import httpx + +from ._utils import is_mapping, extract_type_var_from_base +from ._exceptions import APIError + +if TYPE_CHECKING: + from ._client import Gradient, AsyncGradient + + +_T = TypeVar("_T") + + +class Stream(Generic[_T]): + """Provides the core interface to iterate over a synchronous stream response.""" + + response: httpx.Response + + _decoder: SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: Gradient, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + def __next__(self) -> _T: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[_T]: + for item in self._iterator: + yield item + + def _iter_events(self) -> Iterator[ServerSentEvent]: + yield from self._decoder.iter_bytes(self.response.iter_bytes()) + + def __stream__(self) -> Iterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + for sse in iterator: + if sse.data.startswith("[DONE]"): + break + + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) + + # As we might not fully consume the response stream, we need to close it explicitly + response.close() + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.response.close() + + +class AsyncStream(Generic[_T]): + """Provides the core interface to iterate over an asynchronous stream response.""" + + response: httpx.Response + + _decoder: SSEDecoder | SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: AsyncGradient, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + async def __anext__(self) -> _T: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[_T]: + async for item in self._iterator: + yield item + + async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): + yield sse + + async def __stream__(self) -> AsyncIterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + async for sse in iterator: + if sse.data.startswith("[DONE]"): + break + + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) + + # As we might not fully consume the response stream, we need to close it explicitly + await response.aclose() + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.response.aclose() + + +class ServerSentEvent: + def __init__( + self, + *, + event: str | None = None, + data: str | None = None, + id: str | None = None, + retry: int | None = None, + ) -> None: + if data is None: + data = "" + + self._id = id + self._data = data + self._event = event or None + self._retry = retry + + @property + def event(self) -> str | None: + return self._event + + @property + def id(self) -> str | None: + return self._id + + @property + def retry(self) -> int | None: + return self._retry + + @property + def data(self) -> str: + return self._data + + def json(self) -> Any: + return json.loads(self.data) + + @override + def __repr__(self) -> str: + return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" + + +class SSEDecoder: + _data: list[str] + _event: str | None + _retry: int | None + _last_event_id: str | None + + def __init__(self) -> None: + self._event = None + self._data = [] + self._last_event_id = None + self._retry = None + + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + for chunk in self._iter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + async for chunk in self._aiter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + async for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + def decode(self, line: str) -> ServerSentEvent | None: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = None + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None + + +@runtime_checkable +class SSEBytesDecoder(Protocol): + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + +def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: + """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" + origin = get_origin(typ) or typ + return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) + + +def extract_stream_chunk_type( + stream_cls: type, + *, + failure_message: str | None = None, +) -> type: + """Given a type like `Stream[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyStream(Stream[bytes]): + ... + + extract_stream_chunk_type(MyStream) -> bytes + ``` + """ + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + failure_message=failure_message, + ) diff --git a/src/gradient/_types.py b/src/gradient/_types.py new file mode 100644 index 00000000..11a40997 --- /dev/null +++ b/src/gradient/_types.py @@ -0,0 +1,260 @@ +from __future__ import annotations + +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + TypeVar, + Callable, + Iterator, + Optional, + Sequence, +) +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) + +import httpx +import pydantic +from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport + +if TYPE_CHECKING: + from ._models import BaseModel + from ._response import APIResponse, AsyncAPIResponse + +Transport = BaseTransport +AsyncTransport = AsyncBaseTransport +Query = Mapping[str, object] +Body = object +AnyMapping = Mapping[str, object] +ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) +_T = TypeVar("_T") + + +# Approximates httpx internal ProxiesTypes and RequestFiles types +# while adding support for `PathLike` instances +ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] +ProxiesTypes = Union[str, Proxy, ProxiesDict] +if TYPE_CHECKING: + Base64FileInput = Union[IO[bytes], PathLike[str]] + FileContent = Union[IO[bytes], bytes, PathLike[str]] +else: + Base64FileInput = Union[IO[bytes], PathLike] + FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +# duplicate of the above but without our custom file support +HttpxFileContent = Union[IO[bytes], bytes] +HttpxFileTypes = Union[ + # file (or bytes) + HttpxFileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], HttpxFileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], HttpxFileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], +] +HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] + +# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT +# where ResponseT includes `None`. In order to support directly +# passing `None`, overloads would have to be defined for every +# method that uses `ResponseT` which would lead to an unacceptable +# amount of code duplication and make it unreadable. See _base_client.py +# for example usage. +# +# This unfortunately means that you will either have +# to import this type and pass it explicitly: +# +# from gradient import NoneType +# client.get('/foo', cast_to=NoneType) +# +# or build it yourself: +# +# client.get('/foo', cast_to=type(None)) +if TYPE_CHECKING: + NoneType: Type[None] +else: + NoneType = type(None) + + +class RequestOptions(TypedDict, total=False): + headers: Headers + max_retries: int + timeout: float | Timeout | None + params: Query + extra_json: AnyMapping + idempotency_key: str + follow_redirects: bool + + +# Sentinel class used until PEP 0661 is accepted +class NotGiven: + """ + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. + + For example: + + ```py + def create(timeout: Timeout | None | NotGiven = not_given): ... + + + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + @override + def __repr__(self) -> str: + return "NOT_GIVEN" + + +not_given = NotGiven() +# for backwards compatibility: +NOT_GIVEN = NotGiven() + + +class Omit: + """ + To explicitly omit something from being sent in a request, use `omit`. + + ```py + # as the default `Content-Type` header is `application/json` that will be sent + client.post("/upload/files", files={"file": b"my raw file content"}) + + # you can't explicitly override the header as it has to be dynamically generated + # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' + client.post(..., headers={"Content-Type": "multipart/form-data"}) + + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + +omit = Omit() + + +@runtime_checkable +class ModelBuilderProtocol(Protocol): + @classmethod + def build( + cls: type[_T], + *, + response: Response, + data: object, + ) -> _T: ... + + +Headers = Mapping[str, Union[str, Omit]] + + +class HeadersLikeProtocol(Protocol): + def get(self, __key: str) -> str | None: ... + + +HeadersLike = Union[Headers, HeadersLikeProtocol] + +ResponseT = TypeVar( + "ResponseT", + bound=Union[ + object, + str, + None, + "BaseModel", + List[Any], + Dict[str, Any], + Response, + ModelBuilderProtocol, + "APIResponse[Any]", + "AsyncAPIResponse[Any]", + ], +) + +StrBytesIntFloat = Union[str, bytes, int, float] + +# Note: copied from Pydantic +# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 +IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] + +PostParser = Callable[[Any], Any] + + +@runtime_checkable +class InheritsGeneric(Protocol): + """Represents a type that has inherited from `Generic` + + The `__orig_bases__` property can be used to determine the resolved + type variable for a given base class. + """ + + __orig_bases__: tuple[_GenericAlias] + + +class _GenericAlias(Protocol): + __origin__: type[object] + + +class HttpxSendArgs(TypedDict, total=False): + auth: httpx.Auth + follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... + def count(self, value: Any, /) -> int: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/src/gradient/_utils/__init__.py b/src/gradient/_utils/__init__.py new file mode 100644 index 00000000..dc64e29a --- /dev/null +++ b/src/gradient/_utils/__init__.py @@ -0,0 +1,64 @@ +from ._sync import asyncify as asyncify +from ._proxy import LazyProxy as LazyProxy +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + json_safe as json_safe, + lru_cache as lru_cache, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + is_iterable as is_iterable, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + strip_not_given as strip_not_given, + deepcopy_minimal as deepcopy_minimal, + get_async_library as get_async_library, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, +) +from ._compat import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, +) +from ._typing import ( + is_list_type as is_list_type, + is_union_type as is_union_type, + extract_type_arg as extract_type_arg, + is_iterable_type as is_iterable_type, + is_required_type as is_required_type, + is_sequence_type as is_sequence_type, + is_annotated_type as is_annotated_type, + is_type_alias_type as is_type_alias_type, + strip_annotated_type as strip_annotated_type, + extract_type_var_from_base as extract_type_var_from_base, +) +from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + async_transform as async_transform, + maybe_transform as maybe_transform, + async_maybe_transform as async_maybe_transform, +) +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, +) +from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/src/gradient/_utils/_compat.py b/src/gradient/_utils/_compat.py new file mode 100644 index 00000000..dd703233 --- /dev/null +++ b/src/gradient/_utils/_compat.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +import typing_extensions +from typing import Any, Type, Union, Literal, Optional +from datetime import date, datetime +from typing_extensions import get_args as _get_args, get_origin as _get_origin + +from .._types import StrBytesIntFloat +from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime + +_LITERAL_TYPES = {Literal, typing_extensions.Literal} + + +def get_args(tp: type[Any]) -> tuple[Any, ...]: + return _get_args(tp) + + +def get_origin(tp: type[Any]) -> type[Any] | None: + return _get_origin(tp) + + +def is_union(tp: Optional[Type[Any]]) -> bool: + if sys.version_info < (3, 10): + return tp is Union # type: ignore[comparison-overlap] + else: + import types + + return tp is Union or tp is types.UnionType + + +def is_typeddict(tp: Type[Any]) -> bool: + return typing_extensions.is_typeddict(tp) + + +def is_literal_type(tp: Type[Any]) -> bool: + return get_origin(tp) in _LITERAL_TYPES + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + return _parse_date(value) + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + return _parse_datetime(value) diff --git a/src/gradient/_utils/_datetime_parse.py b/src/gradient/_utils/_datetime_parse.py new file mode 100644 index 00000000..7cb9d9e6 --- /dev/null +++ b/src/gradient/_utils/_datetime_parse.py @@ -0,0 +1,136 @@ +""" +This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py +without the Pydantic v1 specific errors. +""" + +from __future__ import annotations + +import re +from typing import Dict, Union, Optional +from datetime import date, datetime, timezone, timedelta + +from .._types import StrBytesIntFloat + +date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" +time_expr = ( + r"(?P\d{1,2}):(?P\d{1,2})" + r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" + r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" +) + +date_re = re.compile(f"{date_expr}$") +datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") + + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) + + +def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None + + +def _from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: + if value == "Z": + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == "-": + offset = -offset + return timezone(timedelta(minutes=offset)) + else: + return None + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = _get_numeric(value, "datetime") + if number is not None: + return _from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + + match = datetime_re.match(value) + if match is None: + raise ValueError("invalid datetime format") + + kw = match.groupdict() + if kw["microsecond"]: + kw["microsecond"] = kw["microsecond"].ljust(6, "0") + + tzinfo = _parse_timezone(kw.pop("tzinfo")) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_["tzinfo"] = tzinfo + + return datetime(**kw_) # type: ignore + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = _get_numeric(value, "date") + if number is not None: + return _from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + match = date_re.match(value) + if match is None: + raise ValueError("invalid date format") + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise ValueError("invalid date format") from None diff --git a/src/gradient/_utils/_logs.py b/src/gradient/_utils/_logs.py new file mode 100644 index 00000000..a60da7f9 --- /dev/null +++ b/src/gradient/_utils/_logs.py @@ -0,0 +1,25 @@ +import os +import logging + +logger: logging.Logger = logging.getLogger("gradient") +httpx_logger: logging.Logger = logging.getLogger("httpx") + + +def _basic_config() -> None: + # e.g. [2023-10-05 14:12:26 - gradient._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK" + logging.basicConfig( + format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + +def setup_logging() -> None: + env = os.environ.get("GRADIENT_LOG") + if env == "debug": + _basic_config() + logger.setLevel(logging.DEBUG) + httpx_logger.setLevel(logging.DEBUG) + elif env == "info": + _basic_config() + logger.setLevel(logging.INFO) + httpx_logger.setLevel(logging.INFO) diff --git a/src/gradient/_utils/_proxy.py b/src/gradient/_utils/_proxy.py new file mode 100644 index 00000000..0f239a33 --- /dev/null +++ b/src/gradient/_utils/_proxy.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Generic, TypeVar, Iterable, cast +from typing_extensions import override + +T = TypeVar("T") + + +class LazyProxy(Generic[T], ABC): + """Implements data methods to pretend that an instance is another instance. + + This includes forwarding attribute access and other methods. + """ + + # Note: we have to special case proxies that themselves return proxies + # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` + + def __getattr__(self, attr: str) -> object: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied # pyright: ignore + return getattr(proxied, attr) + + @override + def __repr__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return repr(self.__get_proxied__()) + + @override + def __str__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return str(proxied) + + @override + def __dir__(self) -> Iterable[str]: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return [] + return proxied.__dir__() + + @property # type: ignore + @override + def __class__(self) -> type: # pyright: ignore + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) + if issubclass(type(proxied), LazyProxy): + return type(proxied) + return proxied.__class__ + + def __get_proxied__(self) -> T: + return self.__load__() + + def __as_proxied__(self) -> T: + """Helper method that returns the current proxy, typed as the loaded object""" + return cast(T, self) + + @abstractmethod + def __load__(self) -> T: ... diff --git a/src/gradient/_utils/_reflection.py b/src/gradient/_utils/_reflection.py new file mode 100644 index 00000000..89aa712a --- /dev/null +++ b/src/gradient/_utils/_reflection.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import inspect +from typing import Any, Callable + + +def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: + """Returns whether or not the given function has a specific parameter""" + sig = inspect.signature(func) + return arg_name in sig.parameters + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), +) -> None: + """Ensure that the signature of the second function matches the first.""" + + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, source_param in source_sig.parameters.items(): + if name in exclude_params: + continue + + custom_param = check_sig.parameters.get(name) + if not custom_param: + errors.append(f"the `{name}` param is missing") + continue + + if custom_param.annotation != source_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" + ) + continue + + if errors: + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) diff --git a/src/gradient/_utils/_resources_proxy.py b/src/gradient/_utils/_resources_proxy.py new file mode 100644 index 00000000..bf3e570d --- /dev/null +++ b/src/gradient/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `gradient.resources` module. + + This is used so that we can lazily import `gradient.resources` only when + needed *and* so that users can just import `gradient` and reference `gradient.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("gradient.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() diff --git a/src/gradient/_utils/_streams.py b/src/gradient/_utils/_streams.py new file mode 100644 index 00000000..f4a0208f --- /dev/null +++ b/src/gradient/_utils/_streams.py @@ -0,0 +1,12 @@ +from typing import Any +from typing_extensions import Iterator, AsyncIterator + + +def consume_sync_iterator(iterator: Iterator[Any]) -> None: + for _ in iterator: + ... + + +async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None: + async for _ in iterator: + ... diff --git a/src/gradient/_utils/_sync.py b/src/gradient/_utils/_sync.py new file mode 100644 index 00000000..f6027c18 --- /dev/null +++ b/src/gradient/_utils/_sync.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +import asyncio +import functools +from typing import TypeVar, Callable, Awaitable +from typing_extensions import ParamSpec + +import anyio +import sniffio +import anyio.to_thread + +T_Retval = TypeVar("T_Retval") +T_ParamSpec = ParamSpec("T_ParamSpec") + + +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await asyncio.to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + +# inspired by `asyncer`, https://github.com/tiangolo/asyncer +def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: + """ + Take a blocking function and create an async one that receives the same + positional and keyword arguments. + + Usage: + + ```python + def blocking_func(arg1, arg2, kwarg1=None): + # blocking code + return result + + + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) + ``` + + ## Arguments + + `function`: a blocking regular callable (e.g. a function) + + ## Return + + An async function that takes the same positional and keyword arguments as the + original one, that when called runs the same original function in a thread worker + and returns the result. + """ + + async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: + return await to_thread(function, *args, **kwargs) + + return wrapper diff --git a/src/gradient/_utils/_transform.py b/src/gradient/_utils/_transform.py new file mode 100644 index 00000000..52075492 --- /dev/null +++ b/src/gradient/_utils/_transform.py @@ -0,0 +1,457 @@ +from __future__ import annotations + +import io +import base64 +import pathlib +from typing import Any, Mapping, TypeVar, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints + +import anyio +import pydantic + +from ._utils import ( + is_list, + is_given, + lru_cache, + is_mapping, + is_iterable, + is_sequence, +) +from .._files import is_base64_file_input +from ._compat import get_origin, is_typeddict +from ._typing import ( + is_list_type, + is_union_type, + extract_type_arg, + is_iterable_type, + is_required_type, + is_sequence_type, + is_annotated_type, + strip_annotated_type, +) + +_T = TypeVar("_T") + + +# TODO: support for drilling globals() and locals() +# TODO: ensure works correctly with forward references in all cases + + +PropertyFormat = Literal["iso8601", "base64", "custom"] + + +class PropertyInfo: + """Metadata class to be used in Annotated types to provide information about a given type. + + For example: + + class MyParams(TypedDict): + account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] + + This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. + """ + + alias: str | None + format: PropertyFormat | None + format_template: str | None + discriminator: str | None + + def __init__( + self, + *, + alias: str | None = None, + format: PropertyFormat | None = None, + format_template: str | None = None, + discriminator: str | None = None, + ) -> None: + self.alias = alias + self.format = format + self.format_template = format_template + self.discriminator = discriminator + + @override + def __repr__(self) -> str: + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" + + +def maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `transform()` that allows `None` to be passed. + + See `transform()` for more details. + """ + if data is None: + return None + return transform(data, expected_type) + + +# Wrapper over _transform_recursive providing fake types +def transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = _transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +@lru_cache(maxsize=8096) +def _get_annotated_type(type_: type) -> type | None: + """If the given type is an `Annotated` type then it is returned, if not `None` is returned. + + This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` + """ + if is_required_type(type_): + # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` + type_ = get_args(type_)[0] + + if is_annotated_type(type_): + return type_ + + return None + + +def _maybe_transform_key(key: str, type_: type) -> str: + """Transform the given `data` based on the annotations provided in `type_`. + + Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. + """ + annotated_type = _get_annotated_type(type_) + if annotated_type is None: + # no `Annotated` definition for this type, no transformation needed + return key + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.alias is not None: + return annotation.alias + + return key + + +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + +def _transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + from .._compat import model_dump + + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return _transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = _transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return _format_data(data, annotation.format, annotation.format_template) + + return data + + +def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = data.read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +def _transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) + return result + + +async def async_maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `async_transform()` that allows `None` to be passed. + + See `async_transform()` for more details. + """ + if data is None: + return None + return await async_transform(data, expected_type) + + +async def async_transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +async def _async_transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + from .._compat import model_dump + + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return await _async_transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return await _async_format_data(data, annotation.format, annotation.format_template) + + return data + + +async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = await anyio.Path(data).read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +async def _async_transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) + return result + + +@lru_cache(maxsize=8096) +def get_type_hints( + obj: Any, + globalns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, + include_extras: bool = False, +) -> dict[str, Any]: + return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) diff --git a/src/gradient/_utils/_typing.py b/src/gradient/_utils/_typing.py new file mode 100644 index 00000000..193109f3 --- /dev/null +++ b/src/gradient/_utils/_typing.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import sys +import typing +import typing_extensions +from typing import Any, TypeVar, Iterable, cast +from collections import abc as _c_abc +from typing_extensions import ( + TypeIs, + Required, + Annotated, + get_args, + get_origin, +) + +from ._utils import lru_cache +from .._types import InheritsGeneric +from ._compat import is_union as _is_union + + +def is_annotated_type(typ: type) -> bool: + return get_origin(typ) == Annotated + + +def is_list_type(typ: type) -> bool: + return (get_origin(typ) or typ) == list + + +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + +def is_iterable_type(typ: type) -> bool: + """If the given type is `typing.Iterable[T]`""" + origin = get_origin(typ) or typ + return origin == Iterable or origin == _c_abc.Iterable + + +def is_union_type(typ: type) -> bool: + return _is_union(get_origin(typ)) + + +def is_required_type(typ: type) -> bool: + return get_origin(typ) == Required + + +def is_typevar(typ: type) -> bool: + # type ignore is required because type checkers + # think this expression will always return False + return type(typ) == TypeVar # type: ignore + + +_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) +if sys.version_info >= (3, 12): + _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) + + +def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: + """Return whether the provided argument is an instance of `TypeAliasType`. + + ```python + type Int = int + is_type_alias_type(Int) + # > True + Str = TypeAliasType("Str", str) + is_type_alias_type(Str) + # > True + ``` + """ + return isinstance(tp, _TYPE_ALIAS_TYPES) + + +# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +@lru_cache(maxsize=8096) +def strip_annotated_type(typ: type) -> type: + if is_required_type(typ) or is_annotated_type(typ): + return strip_annotated_type(cast(type, get_args(typ)[0])) + + return typ + + +def extract_type_arg(typ: type, index: int) -> type: + args = get_args(typ) + try: + return cast(type, args[index]) + except IndexError as err: + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err + + +def extract_type_var_from_base( + typ: type, + *, + generic_bases: tuple[type, ...], + index: int, + failure_message: str | None = None, +) -> type: + """Given a type like `Foo[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(Foo[bytes]): + ... + + extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes + ``` + + And where a generic subclass is given: + ```py + _T = TypeVar('_T') + class MyResponse(Foo[_T]): + ... + + extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes + ``` + """ + cls = cast(object, get_origin(typ) or typ) + if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] + # we're given the class directly + return extract_type_arg(typ, index) + + # if a subclass is given + # --- + # this is needed as __orig_bases__ is not present in the typeshed stubs + # because it is intended to be for internal use only, however there does + # not seem to be a way to resolve generic TypeVars for inherited subclasses + # without using it. + if isinstance(cls, InheritsGeneric): + target_base_class: Any | None = None + for base in cls.__orig_bases__: + if base.__origin__ in generic_bases: + target_base_class = base + break + + if target_base_class is None: + raise RuntimeError( + "Could not find the generic base class;\n" + "This should never happen;\n" + f"Does {cls} inherit from one of {generic_bases} ?" + ) + + extracted = extract_type_arg(target_base_class, index) + if is_typevar(extracted): + # If the extracted type argument is itself a type variable + # then that means the subclass itself is generic, so we have + # to resolve the type argument from the class itself, not + # the base class. + # + # Note: if there is more than 1 type argument, the subclass could + # change the ordering of the type arguments, this is not currently + # supported. + return extract_type_arg(typ, index) + + return extracted + + raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/src/gradient/_utils/_utils.py b/src/gradient/_utils/_utils.py new file mode 100644 index 00000000..eec7f4a1 --- /dev/null +++ b/src/gradient/_utils/_utils.py @@ -0,0 +1,421 @@ +from __future__ import annotations + +import os +import re +import inspect +import functools +from typing import ( + Any, + Tuple, + Mapping, + TypeVar, + Callable, + Iterable, + Sequence, + cast, + overload, +) +from pathlib import Path +from datetime import date, datetime +from typing_extensions import TypeGuard + +import sniffio + +from .._types import Omit, NotGiven, FileTypes, HeadersLike + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) +_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) +_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) +CallableT = TypeVar("CallableT", bound=Callable[..., Any]) + + +def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: + return [item for sublist in t for item in sublist] + + +def extract_files( + # TODO: this needs to take Dict but variance issues..... + # create protocol type ? + query: Mapping[str, object], + *, + paths: Sequence[Sequence[str]], +) -> list[tuple[str, FileTypes]]: + """Recursively extract files from the given dictionary based on specified paths. + + A path may look like this ['foo', 'files', '', 'data']. + + Note: this mutates the given dictionary. + """ + files: list[tuple[str, FileTypes]] = [] + for path in paths: + files.extend(_extract_items(query, path, index=0, flattened_key=None)) + return files + + +def _extract_items( + obj: object, + path: Sequence[str], + *, + index: int, + flattened_key: str | None, +) -> list[tuple[str, FileTypes]]: + try: + key = path[index] + except IndexError: + if not is_given(obj): + # no value was provided - we can safely ignore + return [] + + # cyclical import + from .._files import assert_is_file_content + + # We have exhausted the path, return the entry we found. + assert flattened_key is not None + + if is_list(obj): + files: list[tuple[str, FileTypes]] = [] + for entry in obj: + assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") + files.append((flattened_key + "[]", cast(FileTypes, entry))) + return files + + assert_is_file_content(obj, key=flattened_key) + return [(flattened_key, cast(FileTypes, obj))] + + index += 1 + if is_dict(obj): + try: + # We are at the last entry in the path so we must remove the field + if (len(path)) == index: + item = obj.pop(key) + else: + item = obj[key] + except KeyError: + # Key was not present in the dictionary, this is not indicative of an error + # as the given path may not point to a required field. We also do not want + # to enforce required fields as the API may differ from the spec in some cases. + return [] + if flattened_key is None: + flattened_key = key + else: + flattened_key += f"[{key}]" + return _extract_items( + item, + path, + index=index, + flattened_key=flattened_key, + ) + elif is_list(obj): + if key != "": + return [] + + return flatten( + [ + _extract_items( + item, + path, + index=index, + flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + ) + for item in obj + ] + ) + + # Something unexpected was passed, just ignore it. + return [] + + +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) + + +# Type safe methods for narrowing types with TypeVars. +# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], +# however this cause Pyright to rightfully report errors. As we know we don't +# care about the contained types we can safely use `object` in its place. +# +# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. +# `is_*` is for when you're dealing with an unknown input +# `is_*_t` is for when you're narrowing a known union type to a specific subset + + +def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: + return isinstance(obj, tuple) + + +def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: + return isinstance(obj, tuple) + + +def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: + return isinstance(obj, Sequence) + + +def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: + return isinstance(obj, Sequence) + + +def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: + return isinstance(obj, Mapping) + + +def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: + return isinstance(obj, Mapping) + + +def is_dict(obj: object) -> TypeGuard[dict[object, object]]: + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: + return isinstance(obj, Iterable) + + +def deepcopy_minimal(item: _T) -> _T: + """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: + + - mappings, e.g. `dict` + - list + + This is done for performance reasons. + """ + if is_mapping(item): + return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) + if is_list(item): + return cast(_T, [deepcopy_minimal(entry) for entry in item]) + return item + + +# copied from https://github.com/Rapptz/RoboDanny +def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: + size = len(seq) + if size == 0: + return "" + + if size == 1: + return seq[0] + + if size == 2: + return f"{seq[0]} {final} {seq[1]}" + + return delim.join(seq[:-1]) + f" {final} {seq[-1]}" + + +def quote(string: str) -> str: + """Add single quotation marks around the given string. Does *not* do any escaping.""" + return f"'{string}'" + + +def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: + """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. + + Useful for enforcing runtime validation of overloaded functions. + + Example usage: + ```py + @overload + def foo(*, a: str) -> str: ... + + + @overload + def foo(*, b: bool) -> str: ... + + + # This enforces the same constraints that a static type checker would + # i.e. that either a or b must be passed to the function + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: bool | None = None) -> str: ... + ``` + """ + + def inner(func: CallableT) -> CallableT: + params = inspect.signature(func).parameters + positional = [ + name + for name, param in params.items() + if param.kind + in { + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + } + ] + + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + given_params: set[str] = set() + for i, _ in enumerate(args): + try: + given_params.add(positional[i]) + except IndexError: + raise TypeError( + f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" + ) from None + + for key in kwargs.keys(): + given_params.add(key) + + for variant in variants: + matches = all((param in given_params for param in variant)) + if matches: + break + else: # no break + if len(variants) > 1: + variations = human_join( + ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] + ) + msg = f"Missing required arguments; Expected either {variations} arguments to be given" + else: + assert len(variants) > 0 + + # TODO: this error message is not deterministic + missing = list(set(variants[0]) - given_params) + if len(missing) > 1: + msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" + else: + msg = f"Missing required argument: {quote(missing[0])}" + raise TypeError(msg) + return func(*args, **kwargs) + + return wrapper # type: ignore + + return inner + + +_K = TypeVar("_K") +_V = TypeVar("_V") + + +@overload +def strip_not_given(obj: None) -> None: ... + + +@overload +def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... + + +@overload +def strip_not_given(obj: object) -> object: ... + + +def strip_not_given(obj: object | None) -> object: + """Remove all top-level keys where their values are instances of `NotGiven`""" + if obj is None: + return None + + if not is_mapping(obj): + return obj + + return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} + + +def coerce_integer(val: str) -> int: + return int(val, base=10) + + +def coerce_float(val: str) -> float: + return float(val) + + +def coerce_boolean(val: str) -> bool: + return val == "true" or val == "1" or val == "on" + + +def maybe_coerce_integer(val: str | None) -> int | None: + if val is None: + return None + return coerce_integer(val) + + +def maybe_coerce_float(val: str | None) -> float | None: + if val is None: + return None + return coerce_float(val) + + +def maybe_coerce_boolean(val: str | None) -> bool | None: + if val is None: + return None + return coerce_boolean(val) + + +def removeprefix(string: str, prefix: str) -> str: + """Remove a prefix from a string. + + Backport of `str.removeprefix` for Python < 3.9 + """ + if string.startswith(prefix): + return string[len(prefix) :] + return string + + +def removesuffix(string: str, suffix: str) -> str: + """Remove a suffix from a string. + + Backport of `str.removesuffix` for Python < 3.9 + """ + if string.endswith(suffix): + return string[: -len(suffix)] + return string + + +def file_from_path(path: str) -> FileTypes: + contents = Path(path).read_bytes() + file_name = os.path.basename(path) + return (file_name, contents) + + +def get_required_header(headers: HeadersLike, header: str) -> str: + lower_header = header.lower() + if is_mapping_t(headers): + # mypy doesn't understand the type narrowing here + for k, v in headers.items(): # type: ignore + if k.lower() == lower_header and isinstance(v, str): + return v + + # to deal with the case where the header looks like Stainless-Event-Id + intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) + + for normalized_header in [header, lower_header, header.upper(), intercaps_header]: + value = headers.get(normalized_header) + if value: + return value + + raise ValueError(f"Could not find {header} header") + + +def get_async_library() -> str: + try: + return sniffio.current_async_library() + except Exception: + return "false" + + +def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: + """A version of functools.lru_cache that retains the type signature + for the wrapped function arguments. + """ + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) + return cast(Any, wrapper) # type: ignore[no-any-return] + + +def json_safe(data: object) -> object: + """Translates a mapping / sequence recursively in the same fashion + as `pydantic` v2's `model_dump(mode="json")`. + """ + if is_mapping(data): + return {json_safe(key): json_safe(value) for key, value in data.items()} + + if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): + return [json_safe(item) for item in data] + + if isinstance(data, (datetime, date)): + return data.isoformat() + + return data diff --git a/src/gradient/_version.py b/src/gradient/_version.py new file mode 100644 index 00000000..defad636 --- /dev/null +++ b/src/gradient/_version.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +__title__ = "gradient" +__version__ = "3.8.0" # x-release-please-version diff --git a/src/gradient/lib/.keep b/src/gradient/lib/.keep new file mode 100644 index 00000000..5e2c99fd --- /dev/null +++ b/src/gradient/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/gradient/py.typed b/src/gradient/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/src/gradient/resources/__init__.py b/src/gradient/resources/__init__.py new file mode 100644 index 00000000..a797b18f --- /dev/null +++ b/src/gradient/resources/__init__.py @@ -0,0 +1,145 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .nfs import ( + NfsResource, + AsyncNfsResource, + NfsResourceWithRawResponse, + AsyncNfsResourceWithRawResponse, + NfsResourceWithStreamingResponse, + AsyncNfsResourceWithStreamingResponse, +) +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .agents import ( + AgentsResource, + AsyncAgentsResource, + AgentsResourceWithRawResponse, + AsyncAgentsResourceWithRawResponse, + AgentsResourceWithStreamingResponse, + AsyncAgentsResourceWithStreamingResponse, +) +from .images import ( + ImagesResource, + AsyncImagesResource, + ImagesResourceWithRawResponse, + AsyncImagesResourceWithRawResponse, + ImagesResourceWithStreamingResponse, + AsyncImagesResourceWithStreamingResponse, +) +from .models import ( + ModelsResource, + AsyncModelsResource, + ModelsResourceWithRawResponse, + AsyncModelsResourceWithRawResponse, + ModelsResourceWithStreamingResponse, + AsyncModelsResourceWithStreamingResponse, +) +from .regions import ( + RegionsResource, + AsyncRegionsResource, + RegionsResourceWithRawResponse, + AsyncRegionsResourceWithRawResponse, + RegionsResourceWithStreamingResponse, + AsyncRegionsResourceWithStreamingResponse, +) +from .databases import ( + DatabasesResource, + AsyncDatabasesResource, + DatabasesResourceWithRawResponse, + AsyncDatabasesResourceWithRawResponse, + DatabasesResourceWithStreamingResponse, + AsyncDatabasesResourceWithStreamingResponse, +) +from .inference import ( + InferenceResource, + AsyncInferenceResource, + InferenceResourceWithRawResponse, + AsyncInferenceResourceWithRawResponse, + InferenceResourceWithStreamingResponse, + AsyncInferenceResourceWithStreamingResponse, +) +from .gpu_droplets import ( + GPUDropletsResource, + AsyncGPUDropletsResource, + GPUDropletsResourceWithRawResponse, + AsyncGPUDropletsResourceWithRawResponse, + GPUDropletsResourceWithStreamingResponse, + AsyncGPUDropletsResourceWithStreamingResponse, +) +from .knowledge_bases import ( + KnowledgeBasesResource, + AsyncKnowledgeBasesResource, + KnowledgeBasesResourceWithRawResponse, + AsyncKnowledgeBasesResourceWithRawResponse, + KnowledgeBasesResourceWithStreamingResponse, + AsyncKnowledgeBasesResourceWithStreamingResponse, +) + +__all__ = [ + "AgentsResource", + "AsyncAgentsResource", + "AgentsResourceWithRawResponse", + "AsyncAgentsResourceWithRawResponse", + "AgentsResourceWithStreamingResponse", + "AsyncAgentsResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", + "ImagesResource", + "AsyncImagesResource", + "ImagesResourceWithRawResponse", + "AsyncImagesResourceWithRawResponse", + "ImagesResourceWithStreamingResponse", + "AsyncImagesResourceWithStreamingResponse", + "GPUDropletsResource", + "AsyncGPUDropletsResource", + "GPUDropletsResourceWithRawResponse", + "AsyncGPUDropletsResourceWithRawResponse", + "GPUDropletsResourceWithStreamingResponse", + "AsyncGPUDropletsResourceWithStreamingResponse", + "InferenceResource", + "AsyncInferenceResource", + "InferenceResourceWithRawResponse", + "AsyncInferenceResourceWithRawResponse", + "InferenceResourceWithStreamingResponse", + "AsyncInferenceResourceWithStreamingResponse", + "KnowledgeBasesResource", + "AsyncKnowledgeBasesResource", + "KnowledgeBasesResourceWithRawResponse", + "AsyncKnowledgeBasesResourceWithRawResponse", + "KnowledgeBasesResourceWithStreamingResponse", + "AsyncKnowledgeBasesResourceWithStreamingResponse", + "ModelsResource", + "AsyncModelsResource", + "ModelsResourceWithRawResponse", + "AsyncModelsResourceWithRawResponse", + "ModelsResourceWithStreamingResponse", + "AsyncModelsResourceWithStreamingResponse", + "RegionsResource", + "AsyncRegionsResource", + "RegionsResourceWithRawResponse", + "AsyncRegionsResourceWithRawResponse", + "RegionsResourceWithStreamingResponse", + "AsyncRegionsResourceWithStreamingResponse", + "DatabasesResource", + "AsyncDatabasesResource", + "DatabasesResourceWithRawResponse", + "AsyncDatabasesResourceWithRawResponse", + "DatabasesResourceWithStreamingResponse", + "AsyncDatabasesResourceWithStreamingResponse", + "NfsResource", + "AsyncNfsResource", + "NfsResourceWithRawResponse", + "AsyncNfsResourceWithRawResponse", + "NfsResourceWithStreamingResponse", + "AsyncNfsResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/agents/__init__.py b/src/gradient/resources/agents/__init__.py new file mode 100644 index 00000000..51075283 --- /dev/null +++ b/src/gradient/resources/agents/__init__.py @@ -0,0 +1,159 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .agents import ( + AgentsResource, + AsyncAgentsResource, + AgentsResourceWithRawResponse, + AsyncAgentsResourceWithRawResponse, + AgentsResourceWithStreamingResponse, + AsyncAgentsResourceWithStreamingResponse, +) +from .routes import ( + RoutesResource, + AsyncRoutesResource, + RoutesResourceWithRawResponse, + AsyncRoutesResourceWithRawResponse, + RoutesResourceWithStreamingResponse, + AsyncRoutesResourceWithStreamingResponse, +) +from .api_keys import ( + APIKeysResource, + AsyncAPIKeysResource, + APIKeysResourceWithRawResponse, + AsyncAPIKeysResourceWithRawResponse, + APIKeysResourceWithStreamingResponse, + AsyncAPIKeysResourceWithStreamingResponse, +) +from .versions import ( + VersionsResource, + AsyncVersionsResource, + VersionsResourceWithRawResponse, + AsyncVersionsResourceWithRawResponse, + VersionsResourceWithStreamingResponse, + AsyncVersionsResourceWithStreamingResponse, +) +from .functions import ( + FunctionsResource, + AsyncFunctionsResource, + FunctionsResourceWithRawResponse, + AsyncFunctionsResourceWithRawResponse, + FunctionsResourceWithStreamingResponse, + AsyncFunctionsResourceWithStreamingResponse, +) +from .evaluation_runs import ( + EvaluationRunsResource, + AsyncEvaluationRunsResource, + EvaluationRunsResourceWithRawResponse, + AsyncEvaluationRunsResourceWithRawResponse, + EvaluationRunsResourceWithStreamingResponse, + AsyncEvaluationRunsResourceWithStreamingResponse, +) +from .knowledge_bases import ( + KnowledgeBasesResource, + AsyncKnowledgeBasesResource, + KnowledgeBasesResourceWithRawResponse, + AsyncKnowledgeBasesResourceWithRawResponse, + KnowledgeBasesResourceWithStreamingResponse, + AsyncKnowledgeBasesResourceWithStreamingResponse, +) +from .evaluation_metrics import ( + EvaluationMetricsResource, + AsyncEvaluationMetricsResource, + EvaluationMetricsResourceWithRawResponse, + AsyncEvaluationMetricsResourceWithRawResponse, + EvaluationMetricsResourceWithStreamingResponse, + AsyncEvaluationMetricsResourceWithStreamingResponse, +) +from .evaluation_datasets import ( + EvaluationDatasetsResource, + AsyncEvaluationDatasetsResource, + EvaluationDatasetsResourceWithRawResponse, + AsyncEvaluationDatasetsResourceWithRawResponse, + EvaluationDatasetsResourceWithStreamingResponse, + AsyncEvaluationDatasetsResourceWithStreamingResponse, +) +from .evaluation_test_cases import ( + EvaluationTestCasesResource, + AsyncEvaluationTestCasesResource, + EvaluationTestCasesResourceWithRawResponse, + AsyncEvaluationTestCasesResourceWithRawResponse, + EvaluationTestCasesResourceWithStreamingResponse, + AsyncEvaluationTestCasesResourceWithStreamingResponse, +) + +__all__ = [ + "APIKeysResource", + "AsyncAPIKeysResource", + "APIKeysResourceWithRawResponse", + "AsyncAPIKeysResourceWithRawResponse", + "APIKeysResourceWithStreamingResponse", + "AsyncAPIKeysResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", + "EvaluationMetricsResource", + "AsyncEvaluationMetricsResource", + "EvaluationMetricsResourceWithRawResponse", + "AsyncEvaluationMetricsResourceWithRawResponse", + "EvaluationMetricsResourceWithStreamingResponse", + "AsyncEvaluationMetricsResourceWithStreamingResponse", + "EvaluationRunsResource", + "AsyncEvaluationRunsResource", + "EvaluationRunsResourceWithRawResponse", + "AsyncEvaluationRunsResourceWithRawResponse", + "EvaluationRunsResourceWithStreamingResponse", + "AsyncEvaluationRunsResourceWithStreamingResponse", + "EvaluationTestCasesResource", + "AsyncEvaluationTestCasesResource", + "EvaluationTestCasesResourceWithRawResponse", + "AsyncEvaluationTestCasesResourceWithRawResponse", + "EvaluationTestCasesResourceWithStreamingResponse", + "AsyncEvaluationTestCasesResourceWithStreamingResponse", + "EvaluationDatasetsResource", + "AsyncEvaluationDatasetsResource", + "EvaluationDatasetsResourceWithRawResponse", + "AsyncEvaluationDatasetsResourceWithRawResponse", + "EvaluationDatasetsResourceWithStreamingResponse", + "AsyncEvaluationDatasetsResourceWithStreamingResponse", + "FunctionsResource", + "AsyncFunctionsResource", + "FunctionsResourceWithRawResponse", + "AsyncFunctionsResourceWithRawResponse", + "FunctionsResourceWithStreamingResponse", + "AsyncFunctionsResourceWithStreamingResponse", + "VersionsResource", + "AsyncVersionsResource", + "VersionsResourceWithRawResponse", + "AsyncVersionsResourceWithRawResponse", + "VersionsResourceWithStreamingResponse", + "AsyncVersionsResourceWithStreamingResponse", + "KnowledgeBasesResource", + "AsyncKnowledgeBasesResource", + "KnowledgeBasesResourceWithRawResponse", + "AsyncKnowledgeBasesResourceWithRawResponse", + "KnowledgeBasesResourceWithStreamingResponse", + "AsyncKnowledgeBasesResourceWithStreamingResponse", + "RoutesResource", + "AsyncRoutesResource", + "RoutesResourceWithRawResponse", + "AsyncRoutesResourceWithRawResponse", + "RoutesResourceWithStreamingResponse", + "AsyncRoutesResourceWithStreamingResponse", + "AgentsResource", + "AsyncAgentsResource", + "AgentsResourceWithRawResponse", + "AsyncAgentsResourceWithRawResponse", + "AgentsResourceWithStreamingResponse", + "AsyncAgentsResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/agents/agents.py b/src/gradient/resources/agents/agents.py new file mode 100644 index 00000000..332fb672 --- /dev/null +++ b/src/gradient/resources/agents/agents.py @@ -0,0 +1,1407 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .routes import ( + RoutesResource, + AsyncRoutesResource, + RoutesResourceWithRawResponse, + AsyncRoutesResourceWithRawResponse, + RoutesResourceWithStreamingResponse, + AsyncRoutesResourceWithStreamingResponse, +) +from ...types import ( + APIRetrievalMethod, + APIDeploymentVisibility, + agent_list_params, + agent_create_params, + agent_update_params, + agent_update_status_params, + agent_retrieve_usage_params, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from .api_keys import ( + APIKeysResource, + AsyncAPIKeysResource, + APIKeysResourceWithRawResponse, + AsyncAPIKeysResourceWithRawResponse, + APIKeysResourceWithStreamingResponse, + AsyncAPIKeysResourceWithStreamingResponse, +) +from .versions import ( + VersionsResource, + AsyncVersionsResource, + VersionsResourceWithRawResponse, + AsyncVersionsResourceWithRawResponse, + VersionsResourceWithStreamingResponse, + AsyncVersionsResourceWithStreamingResponse, +) +from ..._compat import cached_property +from .chat.chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .functions import ( + FunctionsResource, + AsyncFunctionsResource, + FunctionsResourceWithRawResponse, + AsyncFunctionsResourceWithRawResponse, + FunctionsResourceWithStreamingResponse, + AsyncFunctionsResourceWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from .evaluation_runs import ( + EvaluationRunsResource, + AsyncEvaluationRunsResource, + EvaluationRunsResourceWithRawResponse, + AsyncEvaluationRunsResourceWithRawResponse, + EvaluationRunsResourceWithStreamingResponse, + AsyncEvaluationRunsResourceWithStreamingResponse, +) +from .knowledge_bases import ( + KnowledgeBasesResource, + AsyncKnowledgeBasesResource, + KnowledgeBasesResourceWithRawResponse, + AsyncKnowledgeBasesResourceWithRawResponse, + KnowledgeBasesResourceWithStreamingResponse, + AsyncKnowledgeBasesResourceWithStreamingResponse, +) +from .evaluation_datasets import ( + EvaluationDatasetsResource, + AsyncEvaluationDatasetsResource, + EvaluationDatasetsResourceWithRawResponse, + AsyncEvaluationDatasetsResourceWithRawResponse, + EvaluationDatasetsResourceWithStreamingResponse, + AsyncEvaluationDatasetsResourceWithStreamingResponse, +) +from .evaluation_test_cases import ( + EvaluationTestCasesResource, + AsyncEvaluationTestCasesResource, + EvaluationTestCasesResourceWithRawResponse, + AsyncEvaluationTestCasesResourceWithRawResponse, + EvaluationTestCasesResourceWithStreamingResponse, + AsyncEvaluationTestCasesResourceWithStreamingResponse, +) +from ...types.agent_list_response import AgentListResponse +from ...types.api_retrieval_method import APIRetrievalMethod +from ...types.agent_create_response import AgentCreateResponse +from ...types.agent_delete_response import AgentDeleteResponse +from ...types.agent_update_response import AgentUpdateResponse +from ...types.agent_retrieve_response import AgentRetrieveResponse +from ...types.api_deployment_visibility import APIDeploymentVisibility +from ...types.agent_update_status_response import AgentUpdateStatusResponse +from ...types.agent_retrieve_usage_response import AgentRetrieveUsageResponse +from .evaluation_metrics.evaluation_metrics import ( + EvaluationMetricsResource, + AsyncEvaluationMetricsResource, + EvaluationMetricsResourceWithRawResponse, + AsyncEvaluationMetricsResourceWithRawResponse, + EvaluationMetricsResourceWithStreamingResponse, + AsyncEvaluationMetricsResourceWithStreamingResponse, +) + +__all__ = ["AgentsResource", "AsyncAgentsResource"] + + +class AgentsResource(SyncAPIResource): + @cached_property + def api_keys(self) -> APIKeysResource: + return APIKeysResource(self._client) + + @cached_property + def chat(self) -> ChatResource: + return ChatResource(self._client) + + @cached_property + def evaluation_metrics(self) -> EvaluationMetricsResource: + return EvaluationMetricsResource(self._client) + + @cached_property + def evaluation_runs(self) -> EvaluationRunsResource: + return EvaluationRunsResource(self._client) + + @cached_property + def evaluation_test_cases(self) -> EvaluationTestCasesResource: + return EvaluationTestCasesResource(self._client) + + @cached_property + def evaluation_datasets(self) -> EvaluationDatasetsResource: + return EvaluationDatasetsResource(self._client) + + @cached_property + def functions(self) -> FunctionsResource: + return FunctionsResource(self._client) + + @cached_property + def versions(self) -> VersionsResource: + return VersionsResource(self._client) + + @cached_property + def knowledge_bases(self) -> KnowledgeBasesResource: + return KnowledgeBasesResource(self._client) + + @cached_property + def routes(self) -> RoutesResource: + return RoutesResource(self._client) + + @cached_property + def with_raw_response(self) -> AgentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AgentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AgentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AgentsResourceWithStreamingResponse(self) + + def create( + self, + *, + anthropic_key_uuid: str | Omit = omit, + description: str | Omit = omit, + instruction: str | Omit = omit, + knowledge_base_uuid: SequenceNotStr[str] | Omit = omit, + model_provider_key_uuid: str | Omit = omit, + model_uuid: str | Omit = omit, + name: str | Omit = omit, + openai_key_uuid: str | Omit = omit, + project_id: str | Omit = omit, + region: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + workspace_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentCreateResponse: + """To create a new agent, send a POST request to `/v2/gen-ai/agents`. + + The response + body contains a JSON object with the newly created agent object. + + Args: + anthropic_key_uuid: Optional Anthropic API key ID to use with Anthropic models + + description: A text description of the agent, not used in inference + + instruction: Agent instruction. Instructions help your agent to perform its job effectively. + See + [Write Effective Agent Instructions](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#agent-instructions) + for best practices. + + knowledge_base_uuid: Ids of the knowledge base(s) to attach to the agent + + model_uuid: Identifier for the foundation model. + + name: Agent name + + openai_key_uuid: Optional OpenAI API key ID to use with OpenAI models + + project_id: The id of the DigitalOcean project this agent will belong to + + region: The DigitalOcean region to deploy your agent in + + tags: Agent tag to organize related resources + + workspace_uuid: Identifier for the workspace + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/agents" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/agents", + body=maybe_transform( + { + "anthropic_key_uuid": anthropic_key_uuid, + "description": description, + "instruction": instruction, + "knowledge_base_uuid": knowledge_base_uuid, + "model_provider_key_uuid": model_provider_key_uuid, + "model_uuid": model_uuid, + "name": name, + "openai_key_uuid": openai_key_uuid, + "project_id": project_id, + "region": region, + "tags": tags, + "workspace_uuid": workspace_uuid, + }, + agent_create_params.AgentCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentCreateResponse, + ) + + def retrieve( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentRetrieveResponse: + """To retrieve details of an agent, GET request to `/v2/gen-ai/agents/{uuid}`. + + The + response body is a JSON object containing the agent. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/agents/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentRetrieveResponse, + ) + + def update( + self, + path_uuid: str, + *, + agent_log_insights_enabled: bool | Omit = omit, + allowed_domains: SequenceNotStr[str] | Omit = omit, + anthropic_key_uuid: str | Omit = omit, + conversation_logs_enabled: bool | Omit = omit, + description: str | Omit = omit, + instruction: str | Omit = omit, + k: int | Omit = omit, + max_tokens: int | Omit = omit, + model_provider_key_uuid: str | Omit = omit, + model_uuid: str | Omit = omit, + name: str | Omit = omit, + openai_key_uuid: str | Omit = omit, + project_id: str | Omit = omit, + provide_citations: bool | Omit = omit, + retrieval_method: APIRetrievalMethod | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + temperature: float | Omit = omit, + top_p: float | Omit = omit, + body_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentUpdateResponse: + """To update an agent, send a PUT request to `/v2/gen-ai/agents/{uuid}`. + + The + response body is a JSON object containing the agent. + + Args: + allowed_domains: Optional list of allowed domains for the chatbot - Must use fully qualified + domain name (FQDN) such as https://example.com + + anthropic_key_uuid: Optional anthropic key uuid for use with anthropic models + + conversation_logs_enabled: Optional update of conversation logs enabled + + description: Agent description + + instruction: Agent instruction. Instructions help your agent to perform its job effectively. + See + [Write Effective Agent Instructions](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#agent-instructions) + for best practices. + + k: How many results should be considered from an attached knowledge base + + max_tokens: Specifies the maximum number of tokens the model can process in a single input + or output, set as a number between 1 and 512. This determines the length of each + response. + + model_provider_key_uuid: Optional Model Provider uuid for use with provider models + + model_uuid: Identifier for the foundation model. + + name: Agent name + + openai_key_uuid: Optional OpenAI key uuid for use with OpenAI models + + project_id: The id of the DigitalOcean project this agent will belong to + + retrieval_method: - RETRIEVAL_METHOD_UNKNOWN: The retrieval method is unknown + - RETRIEVAL_METHOD_REWRITE: The retrieval method is rewrite + - RETRIEVAL_METHOD_STEP_BACK: The retrieval method is step back + - RETRIEVAL_METHOD_SUB_QUERIES: The retrieval method is sub queries + - RETRIEVAL_METHOD_NONE: The retrieval method is none + + tags: A set of abitrary tags to organize your agent + + temperature: Controls the model’s creativity, specified as a number between 0 and 1. Lower + values produce more predictable and conservative responses, while higher values + encourage creativity and variation. + + top_p: Defines the cumulative probability threshold for word selection, specified as a + number between 0 and 1. Higher values allow for more diverse outputs, while + lower values ensure focused and coherent responses. + + body_uuid: Unique agent id + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return self._put( + f"/v2/gen-ai/agents/{path_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_uuid}", + body=maybe_transform( + { + "agent_log_insights_enabled": agent_log_insights_enabled, + "allowed_domains": allowed_domains, + "anthropic_key_uuid": anthropic_key_uuid, + "conversation_logs_enabled": conversation_logs_enabled, + "description": description, + "instruction": instruction, + "k": k, + "max_tokens": max_tokens, + "model_provider_key_uuid": model_provider_key_uuid, + "model_uuid": model_uuid, + "name": name, + "openai_key_uuid": openai_key_uuid, + "project_id": project_id, + "provide_citations": provide_citations, + "retrieval_method": retrieval_method, + "tags": tags, + "temperature": temperature, + "top_p": top_p, + "body_uuid": body_uuid, + }, + agent_update_params.AgentUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentUpdateResponse, + ) + + def list( + self, + *, + only_deployed: bool | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentListResponse: + """ + To list all agents, send a GET request to `/v2/gen-ai/agents`. + + Args: + only_deployed: Only list agents that are deployed. + + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/agents" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "only_deployed": only_deployed, + "page": page, + "per_page": per_page, + }, + agent_list_params.AgentListParams, + ), + ), + cast_to=AgentListResponse, + ) + + def delete( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentDeleteResponse: + """ + To delete an agent, send a DELETE request to `/v2/gen-ai/agents/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._delete( + f"/v2/gen-ai/agents/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentDeleteResponse, + ) + + def retrieve_usage( + self, + uuid: str, + *, + start: str | Omit = omit, + stop: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentRetrieveUsageResponse: + """ + To get agent usage, send a GET request to `/v2/gen-ai/agents/{uuid}/usage`. + Returns usage metrics for the specified agent within the provided time range. + + Args: + start: Return all usage data from this date. + + stop: Return all usage data up to this date, if omitted, will return up to the current + date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/agents/{uuid}/usage" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}/usage", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start": start, + "stop": stop, + }, + agent_retrieve_usage_params.AgentRetrieveUsageParams, + ), + ), + cast_to=AgentRetrieveUsageResponse, + ) + + def update_status( + self, + path_uuid: str, + *, + body_uuid: str | Omit = omit, + visibility: APIDeploymentVisibility | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentUpdateStatusResponse: + """Check whether an agent is public or private. + + To update the agent status, send a + PUT request to `/v2/gen-ai/agents/{uuid}/deployment_visibility`. + + Args: + body_uuid: Unique id + + visibility: - VISIBILITY_UNKNOWN: The status of the deployment is unknown + - VISIBILITY_DISABLED: The deployment is disabled and will no longer service + requests + - VISIBILITY_PLAYGROUND: Deprecated: No longer a valid state + - VISIBILITY_PUBLIC: The deployment is public and will service requests from the + public internet + - VISIBILITY_PRIVATE: The deployment is private and will only service requests + from other agents, or through API keys + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return self._put( + f"/v2/gen-ai/agents/{path_uuid}/deployment_visibility" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_uuid}/deployment_visibility", + body=maybe_transform( + { + "body_uuid": body_uuid, + "visibility": visibility, + }, + agent_update_status_params.AgentUpdateStatusParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentUpdateStatusResponse, + ) + + +class AsyncAgentsResource(AsyncAPIResource): + @cached_property + def api_keys(self) -> AsyncAPIKeysResource: + return AsyncAPIKeysResource(self._client) + + @cached_property + def chat(self) -> AsyncChatResource: + return AsyncChatResource(self._client) + + @cached_property + def evaluation_metrics(self) -> AsyncEvaluationMetricsResource: + return AsyncEvaluationMetricsResource(self._client) + + @cached_property + def evaluation_runs(self) -> AsyncEvaluationRunsResource: + return AsyncEvaluationRunsResource(self._client) + + @cached_property + def evaluation_test_cases(self) -> AsyncEvaluationTestCasesResource: + return AsyncEvaluationTestCasesResource(self._client) + + @cached_property + def evaluation_datasets(self) -> AsyncEvaluationDatasetsResource: + return AsyncEvaluationDatasetsResource(self._client) + + @cached_property + def functions(self) -> AsyncFunctionsResource: + return AsyncFunctionsResource(self._client) + + @cached_property + def versions(self) -> AsyncVersionsResource: + return AsyncVersionsResource(self._client) + + @cached_property + def knowledge_bases(self) -> AsyncKnowledgeBasesResource: + return AsyncKnowledgeBasesResource(self._client) + + @cached_property + def routes(self) -> AsyncRoutesResource: + return AsyncRoutesResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAgentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncAgentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAgentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncAgentsResourceWithStreamingResponse(self) + + async def create( + self, + *, + anthropic_key_uuid: str | Omit = omit, + description: str | Omit = omit, + instruction: str | Omit = omit, + knowledge_base_uuid: SequenceNotStr[str] | Omit = omit, + model_provider_key_uuid: str | Omit = omit, + model_uuid: str | Omit = omit, + name: str | Omit = omit, + openai_key_uuid: str | Omit = omit, + project_id: str | Omit = omit, + region: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + workspace_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentCreateResponse: + """To create a new agent, send a POST request to `/v2/gen-ai/agents`. + + The response + body contains a JSON object with the newly created agent object. + + Args: + anthropic_key_uuid: Optional Anthropic API key ID to use with Anthropic models + + description: A text description of the agent, not used in inference + + instruction: Agent instruction. Instructions help your agent to perform its job effectively. + See + [Write Effective Agent Instructions](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#agent-instructions) + for best practices. + + knowledge_base_uuid: Ids of the knowledge base(s) to attach to the agent + + model_uuid: Identifier for the foundation model. + + name: Agent name + + openai_key_uuid: Optional OpenAI API key ID to use with OpenAI models + + project_id: The id of the DigitalOcean project this agent will belong to + + region: The DigitalOcean region to deploy your agent in + + tags: Agent tag to organize related resources + + workspace_uuid: Identifier for the workspace + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/agents" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/agents", + body=await async_maybe_transform( + { + "anthropic_key_uuid": anthropic_key_uuid, + "description": description, + "instruction": instruction, + "knowledge_base_uuid": knowledge_base_uuid, + "model_provider_key_uuid": model_provider_key_uuid, + "model_uuid": model_uuid, + "name": name, + "openai_key_uuid": openai_key_uuid, + "project_id": project_id, + "region": region, + "tags": tags, + "workspace_uuid": workspace_uuid, + }, + agent_create_params.AgentCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentCreateResponse, + ) + + async def retrieve( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentRetrieveResponse: + """To retrieve details of an agent, GET request to `/v2/gen-ai/agents/{uuid}`. + + The + response body is a JSON object containing the agent. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/agents/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentRetrieveResponse, + ) + + async def update( + self, + path_uuid: str, + *, + agent_log_insights_enabled: bool | Omit = omit, + allowed_domains: SequenceNotStr[str] | Omit = omit, + anthropic_key_uuid: str | Omit = omit, + conversation_logs_enabled: bool | Omit = omit, + description: str | Omit = omit, + instruction: str | Omit = omit, + k: int | Omit = omit, + max_tokens: int | Omit = omit, + model_provider_key_uuid: str | Omit = omit, + model_uuid: str | Omit = omit, + name: str | Omit = omit, + openai_key_uuid: str | Omit = omit, + project_id: str | Omit = omit, + provide_citations: bool | Omit = omit, + retrieval_method: APIRetrievalMethod | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + temperature: float | Omit = omit, + top_p: float | Omit = omit, + body_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentUpdateResponse: + """To update an agent, send a PUT request to `/v2/gen-ai/agents/{uuid}`. + + The + response body is a JSON object containing the agent. + + Args: + allowed_domains: Optional list of allowed domains for the chatbot - Must use fully qualified + domain name (FQDN) such as https://example.com + + anthropic_key_uuid: Optional anthropic key uuid for use with anthropic models + + conversation_logs_enabled: Optional update of conversation logs enabled + + description: Agent description + + instruction: Agent instruction. Instructions help your agent to perform its job effectively. + See + [Write Effective Agent Instructions](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#agent-instructions) + for best practices. + + k: How many results should be considered from an attached knowledge base + + max_tokens: Specifies the maximum number of tokens the model can process in a single input + or output, set as a number between 1 and 512. This determines the length of each + response. + + model_provider_key_uuid: Optional Model Provider uuid for use with provider models + + model_uuid: Identifier for the foundation model. + + name: Agent name + + openai_key_uuid: Optional OpenAI key uuid for use with OpenAI models + + project_id: The id of the DigitalOcean project this agent will belong to + + retrieval_method: - RETRIEVAL_METHOD_UNKNOWN: The retrieval method is unknown + - RETRIEVAL_METHOD_REWRITE: The retrieval method is rewrite + - RETRIEVAL_METHOD_STEP_BACK: The retrieval method is step back + - RETRIEVAL_METHOD_SUB_QUERIES: The retrieval method is sub queries + - RETRIEVAL_METHOD_NONE: The retrieval method is none + + tags: A set of abitrary tags to organize your agent + + temperature: Controls the model’s creativity, specified as a number between 0 and 1. Lower + values produce more predictable and conservative responses, while higher values + encourage creativity and variation. + + top_p: Defines the cumulative probability threshold for word selection, specified as a + number between 0 and 1. Higher values allow for more diverse outputs, while + lower values ensure focused and coherent responses. + + body_uuid: Unique agent id + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return await self._put( + f"/v2/gen-ai/agents/{path_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_uuid}", + body=await async_maybe_transform( + { + "agent_log_insights_enabled": agent_log_insights_enabled, + "allowed_domains": allowed_domains, + "anthropic_key_uuid": anthropic_key_uuid, + "conversation_logs_enabled": conversation_logs_enabled, + "description": description, + "instruction": instruction, + "k": k, + "max_tokens": max_tokens, + "model_provider_key_uuid": model_provider_key_uuid, + "model_uuid": model_uuid, + "name": name, + "openai_key_uuid": openai_key_uuid, + "project_id": project_id, + "provide_citations": provide_citations, + "retrieval_method": retrieval_method, + "tags": tags, + "temperature": temperature, + "top_p": top_p, + "body_uuid": body_uuid, + }, + agent_update_params.AgentUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentUpdateResponse, + ) + + async def list( + self, + *, + only_deployed: bool | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentListResponse: + """ + To list all agents, send a GET request to `/v2/gen-ai/agents`. + + Args: + only_deployed: Only list agents that are deployed. + + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/agents" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "only_deployed": only_deployed, + "page": page, + "per_page": per_page, + }, + agent_list_params.AgentListParams, + ), + ), + cast_to=AgentListResponse, + ) + + async def delete( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentDeleteResponse: + """ + To delete an agent, send a DELETE request to `/v2/gen-ai/agents/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._delete( + f"/v2/gen-ai/agents/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentDeleteResponse, + ) + + async def retrieve_usage( + self, + uuid: str, + *, + start: str | Omit = omit, + stop: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentRetrieveUsageResponse: + """ + To get agent usage, send a GET request to `/v2/gen-ai/agents/{uuid}/usage`. + Returns usage metrics for the specified agent within the provided time range. + + Args: + start: Return all usage data from this date. + + stop: Return all usage data up to this date, if omitted, will return up to the current + date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/agents/{uuid}/usage" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}/usage", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start": start, + "stop": stop, + }, + agent_retrieve_usage_params.AgentRetrieveUsageParams, + ), + ), + cast_to=AgentRetrieveUsageResponse, + ) + + async def update_status( + self, + path_uuid: str, + *, + body_uuid: str | Omit = omit, + visibility: APIDeploymentVisibility | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentUpdateStatusResponse: + """Check whether an agent is public or private. + + To update the agent status, send a + PUT request to `/v2/gen-ai/agents/{uuid}/deployment_visibility`. + + Args: + body_uuid: Unique id + + visibility: - VISIBILITY_UNKNOWN: The status of the deployment is unknown + - VISIBILITY_DISABLED: The deployment is disabled and will no longer service + requests + - VISIBILITY_PLAYGROUND: Deprecated: No longer a valid state + - VISIBILITY_PUBLIC: The deployment is public and will service requests from the + public internet + - VISIBILITY_PRIVATE: The deployment is private and will only service requests + from other agents, or through API keys + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return await self._put( + f"/v2/gen-ai/agents/{path_uuid}/deployment_visibility" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_uuid}/deployment_visibility", + body=await async_maybe_transform( + { + "body_uuid": body_uuid, + "visibility": visibility, + }, + agent_update_status_params.AgentUpdateStatusParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentUpdateStatusResponse, + ) + + +class AgentsResourceWithRawResponse: + def __init__(self, agents: AgentsResource) -> None: + self._agents = agents + + self.create = to_raw_response_wrapper( + agents.create, + ) + self.retrieve = to_raw_response_wrapper( + agents.retrieve, + ) + self.update = to_raw_response_wrapper( + agents.update, + ) + self.list = to_raw_response_wrapper( + agents.list, + ) + self.delete = to_raw_response_wrapper( + agents.delete, + ) + self.retrieve_usage = to_raw_response_wrapper( + agents.retrieve_usage, + ) + self.update_status = to_raw_response_wrapper( + agents.update_status, + ) + + @cached_property + def api_keys(self) -> APIKeysResourceWithRawResponse: + return APIKeysResourceWithRawResponse(self._agents.api_keys) + + @cached_property + def chat(self) -> ChatResourceWithRawResponse: + return ChatResourceWithRawResponse(self._agents.chat) + + @cached_property + def evaluation_metrics(self) -> EvaluationMetricsResourceWithRawResponse: + return EvaluationMetricsResourceWithRawResponse(self._agents.evaluation_metrics) + + @cached_property + def evaluation_runs(self) -> EvaluationRunsResourceWithRawResponse: + return EvaluationRunsResourceWithRawResponse(self._agents.evaluation_runs) + + @cached_property + def evaluation_test_cases(self) -> EvaluationTestCasesResourceWithRawResponse: + return EvaluationTestCasesResourceWithRawResponse(self._agents.evaluation_test_cases) + + @cached_property + def evaluation_datasets(self) -> EvaluationDatasetsResourceWithRawResponse: + return EvaluationDatasetsResourceWithRawResponse(self._agents.evaluation_datasets) + + @cached_property + def functions(self) -> FunctionsResourceWithRawResponse: + return FunctionsResourceWithRawResponse(self._agents.functions) + + @cached_property + def versions(self) -> VersionsResourceWithRawResponse: + return VersionsResourceWithRawResponse(self._agents.versions) + + @cached_property + def knowledge_bases(self) -> KnowledgeBasesResourceWithRawResponse: + return KnowledgeBasesResourceWithRawResponse(self._agents.knowledge_bases) + + @cached_property + def routes(self) -> RoutesResourceWithRawResponse: + return RoutesResourceWithRawResponse(self._agents.routes) + + +class AsyncAgentsResourceWithRawResponse: + def __init__(self, agents: AsyncAgentsResource) -> None: + self._agents = agents + + self.create = async_to_raw_response_wrapper( + agents.create, + ) + self.retrieve = async_to_raw_response_wrapper( + agents.retrieve, + ) + self.update = async_to_raw_response_wrapper( + agents.update, + ) + self.list = async_to_raw_response_wrapper( + agents.list, + ) + self.delete = async_to_raw_response_wrapper( + agents.delete, + ) + self.retrieve_usage = async_to_raw_response_wrapper( + agents.retrieve_usage, + ) + self.update_status = async_to_raw_response_wrapper( + agents.update_status, + ) + + @cached_property + def api_keys(self) -> AsyncAPIKeysResourceWithRawResponse: + return AsyncAPIKeysResourceWithRawResponse(self._agents.api_keys) + + @cached_property + def chat(self) -> AsyncChatResourceWithRawResponse: + return AsyncChatResourceWithRawResponse(self._agents.chat) + + @cached_property + def evaluation_metrics(self) -> AsyncEvaluationMetricsResourceWithRawResponse: + return AsyncEvaluationMetricsResourceWithRawResponse(self._agents.evaluation_metrics) + + @cached_property + def evaluation_runs(self) -> AsyncEvaluationRunsResourceWithRawResponse: + return AsyncEvaluationRunsResourceWithRawResponse(self._agents.evaluation_runs) + + @cached_property + def evaluation_test_cases(self) -> AsyncEvaluationTestCasesResourceWithRawResponse: + return AsyncEvaluationTestCasesResourceWithRawResponse(self._agents.evaluation_test_cases) + + @cached_property + def evaluation_datasets(self) -> AsyncEvaluationDatasetsResourceWithRawResponse: + return AsyncEvaluationDatasetsResourceWithRawResponse(self._agents.evaluation_datasets) + + @cached_property + def functions(self) -> AsyncFunctionsResourceWithRawResponse: + return AsyncFunctionsResourceWithRawResponse(self._agents.functions) + + @cached_property + def versions(self) -> AsyncVersionsResourceWithRawResponse: + return AsyncVersionsResourceWithRawResponse(self._agents.versions) + + @cached_property + def knowledge_bases(self) -> AsyncKnowledgeBasesResourceWithRawResponse: + return AsyncKnowledgeBasesResourceWithRawResponse(self._agents.knowledge_bases) + + @cached_property + def routes(self) -> AsyncRoutesResourceWithRawResponse: + return AsyncRoutesResourceWithRawResponse(self._agents.routes) + + +class AgentsResourceWithStreamingResponse: + def __init__(self, agents: AgentsResource) -> None: + self._agents = agents + + self.create = to_streamed_response_wrapper( + agents.create, + ) + self.retrieve = to_streamed_response_wrapper( + agents.retrieve, + ) + self.update = to_streamed_response_wrapper( + agents.update, + ) + self.list = to_streamed_response_wrapper( + agents.list, + ) + self.delete = to_streamed_response_wrapper( + agents.delete, + ) + self.retrieve_usage = to_streamed_response_wrapper( + agents.retrieve_usage, + ) + self.update_status = to_streamed_response_wrapper( + agents.update_status, + ) + + @cached_property + def api_keys(self) -> APIKeysResourceWithStreamingResponse: + return APIKeysResourceWithStreamingResponse(self._agents.api_keys) + + @cached_property + def chat(self) -> ChatResourceWithStreamingResponse: + return ChatResourceWithStreamingResponse(self._agents.chat) + + @cached_property + def evaluation_metrics(self) -> EvaluationMetricsResourceWithStreamingResponse: + return EvaluationMetricsResourceWithStreamingResponse(self._agents.evaluation_metrics) + + @cached_property + def evaluation_runs(self) -> EvaluationRunsResourceWithStreamingResponse: + return EvaluationRunsResourceWithStreamingResponse(self._agents.evaluation_runs) + + @cached_property + def evaluation_test_cases(self) -> EvaluationTestCasesResourceWithStreamingResponse: + return EvaluationTestCasesResourceWithStreamingResponse(self._agents.evaluation_test_cases) + + @cached_property + def evaluation_datasets(self) -> EvaluationDatasetsResourceWithStreamingResponse: + return EvaluationDatasetsResourceWithStreamingResponse(self._agents.evaluation_datasets) + + @cached_property + def functions(self) -> FunctionsResourceWithStreamingResponse: + return FunctionsResourceWithStreamingResponse(self._agents.functions) + + @cached_property + def versions(self) -> VersionsResourceWithStreamingResponse: + return VersionsResourceWithStreamingResponse(self._agents.versions) + + @cached_property + def knowledge_bases(self) -> KnowledgeBasesResourceWithStreamingResponse: + return KnowledgeBasesResourceWithStreamingResponse(self._agents.knowledge_bases) + + @cached_property + def routes(self) -> RoutesResourceWithStreamingResponse: + return RoutesResourceWithStreamingResponse(self._agents.routes) + + +class AsyncAgentsResourceWithStreamingResponse: + def __init__(self, agents: AsyncAgentsResource) -> None: + self._agents = agents + + self.create = async_to_streamed_response_wrapper( + agents.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + agents.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + agents.update, + ) + self.list = async_to_streamed_response_wrapper( + agents.list, + ) + self.delete = async_to_streamed_response_wrapper( + agents.delete, + ) + self.retrieve_usage = async_to_streamed_response_wrapper( + agents.retrieve_usage, + ) + self.update_status = async_to_streamed_response_wrapper( + agents.update_status, + ) + + @cached_property + def api_keys(self) -> AsyncAPIKeysResourceWithStreamingResponse: + return AsyncAPIKeysResourceWithStreamingResponse(self._agents.api_keys) + + @cached_property + def chat(self) -> AsyncChatResourceWithStreamingResponse: + return AsyncChatResourceWithStreamingResponse(self._agents.chat) + + @cached_property + def evaluation_metrics(self) -> AsyncEvaluationMetricsResourceWithStreamingResponse: + return AsyncEvaluationMetricsResourceWithStreamingResponse(self._agents.evaluation_metrics) + + @cached_property + def evaluation_runs(self) -> AsyncEvaluationRunsResourceWithStreamingResponse: + return AsyncEvaluationRunsResourceWithStreamingResponse(self._agents.evaluation_runs) + + @cached_property + def evaluation_test_cases(self) -> AsyncEvaluationTestCasesResourceWithStreamingResponse: + return AsyncEvaluationTestCasesResourceWithStreamingResponse(self._agents.evaluation_test_cases) + + @cached_property + def evaluation_datasets(self) -> AsyncEvaluationDatasetsResourceWithStreamingResponse: + return AsyncEvaluationDatasetsResourceWithStreamingResponse(self._agents.evaluation_datasets) + + @cached_property + def functions(self) -> AsyncFunctionsResourceWithStreamingResponse: + return AsyncFunctionsResourceWithStreamingResponse(self._agents.functions) + + @cached_property + def versions(self) -> AsyncVersionsResourceWithStreamingResponse: + return AsyncVersionsResourceWithStreamingResponse(self._agents.versions) + + @cached_property + def knowledge_bases(self) -> AsyncKnowledgeBasesResourceWithStreamingResponse: + return AsyncKnowledgeBasesResourceWithStreamingResponse(self._agents.knowledge_bases) + + @cached_property + def routes(self) -> AsyncRoutesResourceWithStreamingResponse: + return AsyncRoutesResourceWithStreamingResponse(self._agents.routes) diff --git a/src/gradient/resources/agents/api_keys.py b/src/gradient/resources/agents/api_keys.py new file mode 100644 index 00000000..174ebf60 --- /dev/null +++ b/src/gradient/resources/agents/api_keys.py @@ -0,0 +1,621 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.agents import api_key_list_params, api_key_create_params, api_key_update_params +from ...types.agents.api_key_list_response import APIKeyListResponse +from ...types.agents.api_key_create_response import APIKeyCreateResponse +from ...types.agents.api_key_delete_response import APIKeyDeleteResponse +from ...types.agents.api_key_update_response import APIKeyUpdateResponse +from ...types.agents.api_key_regenerate_response import APIKeyRegenerateResponse + +__all__ = ["APIKeysResource", "AsyncAPIKeysResource"] + + +class APIKeysResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> APIKeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return APIKeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> APIKeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return APIKeysResourceWithStreamingResponse(self) + + def create( + self, + path_agent_uuid: str, + *, + body_agent_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyCreateResponse: + """ + To create an agent API key, send a POST request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys`. + + Args: + body_agent_uuid: Agent id + + name: A human friendly name to identify the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_agent_uuid: + raise ValueError(f"Expected a non-empty value for `path_agent_uuid` but received {path_agent_uuid!r}") + return self._post( + f"/v2/gen-ai/agents/{path_agent_uuid}/api_keys" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_agent_uuid}/api_keys", + body=maybe_transform( + { + "body_agent_uuid": body_agent_uuid, + "name": name, + }, + api_key_create_params.APIKeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyCreateResponse, + ) + + def update( + self, + path_api_key_uuid: str, + *, + path_agent_uuid: str, + body_agent_uuid: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyUpdateResponse: + """ + To update an agent API key, send a PUT request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}`. + + Args: + body_agent_uuid: Agent id + + body_api_key_uuid: API key ID + + name: Name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_agent_uuid: + raise ValueError(f"Expected a non-empty value for `path_agent_uuid` but received {path_agent_uuid!r}") + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return self._put( + f"/v2/gen-ai/agents/{path_agent_uuid}/api_keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_agent_uuid}/api_keys/{path_api_key_uuid}", + body=maybe_transform( + { + "body_agent_uuid": body_agent_uuid, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + api_key_update_params.APIKeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyUpdateResponse, + ) + + def list( + self, + agent_uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyListResponse: + """ + To list all agent API keys, send a GET request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + return self._get( + f"/v2/gen-ai/agents/{agent_uuid}/api_keys" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/api_keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + api_key_list_params.APIKeyListParams, + ), + ), + cast_to=APIKeyListResponse, + ) + + def delete( + self, + api_key_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyDeleteResponse: + """ + To delete an API key for an agent, send a DELETE request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._delete( + f"/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyDeleteResponse, + ) + + def regenerate( + self, + api_key_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyRegenerateResponse: + """ + To regenerate an agent API key, send a PUT request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}/regenerate`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._put( + f"/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}/regenerate" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}/regenerate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyRegenerateResponse, + ) + + +class AsyncAPIKeysResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAPIKeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncAPIKeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAPIKeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncAPIKeysResourceWithStreamingResponse(self) + + async def create( + self, + path_agent_uuid: str, + *, + body_agent_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyCreateResponse: + """ + To create an agent API key, send a POST request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys`. + + Args: + body_agent_uuid: Agent id + + name: A human friendly name to identify the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_agent_uuid: + raise ValueError(f"Expected a non-empty value for `path_agent_uuid` but received {path_agent_uuid!r}") + return await self._post( + f"/v2/gen-ai/agents/{path_agent_uuid}/api_keys" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_agent_uuid}/api_keys", + body=await async_maybe_transform( + { + "body_agent_uuid": body_agent_uuid, + "name": name, + }, + api_key_create_params.APIKeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyCreateResponse, + ) + + async def update( + self, + path_api_key_uuid: str, + *, + path_agent_uuid: str, + body_agent_uuid: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyUpdateResponse: + """ + To update an agent API key, send a PUT request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}`. + + Args: + body_agent_uuid: Agent id + + body_api_key_uuid: API key ID + + name: Name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_agent_uuid: + raise ValueError(f"Expected a non-empty value for `path_agent_uuid` but received {path_agent_uuid!r}") + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return await self._put( + f"/v2/gen-ai/agents/{path_agent_uuid}/api_keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_agent_uuid}/api_keys/{path_api_key_uuid}", + body=await async_maybe_transform( + { + "body_agent_uuid": body_agent_uuid, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + api_key_update_params.APIKeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyUpdateResponse, + ) + + async def list( + self, + agent_uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyListResponse: + """ + To list all agent API keys, send a GET request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + return await self._get( + f"/v2/gen-ai/agents/{agent_uuid}/api_keys" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/api_keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + api_key_list_params.APIKeyListParams, + ), + ), + cast_to=APIKeyListResponse, + ) + + async def delete( + self, + api_key_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyDeleteResponse: + """ + To delete an API key for an agent, send a DELETE request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._delete( + f"/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyDeleteResponse, + ) + + async def regenerate( + self, + api_key_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyRegenerateResponse: + """ + To regenerate an agent API key, send a PUT request to + `/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}/regenerate`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._put( + f"/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}/regenerate" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/api_keys/{api_key_uuid}/regenerate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyRegenerateResponse, + ) + + +class APIKeysResourceWithRawResponse: + def __init__(self, api_keys: APIKeysResource) -> None: + self._api_keys = api_keys + + self.create = to_raw_response_wrapper( + api_keys.create, + ) + self.update = to_raw_response_wrapper( + api_keys.update, + ) + self.list = to_raw_response_wrapper( + api_keys.list, + ) + self.delete = to_raw_response_wrapper( + api_keys.delete, + ) + self.regenerate = to_raw_response_wrapper( + api_keys.regenerate, + ) + + +class AsyncAPIKeysResourceWithRawResponse: + def __init__(self, api_keys: AsyncAPIKeysResource) -> None: + self._api_keys = api_keys + + self.create = async_to_raw_response_wrapper( + api_keys.create, + ) + self.update = async_to_raw_response_wrapper( + api_keys.update, + ) + self.list = async_to_raw_response_wrapper( + api_keys.list, + ) + self.delete = async_to_raw_response_wrapper( + api_keys.delete, + ) + self.regenerate = async_to_raw_response_wrapper( + api_keys.regenerate, + ) + + +class APIKeysResourceWithStreamingResponse: + def __init__(self, api_keys: APIKeysResource) -> None: + self._api_keys = api_keys + + self.create = to_streamed_response_wrapper( + api_keys.create, + ) + self.update = to_streamed_response_wrapper( + api_keys.update, + ) + self.list = to_streamed_response_wrapper( + api_keys.list, + ) + self.delete = to_streamed_response_wrapper( + api_keys.delete, + ) + self.regenerate = to_streamed_response_wrapper( + api_keys.regenerate, + ) + + +class AsyncAPIKeysResourceWithStreamingResponse: + def __init__(self, api_keys: AsyncAPIKeysResource) -> None: + self._api_keys = api_keys + + self.create = async_to_streamed_response_wrapper( + api_keys.create, + ) + self.update = async_to_streamed_response_wrapper( + api_keys.update, + ) + self.list = async_to_streamed_response_wrapper( + api_keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + api_keys.delete, + ) + self.regenerate = async_to_streamed_response_wrapper( + api_keys.regenerate, + ) diff --git a/src/gradient/resources/agents/chat/__init__.py b/src/gradient/resources/agents/chat/__init__.py new file mode 100644 index 00000000..ec960eb4 --- /dev/null +++ b/src/gradient/resources/agents/chat/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .completions import ( + CompletionsResource, + AsyncCompletionsResource, + CompletionsResourceWithRawResponse, + AsyncCompletionsResourceWithRawResponse, + CompletionsResourceWithStreamingResponse, + AsyncCompletionsResourceWithStreamingResponse, +) + +__all__ = [ + "CompletionsResource", + "AsyncCompletionsResource", + "CompletionsResourceWithRawResponse", + "AsyncCompletionsResourceWithRawResponse", + "CompletionsResourceWithStreamingResponse", + "AsyncCompletionsResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/agents/chat/chat.py b/src/gradient/resources/agents/chat/chat.py new file mode 100644 index 00000000..80947cfb --- /dev/null +++ b/src/gradient/resources/agents/chat/chat.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ...._compat import cached_property +from .completions import ( + CompletionsResource, + AsyncCompletionsResource, + CompletionsResourceWithRawResponse, + AsyncCompletionsResourceWithRawResponse, + CompletionsResourceWithStreamingResponse, + AsyncCompletionsResourceWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def completions(self) -> CompletionsResource: + return CompletionsResource(self._client) + + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def completions(self) -> AsyncCompletionsResource: + return AsyncCompletionsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> CompletionsResourceWithRawResponse: + return CompletionsResourceWithRawResponse(self._chat.completions) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsResourceWithRawResponse: + return AsyncCompletionsResourceWithRawResponse(self._chat.completions) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> CompletionsResourceWithStreamingResponse: + return CompletionsResourceWithStreamingResponse(self._chat.completions) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsResourceWithStreamingResponse: + return AsyncCompletionsResourceWithStreamingResponse(self._chat.completions) diff --git a/src/gradient/resources/agents/chat/completions.py b/src/gradient/resources/agents/chat/completions.py new file mode 100644 index 00000000..53c13f21 --- /dev/null +++ b/src/gradient/resources/agents/chat/completions.py @@ -0,0 +1,1008 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, overload + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._streaming import Stream, AsyncStream +from ...._base_client import make_request_options +from ....types.agents.chat import completion_create_params +from ....types.shared.chat_completion_chunk import ChatCompletionChunk +from ....types.agents.chat.completion_create_response import CompletionCreateResponse + +__all__ = ["CompletionsResource", "AsyncCompletionsResource"] + + +class CompletionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CompletionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return CompletionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CompletionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return CompletionsResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + stream: Literal[True], + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + stream: bool, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse | Stream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["messages", "model"], ["messages", "model", "stream"]) + def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse | Stream[ChatCompletionChunk]: + return self._post( + "/chat/completions?agent=true" + if self._client._base_url_overridden + else f"{self._client.agent_endpoint}/api/v1/chat/completions?agent=true", + body=maybe_transform( + { + "messages": messages, + "model": model, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "metadata": metadata, + "n": n, + "presence_penalty": presence_penalty, + "stop": stop, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CompletionCreateResponse, + stream=stream or False, + stream_cls=Stream[ChatCompletionChunk], + ) + + +class AsyncCompletionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCompletionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncCompletionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCompletionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncCompletionsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + stream: Literal[True], + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + stream: bool, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse | AsyncStream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["messages", "model"], ["messages", "model", "stream"]) + async def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse | AsyncStream[ChatCompletionChunk]: + return await self._post( + "/chat/completions?agent=true" + if self._client._base_url_overridden + else f"{self._client.agent_endpoint}/api/v1/chat/completions?agent=true", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "metadata": metadata, + "n": n, + "presence_penalty": presence_penalty, + "stop": stop, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CompletionCreateResponse, + stream=stream or False, + stream_cls=AsyncStream[ChatCompletionChunk], + ) + + +class CompletionsResourceWithRawResponse: + def __init__(self, completions: CompletionsResource) -> None: + self._completions = completions + + self.create = to_raw_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsResourceWithRawResponse: + def __init__(self, completions: AsyncCompletionsResource) -> None: + self._completions = completions + + self.create = async_to_raw_response_wrapper( + completions.create, + ) + + +class CompletionsResourceWithStreamingResponse: + def __init__(self, completions: CompletionsResource) -> None: + self._completions = completions + + self.create = to_streamed_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsResourceWithStreamingResponse: + def __init__(self, completions: AsyncCompletionsResource) -> None: + self._completions = completions + + self.create = async_to_streamed_response_wrapper( + completions.create, + ) diff --git a/src/gradient/resources/agents/evaluation_datasets.py b/src/gradient/resources/agents/evaluation_datasets.py new file mode 100644 index 00000000..0f9631ba --- /dev/null +++ b/src/gradient/resources/agents/evaluation_datasets.py @@ -0,0 +1,292 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.agents import ( + evaluation_dataset_create_params, + evaluation_dataset_create_file_upload_presigned_urls_params, +) +from ...types.agents.evaluation_dataset_create_response import EvaluationDatasetCreateResponse +from ...types.knowledge_bases.api_file_upload_data_source_param import APIFileUploadDataSourceParam +from ...types.agents.evaluation_dataset_create_file_upload_presigned_urls_response import ( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, +) + +__all__ = ["EvaluationDatasetsResource", "AsyncEvaluationDatasetsResource"] + + +class EvaluationDatasetsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EvaluationDatasetsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return EvaluationDatasetsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EvaluationDatasetsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return EvaluationDatasetsResourceWithStreamingResponse(self) + + def create( + self, + *, + file_upload_dataset: APIFileUploadDataSourceParam | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationDatasetCreateResponse: + """ + To create an evaluation dataset, send a POST request to + `/v2/gen-ai/evaluation_datasets`. + + Args: + file_upload_dataset: File to upload as data source for knowledge base. + + name: The name of the agent evaluation dataset. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/evaluation_datasets" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_datasets", + body=maybe_transform( + { + "file_upload_dataset": file_upload_dataset, + "name": name, + }, + evaluation_dataset_create_params.EvaluationDatasetCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationDatasetCreateResponse, + ) + + def create_file_upload_presigned_urls( + self, + *, + files: Iterable[evaluation_dataset_create_file_upload_presigned_urls_params.File] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationDatasetCreateFileUploadPresignedURLsResponse: + """ + To create presigned URLs for evaluation dataset file upload, send a POST request + to `/v2/gen-ai/evaluation_datasets/file_upload_presigned_urls`. + + Args: + files: A list of files to generate presigned URLs for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/evaluation_datasets/file_upload_presigned_urls" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_datasets/file_upload_presigned_urls", + body=maybe_transform( + {"files": files}, + evaluation_dataset_create_file_upload_presigned_urls_params.EvaluationDatasetCreateFileUploadPresignedURLsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationDatasetCreateFileUploadPresignedURLsResponse, + ) + + +class AsyncEvaluationDatasetsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEvaluationDatasetsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncEvaluationDatasetsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEvaluationDatasetsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncEvaluationDatasetsResourceWithStreamingResponse(self) + + async def create( + self, + *, + file_upload_dataset: APIFileUploadDataSourceParam | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationDatasetCreateResponse: + """ + To create an evaluation dataset, send a POST request to + `/v2/gen-ai/evaluation_datasets`. + + Args: + file_upload_dataset: File to upload as data source for knowledge base. + + name: The name of the agent evaluation dataset. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/evaluation_datasets" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_datasets", + body=await async_maybe_transform( + { + "file_upload_dataset": file_upload_dataset, + "name": name, + }, + evaluation_dataset_create_params.EvaluationDatasetCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationDatasetCreateResponse, + ) + + async def create_file_upload_presigned_urls( + self, + *, + files: Iterable[evaluation_dataset_create_file_upload_presigned_urls_params.File] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationDatasetCreateFileUploadPresignedURLsResponse: + """ + To create presigned URLs for evaluation dataset file upload, send a POST request + to `/v2/gen-ai/evaluation_datasets/file_upload_presigned_urls`. + + Args: + files: A list of files to generate presigned URLs for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/evaluation_datasets/file_upload_presigned_urls" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_datasets/file_upload_presigned_urls", + body=await async_maybe_transform( + {"files": files}, + evaluation_dataset_create_file_upload_presigned_urls_params.EvaluationDatasetCreateFileUploadPresignedURLsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationDatasetCreateFileUploadPresignedURLsResponse, + ) + + +class EvaluationDatasetsResourceWithRawResponse: + def __init__(self, evaluation_datasets: EvaluationDatasetsResource) -> None: + self._evaluation_datasets = evaluation_datasets + + self.create = to_raw_response_wrapper( + evaluation_datasets.create, + ) + self.create_file_upload_presigned_urls = to_raw_response_wrapper( + evaluation_datasets.create_file_upload_presigned_urls, + ) + + +class AsyncEvaluationDatasetsResourceWithRawResponse: + def __init__(self, evaluation_datasets: AsyncEvaluationDatasetsResource) -> None: + self._evaluation_datasets = evaluation_datasets + + self.create = async_to_raw_response_wrapper( + evaluation_datasets.create, + ) + self.create_file_upload_presigned_urls = async_to_raw_response_wrapper( + evaluation_datasets.create_file_upload_presigned_urls, + ) + + +class EvaluationDatasetsResourceWithStreamingResponse: + def __init__(self, evaluation_datasets: EvaluationDatasetsResource) -> None: + self._evaluation_datasets = evaluation_datasets + + self.create = to_streamed_response_wrapper( + evaluation_datasets.create, + ) + self.create_file_upload_presigned_urls = to_streamed_response_wrapper( + evaluation_datasets.create_file_upload_presigned_urls, + ) + + +class AsyncEvaluationDatasetsResourceWithStreamingResponse: + def __init__(self, evaluation_datasets: AsyncEvaluationDatasetsResource) -> None: + self._evaluation_datasets = evaluation_datasets + + self.create = async_to_streamed_response_wrapper( + evaluation_datasets.create, + ) + self.create_file_upload_presigned_urls = async_to_streamed_response_wrapper( + evaluation_datasets.create_file_upload_presigned_urls, + ) diff --git a/src/gradient/resources/agents/evaluation_metrics/__init__.py b/src/gradient/resources/agents/evaluation_metrics/__init__.py new file mode 100644 index 00000000..fcb54c78 --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/__init__.py @@ -0,0 +1,89 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .oauth2 import ( + Oauth2Resource, + AsyncOauth2Resource, + Oauth2ResourceWithRawResponse, + AsyncOauth2ResourceWithRawResponse, + Oauth2ResourceWithStreamingResponse, + AsyncOauth2ResourceWithStreamingResponse, +) +from .openai import ( + OpenAIResource, + AsyncOpenAIResource, + OpenAIResourceWithRawResponse, + AsyncOpenAIResourceWithRawResponse, + OpenAIResourceWithStreamingResponse, + AsyncOpenAIResourceWithStreamingResponse, +) +from .anthropic import ( + AnthropicResource, + AsyncAnthropicResource, + AnthropicResourceWithRawResponse, + AsyncAnthropicResourceWithRawResponse, + AnthropicResourceWithStreamingResponse, + AsyncAnthropicResourceWithStreamingResponse, +) +from .workspaces import ( + WorkspacesResource, + AsyncWorkspacesResource, + WorkspacesResourceWithRawResponse, + AsyncWorkspacesResourceWithRawResponse, + WorkspacesResourceWithStreamingResponse, + AsyncWorkspacesResourceWithStreamingResponse, +) +from .evaluation_metrics import ( + EvaluationMetricsResource, + AsyncEvaluationMetricsResource, + EvaluationMetricsResourceWithRawResponse, + AsyncEvaluationMetricsResourceWithRawResponse, + EvaluationMetricsResourceWithStreamingResponse, + AsyncEvaluationMetricsResourceWithStreamingResponse, +) +from .scheduled_indexing import ( + ScheduledIndexingResource, + AsyncScheduledIndexingResource, + ScheduledIndexingResourceWithRawResponse, + AsyncScheduledIndexingResourceWithRawResponse, + ScheduledIndexingResourceWithStreamingResponse, + AsyncScheduledIndexingResourceWithStreamingResponse, +) + +__all__ = [ + "WorkspacesResource", + "AsyncWorkspacesResource", + "WorkspacesResourceWithRawResponse", + "AsyncWorkspacesResourceWithRawResponse", + "WorkspacesResourceWithStreamingResponse", + "AsyncWorkspacesResourceWithStreamingResponse", + "AnthropicResource", + "AsyncAnthropicResource", + "AnthropicResourceWithRawResponse", + "AsyncAnthropicResourceWithRawResponse", + "AnthropicResourceWithStreamingResponse", + "AsyncAnthropicResourceWithStreamingResponse", + "OpenAIResource", + "AsyncOpenAIResource", + "OpenAIResourceWithRawResponse", + "AsyncOpenAIResourceWithRawResponse", + "OpenAIResourceWithStreamingResponse", + "AsyncOpenAIResourceWithStreamingResponse", + "Oauth2Resource", + "AsyncOauth2Resource", + "Oauth2ResourceWithRawResponse", + "AsyncOauth2ResourceWithRawResponse", + "Oauth2ResourceWithStreamingResponse", + "AsyncOauth2ResourceWithStreamingResponse", + "ScheduledIndexingResource", + "AsyncScheduledIndexingResource", + "ScheduledIndexingResourceWithRawResponse", + "AsyncScheduledIndexingResourceWithRawResponse", + "ScheduledIndexingResourceWithStreamingResponse", + "AsyncScheduledIndexingResourceWithStreamingResponse", + "EvaluationMetricsResource", + "AsyncEvaluationMetricsResource", + "EvaluationMetricsResourceWithRawResponse", + "AsyncEvaluationMetricsResourceWithRawResponse", + "EvaluationMetricsResourceWithStreamingResponse", + "AsyncEvaluationMetricsResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/agents/evaluation_metrics/anthropic/__init__.py b/src/gradient/resources/agents/evaluation_metrics/anthropic/__init__.py new file mode 100644 index 00000000..057a3a2f --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/anthropic/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .keys import ( + KeysResource, + AsyncKeysResource, + KeysResourceWithRawResponse, + AsyncKeysResourceWithRawResponse, + KeysResourceWithStreamingResponse, + AsyncKeysResourceWithStreamingResponse, +) +from .anthropic import ( + AnthropicResource, + AsyncAnthropicResource, + AnthropicResourceWithRawResponse, + AsyncAnthropicResourceWithRawResponse, + AnthropicResourceWithStreamingResponse, + AsyncAnthropicResourceWithStreamingResponse, +) + +__all__ = [ + "KeysResource", + "AsyncKeysResource", + "KeysResourceWithRawResponse", + "AsyncKeysResourceWithRawResponse", + "KeysResourceWithStreamingResponse", + "AsyncKeysResourceWithStreamingResponse", + "AnthropicResource", + "AsyncAnthropicResource", + "AnthropicResourceWithRawResponse", + "AsyncAnthropicResourceWithRawResponse", + "AnthropicResourceWithStreamingResponse", + "AsyncAnthropicResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/agents/evaluation_metrics/anthropic/anthropic.py b/src/gradient/resources/agents/evaluation_metrics/anthropic/anthropic.py new file mode 100644 index 00000000..0079d59b --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/anthropic/anthropic.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .keys import ( + KeysResource, + AsyncKeysResource, + KeysResourceWithRawResponse, + AsyncKeysResourceWithRawResponse, + KeysResourceWithStreamingResponse, + AsyncKeysResourceWithStreamingResponse, +) +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["AnthropicResource", "AsyncAnthropicResource"] + + +class AnthropicResource(SyncAPIResource): + @cached_property + def keys(self) -> KeysResource: + return KeysResource(self._client) + + @cached_property + def with_raw_response(self) -> AnthropicResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AnthropicResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AnthropicResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AnthropicResourceWithStreamingResponse(self) + + +class AsyncAnthropicResource(AsyncAPIResource): + @cached_property + def keys(self) -> AsyncKeysResource: + return AsyncKeysResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAnthropicResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncAnthropicResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAnthropicResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncAnthropicResourceWithStreamingResponse(self) + + +class AnthropicResourceWithRawResponse: + def __init__(self, anthropic: AnthropicResource) -> None: + self._anthropic = anthropic + + @cached_property + def keys(self) -> KeysResourceWithRawResponse: + return KeysResourceWithRawResponse(self._anthropic.keys) + + +class AsyncAnthropicResourceWithRawResponse: + def __init__(self, anthropic: AsyncAnthropicResource) -> None: + self._anthropic = anthropic + + @cached_property + def keys(self) -> AsyncKeysResourceWithRawResponse: + return AsyncKeysResourceWithRawResponse(self._anthropic.keys) + + +class AnthropicResourceWithStreamingResponse: + def __init__(self, anthropic: AnthropicResource) -> None: + self._anthropic = anthropic + + @cached_property + def keys(self) -> KeysResourceWithStreamingResponse: + return KeysResourceWithStreamingResponse(self._anthropic.keys) + + +class AsyncAnthropicResourceWithStreamingResponse: + def __init__(self, anthropic: AsyncAnthropicResource) -> None: + self._anthropic = anthropic + + @cached_property + def keys(self) -> AsyncKeysResourceWithStreamingResponse: + return AsyncKeysResourceWithStreamingResponse(self._anthropic.keys) diff --git a/src/gradient/resources/agents/evaluation_metrics/anthropic/keys.py b/src/gradient/resources/agents/evaluation_metrics/anthropic/keys.py new file mode 100644 index 00000000..e015bf5c --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/anthropic/keys.py @@ -0,0 +1,711 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....._base_client import make_request_options +from .....types.agents.evaluation_metrics.anthropic import ( + key_list_params, + key_create_params, + key_update_params, + key_list_agents_params, +) +from .....types.agents.evaluation_metrics.anthropic.key_list_response import KeyListResponse +from .....types.agents.evaluation_metrics.anthropic.key_create_response import KeyCreateResponse +from .....types.agents.evaluation_metrics.anthropic.key_delete_response import KeyDeleteResponse +from .....types.agents.evaluation_metrics.anthropic.key_update_response import KeyUpdateResponse +from .....types.agents.evaluation_metrics.anthropic.key_retrieve_response import KeyRetrieveResponse +from .....types.agents.evaluation_metrics.anthropic.key_list_agents_response import KeyListAgentsResponse + +__all__ = ["KeysResource", "AsyncKeysResource"] + + +class KeysResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> KeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return KeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> KeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return KeysResourceWithStreamingResponse(self) + + def create( + self, + *, + api_key: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyCreateResponse: + """ + To create an Anthropic API key, send a POST request to + `/v2/gen-ai/anthropic/keys`. + + Args: + api_key: Anthropic API key + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/anthropic/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/anthropic/keys", + body=maybe_transform( + { + "api_key": api_key, + "name": name, + }, + key_create_params.KeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyCreateResponse, + ) + + def retrieve( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyRetrieveResponse: + """ + To retrieve details of an Anthropic API key, send a GET request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._get( + f"/v2/gen-ai/anthropic/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyRetrieveResponse, + ) + + def update( + self, + path_api_key_uuid: str, + *, + api_key: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyUpdateResponse: + """ + To update an Anthropic API key, send a PUT request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + api_key: Anthropic API key + + body_api_key_uuid: API key ID + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return self._put( + f"/v2/gen-ai/anthropic/keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{path_api_key_uuid}", + body=maybe_transform( + { + "api_key": api_key, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + key_update_params.KeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListResponse: + """ + To list all Anthropic API keys, send a GET request to + `/v2/gen-ai/anthropic/keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/anthropic/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/anthropic/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_params.KeyListParams, + ), + ), + cast_to=KeyListResponse, + ) + + def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyDeleteResponse: + """ + To delete an Anthropic API key, send a DELETE request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._delete( + f"/v2/gen-ai/anthropic/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyDeleteResponse, + ) + + def list_agents( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListAgentsResponse: + """ + List Agents by Anthropic Key. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/anthropic/keys/{uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_agents_params.KeyListAgentsParams, + ), + ), + cast_to=KeyListAgentsResponse, + ) + + +class AsyncKeysResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncKeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncKeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncKeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncKeysResourceWithStreamingResponse(self) + + async def create( + self, + *, + api_key: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyCreateResponse: + """ + To create an Anthropic API key, send a POST request to + `/v2/gen-ai/anthropic/keys`. + + Args: + api_key: Anthropic API key + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/anthropic/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/anthropic/keys", + body=await async_maybe_transform( + { + "api_key": api_key, + "name": name, + }, + key_create_params.KeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyCreateResponse, + ) + + async def retrieve( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyRetrieveResponse: + """ + To retrieve details of an Anthropic API key, send a GET request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._get( + f"/v2/gen-ai/anthropic/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyRetrieveResponse, + ) + + async def update( + self, + path_api_key_uuid: str, + *, + api_key: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyUpdateResponse: + """ + To update an Anthropic API key, send a PUT request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + api_key: Anthropic API key + + body_api_key_uuid: API key ID + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return await self._put( + f"/v2/gen-ai/anthropic/keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{path_api_key_uuid}", + body=await async_maybe_transform( + { + "api_key": api_key, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + key_update_params.KeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListResponse: + """ + To list all Anthropic API keys, send a GET request to + `/v2/gen-ai/anthropic/keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/anthropic/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/anthropic/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_params.KeyListParams, + ), + ), + cast_to=KeyListResponse, + ) + + async def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyDeleteResponse: + """ + To delete an Anthropic API key, send a DELETE request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._delete( + f"/v2/gen-ai/anthropic/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyDeleteResponse, + ) + + async def list_agents( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListAgentsResponse: + """ + List Agents by Anthropic Key. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/anthropic/keys/{uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_agents_params.KeyListAgentsParams, + ), + ), + cast_to=KeyListAgentsResponse, + ) + + +class KeysResourceWithRawResponse: + def __init__(self, keys: KeysResource) -> None: + self._keys = keys + + self.create = to_raw_response_wrapper( + keys.create, + ) + self.retrieve = to_raw_response_wrapper( + keys.retrieve, + ) + self.update = to_raw_response_wrapper( + keys.update, + ) + self.list = to_raw_response_wrapper( + keys.list, + ) + self.delete = to_raw_response_wrapper( + keys.delete, + ) + self.list_agents = to_raw_response_wrapper( + keys.list_agents, + ) + + +class AsyncKeysResourceWithRawResponse: + def __init__(self, keys: AsyncKeysResource) -> None: + self._keys = keys + + self.create = async_to_raw_response_wrapper( + keys.create, + ) + self.retrieve = async_to_raw_response_wrapper( + keys.retrieve, + ) + self.update = async_to_raw_response_wrapper( + keys.update, + ) + self.list = async_to_raw_response_wrapper( + keys.list, + ) + self.delete = async_to_raw_response_wrapper( + keys.delete, + ) + self.list_agents = async_to_raw_response_wrapper( + keys.list_agents, + ) + + +class KeysResourceWithStreamingResponse: + def __init__(self, keys: KeysResource) -> None: + self._keys = keys + + self.create = to_streamed_response_wrapper( + keys.create, + ) + self.retrieve = to_streamed_response_wrapper( + keys.retrieve, + ) + self.update = to_streamed_response_wrapper( + keys.update, + ) + self.list = to_streamed_response_wrapper( + keys.list, + ) + self.delete = to_streamed_response_wrapper( + keys.delete, + ) + self.list_agents = to_streamed_response_wrapper( + keys.list_agents, + ) + + +class AsyncKeysResourceWithStreamingResponse: + def __init__(self, keys: AsyncKeysResource) -> None: + self._keys = keys + + self.create = async_to_streamed_response_wrapper( + keys.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + keys.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + keys.update, + ) + self.list = async_to_streamed_response_wrapper( + keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + keys.delete, + ) + self.list_agents = async_to_streamed_response_wrapper( + keys.list_agents, + ) diff --git a/src/gradient/resources/agents/evaluation_metrics/evaluation_metrics.py b/src/gradient/resources/agents/evaluation_metrics/evaluation_metrics.py new file mode 100644 index 00000000..b9080132 --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/evaluation_metrics.py @@ -0,0 +1,416 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .oauth2.oauth2 import ( + Oauth2Resource, + AsyncOauth2Resource, + Oauth2ResourceWithRawResponse, + AsyncOauth2ResourceWithRawResponse, + Oauth2ResourceWithStreamingResponse, + AsyncOauth2ResourceWithStreamingResponse, +) +from .openai.openai import ( + OpenAIResource, + AsyncOpenAIResource, + OpenAIResourceWithRawResponse, + AsyncOpenAIResourceWithRawResponse, + OpenAIResourceWithStreamingResponse, + AsyncOpenAIResourceWithStreamingResponse, +) +from ...._base_client import make_request_options +from ....types.agents import evaluation_metric_list_regions_params +from .scheduled_indexing import ( + ScheduledIndexingResource, + AsyncScheduledIndexingResource, + ScheduledIndexingResourceWithRawResponse, + AsyncScheduledIndexingResourceWithRawResponse, + ScheduledIndexingResourceWithStreamingResponse, + AsyncScheduledIndexingResourceWithStreamingResponse, +) +from .anthropic.anthropic import ( + AnthropicResource, + AsyncAnthropicResource, + AnthropicResourceWithRawResponse, + AsyncAnthropicResourceWithRawResponse, + AnthropicResourceWithStreamingResponse, + AsyncAnthropicResourceWithStreamingResponse, +) +from .workspaces.workspaces import ( + WorkspacesResource, + AsyncWorkspacesResource, + WorkspacesResourceWithRawResponse, + AsyncWorkspacesResourceWithRawResponse, + WorkspacesResourceWithStreamingResponse, + AsyncWorkspacesResourceWithStreamingResponse, +) +from ....types.agents.evaluation_metric_list_response import EvaluationMetricListResponse +from ....types.agents.evaluation_metric_list_regions_response import EvaluationMetricListRegionsResponse + +__all__ = ["EvaluationMetricsResource", "AsyncEvaluationMetricsResource"] + + +class EvaluationMetricsResource(SyncAPIResource): + @cached_property + def workspaces(self) -> WorkspacesResource: + return WorkspacesResource(self._client) + + @cached_property + def anthropic(self) -> AnthropicResource: + return AnthropicResource(self._client) + + @cached_property + def openai(self) -> OpenAIResource: + return OpenAIResource(self._client) + + @cached_property + def oauth2(self) -> Oauth2Resource: + return Oauth2Resource(self._client) + + @cached_property + def scheduled_indexing(self) -> ScheduledIndexingResource: + return ScheduledIndexingResource(self._client) + + @cached_property + def with_raw_response(self) -> EvaluationMetricsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return EvaluationMetricsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EvaluationMetricsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return EvaluationMetricsResourceWithStreamingResponse(self) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationMetricListResponse: + """ + To list all evaluation metrics, send a GET request to + `/v2/gen-ai/evaluation_metrics`. + """ + return self._get( + "/v2/gen-ai/evaluation_metrics" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_metrics", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationMetricListResponse, + ) + + def list_regions( + self, + *, + serves_batch: bool | Omit = omit, + serves_inference: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationMetricListRegionsResponse: + """ + To list all datacenter regions, send a GET request to `/v2/gen-ai/regions`. + + Args: + serves_batch: Include datacenters that are capable of running batch jobs. + + serves_inference: Include datacenters that serve inference. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/regions" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/regions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "serves_batch": serves_batch, + "serves_inference": serves_inference, + }, + evaluation_metric_list_regions_params.EvaluationMetricListRegionsParams, + ), + ), + cast_to=EvaluationMetricListRegionsResponse, + ) + + +class AsyncEvaluationMetricsResource(AsyncAPIResource): + @cached_property + def workspaces(self) -> AsyncWorkspacesResource: + return AsyncWorkspacesResource(self._client) + + @cached_property + def anthropic(self) -> AsyncAnthropicResource: + return AsyncAnthropicResource(self._client) + + @cached_property + def openai(self) -> AsyncOpenAIResource: + return AsyncOpenAIResource(self._client) + + @cached_property + def oauth2(self) -> AsyncOauth2Resource: + return AsyncOauth2Resource(self._client) + + @cached_property + def scheduled_indexing(self) -> AsyncScheduledIndexingResource: + return AsyncScheduledIndexingResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncEvaluationMetricsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncEvaluationMetricsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEvaluationMetricsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncEvaluationMetricsResourceWithStreamingResponse(self) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationMetricListResponse: + """ + To list all evaluation metrics, send a GET request to + `/v2/gen-ai/evaluation_metrics`. + """ + return await self._get( + "/v2/gen-ai/evaluation_metrics" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_metrics", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationMetricListResponse, + ) + + async def list_regions( + self, + *, + serves_batch: bool | Omit = omit, + serves_inference: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationMetricListRegionsResponse: + """ + To list all datacenter regions, send a GET request to `/v2/gen-ai/regions`. + + Args: + serves_batch: Include datacenters that are capable of running batch jobs. + + serves_inference: Include datacenters that serve inference. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/regions" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/regions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "serves_batch": serves_batch, + "serves_inference": serves_inference, + }, + evaluation_metric_list_regions_params.EvaluationMetricListRegionsParams, + ), + ), + cast_to=EvaluationMetricListRegionsResponse, + ) + + +class EvaluationMetricsResourceWithRawResponse: + def __init__(self, evaluation_metrics: EvaluationMetricsResource) -> None: + self._evaluation_metrics = evaluation_metrics + + self.list = to_raw_response_wrapper( + evaluation_metrics.list, + ) + self.list_regions = to_raw_response_wrapper( + evaluation_metrics.list_regions, + ) + + @cached_property + def workspaces(self) -> WorkspacesResourceWithRawResponse: + return WorkspacesResourceWithRawResponse(self._evaluation_metrics.workspaces) + + @cached_property + def anthropic(self) -> AnthropicResourceWithRawResponse: + return AnthropicResourceWithRawResponse(self._evaluation_metrics.anthropic) + + @cached_property + def openai(self) -> OpenAIResourceWithRawResponse: + return OpenAIResourceWithRawResponse(self._evaluation_metrics.openai) + + @cached_property + def oauth2(self) -> Oauth2ResourceWithRawResponse: + return Oauth2ResourceWithRawResponse(self._evaluation_metrics.oauth2) + + @cached_property + def scheduled_indexing(self) -> ScheduledIndexingResourceWithRawResponse: + return ScheduledIndexingResourceWithRawResponse(self._evaluation_metrics.scheduled_indexing) + + +class AsyncEvaluationMetricsResourceWithRawResponse: + def __init__(self, evaluation_metrics: AsyncEvaluationMetricsResource) -> None: + self._evaluation_metrics = evaluation_metrics + + self.list = async_to_raw_response_wrapper( + evaluation_metrics.list, + ) + self.list_regions = async_to_raw_response_wrapper( + evaluation_metrics.list_regions, + ) + + @cached_property + def workspaces(self) -> AsyncWorkspacesResourceWithRawResponse: + return AsyncWorkspacesResourceWithRawResponse(self._evaluation_metrics.workspaces) + + @cached_property + def anthropic(self) -> AsyncAnthropicResourceWithRawResponse: + return AsyncAnthropicResourceWithRawResponse(self._evaluation_metrics.anthropic) + + @cached_property + def openai(self) -> AsyncOpenAIResourceWithRawResponse: + return AsyncOpenAIResourceWithRawResponse(self._evaluation_metrics.openai) + + @cached_property + def oauth2(self) -> AsyncOauth2ResourceWithRawResponse: + return AsyncOauth2ResourceWithRawResponse(self._evaluation_metrics.oauth2) + + @cached_property + def scheduled_indexing(self) -> AsyncScheduledIndexingResourceWithRawResponse: + return AsyncScheduledIndexingResourceWithRawResponse(self._evaluation_metrics.scheduled_indexing) + + +class EvaluationMetricsResourceWithStreamingResponse: + def __init__(self, evaluation_metrics: EvaluationMetricsResource) -> None: + self._evaluation_metrics = evaluation_metrics + + self.list = to_streamed_response_wrapper( + evaluation_metrics.list, + ) + self.list_regions = to_streamed_response_wrapper( + evaluation_metrics.list_regions, + ) + + @cached_property + def workspaces(self) -> WorkspacesResourceWithStreamingResponse: + return WorkspacesResourceWithStreamingResponse(self._evaluation_metrics.workspaces) + + @cached_property + def anthropic(self) -> AnthropicResourceWithStreamingResponse: + return AnthropicResourceWithStreamingResponse(self._evaluation_metrics.anthropic) + + @cached_property + def openai(self) -> OpenAIResourceWithStreamingResponse: + return OpenAIResourceWithStreamingResponse(self._evaluation_metrics.openai) + + @cached_property + def oauth2(self) -> Oauth2ResourceWithStreamingResponse: + return Oauth2ResourceWithStreamingResponse(self._evaluation_metrics.oauth2) + + @cached_property + def scheduled_indexing(self) -> ScheduledIndexingResourceWithStreamingResponse: + return ScheduledIndexingResourceWithStreamingResponse(self._evaluation_metrics.scheduled_indexing) + + +class AsyncEvaluationMetricsResourceWithStreamingResponse: + def __init__(self, evaluation_metrics: AsyncEvaluationMetricsResource) -> None: + self._evaluation_metrics = evaluation_metrics + + self.list = async_to_streamed_response_wrapper( + evaluation_metrics.list, + ) + self.list_regions = async_to_streamed_response_wrapper( + evaluation_metrics.list_regions, + ) + + @cached_property + def workspaces(self) -> AsyncWorkspacesResourceWithStreamingResponse: + return AsyncWorkspacesResourceWithStreamingResponse(self._evaluation_metrics.workspaces) + + @cached_property + def anthropic(self) -> AsyncAnthropicResourceWithStreamingResponse: + return AsyncAnthropicResourceWithStreamingResponse(self._evaluation_metrics.anthropic) + + @cached_property + def openai(self) -> AsyncOpenAIResourceWithStreamingResponse: + return AsyncOpenAIResourceWithStreamingResponse(self._evaluation_metrics.openai) + + @cached_property + def oauth2(self) -> AsyncOauth2ResourceWithStreamingResponse: + return AsyncOauth2ResourceWithStreamingResponse(self._evaluation_metrics.oauth2) + + @cached_property + def scheduled_indexing(self) -> AsyncScheduledIndexingResourceWithStreamingResponse: + return AsyncScheduledIndexingResourceWithStreamingResponse(self._evaluation_metrics.scheduled_indexing) diff --git a/src/gradient/resources/agents/evaluation_metrics/oauth2/__init__.py b/src/gradient/resources/agents/evaluation_metrics/oauth2/__init__.py new file mode 100644 index 00000000..c74ddfe8 --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/oauth2/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .oauth2 import ( + Oauth2Resource, + AsyncOauth2Resource, + Oauth2ResourceWithRawResponse, + AsyncOauth2ResourceWithRawResponse, + Oauth2ResourceWithStreamingResponse, + AsyncOauth2ResourceWithStreamingResponse, +) +from .dropbox import ( + DropboxResource, + AsyncDropboxResource, + DropboxResourceWithRawResponse, + AsyncDropboxResourceWithRawResponse, + DropboxResourceWithStreamingResponse, + AsyncDropboxResourceWithStreamingResponse, +) + +__all__ = [ + "DropboxResource", + "AsyncDropboxResource", + "DropboxResourceWithRawResponse", + "AsyncDropboxResourceWithRawResponse", + "DropboxResourceWithStreamingResponse", + "AsyncDropboxResourceWithStreamingResponse", + "Oauth2Resource", + "AsyncOauth2Resource", + "Oauth2ResourceWithRawResponse", + "AsyncOauth2ResourceWithRawResponse", + "Oauth2ResourceWithStreamingResponse", + "AsyncOauth2ResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/agents/evaluation_metrics/oauth2/dropbox.py b/src/gradient/resources/agents/evaluation_metrics/oauth2/dropbox.py new file mode 100644 index 00000000..256040ba --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/oauth2/dropbox.py @@ -0,0 +1,193 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....._base_client import make_request_options +from .....types.agents.evaluation_metrics.oauth2 import dropbox_create_tokens_params +from .....types.agents.evaluation_metrics.oauth2.dropbox_create_tokens_response import DropboxCreateTokensResponse + +__all__ = ["DropboxResource", "AsyncDropboxResource"] + + +class DropboxResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DropboxResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return DropboxResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DropboxResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return DropboxResourceWithStreamingResponse(self) + + def create_tokens( + self, + *, + code: str | Omit = omit, + redirect_url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DropboxCreateTokensResponse: + """ + To obtain the refresh token, needed for creation of data sources, send a GET + request to `/v2/gen-ai/oauth2/dropbox/tokens`. Pass the code you obtrained from + the oauth flow in the field 'code' + + Args: + code: The oauth2 code from google + + redirect_url: Redirect url + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/oauth2/dropbox/tokens" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/oauth2/dropbox/tokens", + body=maybe_transform( + { + "code": code, + "redirect_url": redirect_url, + }, + dropbox_create_tokens_params.DropboxCreateTokensParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DropboxCreateTokensResponse, + ) + + +class AsyncDropboxResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDropboxResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncDropboxResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDropboxResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncDropboxResourceWithStreamingResponse(self) + + async def create_tokens( + self, + *, + code: str | Omit = omit, + redirect_url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DropboxCreateTokensResponse: + """ + To obtain the refresh token, needed for creation of data sources, send a GET + request to `/v2/gen-ai/oauth2/dropbox/tokens`. Pass the code you obtrained from + the oauth flow in the field 'code' + + Args: + code: The oauth2 code from google + + redirect_url: Redirect url + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/oauth2/dropbox/tokens" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/oauth2/dropbox/tokens", + body=await async_maybe_transform( + { + "code": code, + "redirect_url": redirect_url, + }, + dropbox_create_tokens_params.DropboxCreateTokensParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DropboxCreateTokensResponse, + ) + + +class DropboxResourceWithRawResponse: + def __init__(self, dropbox: DropboxResource) -> None: + self._dropbox = dropbox + + self.create_tokens = to_raw_response_wrapper( + dropbox.create_tokens, + ) + + +class AsyncDropboxResourceWithRawResponse: + def __init__(self, dropbox: AsyncDropboxResource) -> None: + self._dropbox = dropbox + + self.create_tokens = async_to_raw_response_wrapper( + dropbox.create_tokens, + ) + + +class DropboxResourceWithStreamingResponse: + def __init__(self, dropbox: DropboxResource) -> None: + self._dropbox = dropbox + + self.create_tokens = to_streamed_response_wrapper( + dropbox.create_tokens, + ) + + +class AsyncDropboxResourceWithStreamingResponse: + def __init__(self, dropbox: AsyncDropboxResource) -> None: + self._dropbox = dropbox + + self.create_tokens = async_to_streamed_response_wrapper( + dropbox.create_tokens, + ) diff --git a/src/gradient/resources/agents/evaluation_metrics/oauth2/oauth2.py b/src/gradient/resources/agents/evaluation_metrics/oauth2/oauth2.py new file mode 100644 index 00000000..335e58d7 --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/oauth2/oauth2.py @@ -0,0 +1,229 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .dropbox import ( + DropboxResource, + AsyncDropboxResource, + DropboxResourceWithRawResponse, + AsyncDropboxResourceWithRawResponse, + DropboxResourceWithStreamingResponse, + AsyncDropboxResourceWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....._base_client import make_request_options +from .....types.agents.evaluation_metrics import oauth2_generate_url_params +from .....types.agents.evaluation_metrics.oauth2_generate_url_response import Oauth2GenerateURLResponse + +__all__ = ["Oauth2Resource", "AsyncOauth2Resource"] + + +class Oauth2Resource(SyncAPIResource): + @cached_property + def dropbox(self) -> DropboxResource: + return DropboxResource(self._client) + + @cached_property + def with_raw_response(self) -> Oauth2ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return Oauth2ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> Oauth2ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return Oauth2ResourceWithStreamingResponse(self) + + def generate_url( + self, + *, + redirect_url: str | Omit = omit, + type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Oauth2GenerateURLResponse: + """ + To generate an Oauth2-URL for use with your localhost, send a GET request to + `/v2/gen-ai/oauth2/url`. Pass 'http://localhost:3000 as redirect_url + + Args: + redirect_url: The redirect url. + + type: Type "google" / "dropbox". + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/oauth2/url" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/oauth2/url", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "redirect_url": redirect_url, + "type": type, + }, + oauth2_generate_url_params.Oauth2GenerateURLParams, + ), + ), + cast_to=Oauth2GenerateURLResponse, + ) + + +class AsyncOauth2Resource(AsyncAPIResource): + @cached_property + def dropbox(self) -> AsyncDropboxResource: + return AsyncDropboxResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncOauth2ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncOauth2ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOauth2ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncOauth2ResourceWithStreamingResponse(self) + + async def generate_url( + self, + *, + redirect_url: str | Omit = omit, + type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Oauth2GenerateURLResponse: + """ + To generate an Oauth2-URL for use with your localhost, send a GET request to + `/v2/gen-ai/oauth2/url`. Pass 'http://localhost:3000 as redirect_url + + Args: + redirect_url: The redirect url. + + type: Type "google" / "dropbox". + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/oauth2/url" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/oauth2/url", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "redirect_url": redirect_url, + "type": type, + }, + oauth2_generate_url_params.Oauth2GenerateURLParams, + ), + ), + cast_to=Oauth2GenerateURLResponse, + ) + + +class Oauth2ResourceWithRawResponse: + def __init__(self, oauth2: Oauth2Resource) -> None: + self._oauth2 = oauth2 + + self.generate_url = to_raw_response_wrapper( + oauth2.generate_url, + ) + + @cached_property + def dropbox(self) -> DropboxResourceWithRawResponse: + return DropboxResourceWithRawResponse(self._oauth2.dropbox) + + +class AsyncOauth2ResourceWithRawResponse: + def __init__(self, oauth2: AsyncOauth2Resource) -> None: + self._oauth2 = oauth2 + + self.generate_url = async_to_raw_response_wrapper( + oauth2.generate_url, + ) + + @cached_property + def dropbox(self) -> AsyncDropboxResourceWithRawResponse: + return AsyncDropboxResourceWithRawResponse(self._oauth2.dropbox) + + +class Oauth2ResourceWithStreamingResponse: + def __init__(self, oauth2: Oauth2Resource) -> None: + self._oauth2 = oauth2 + + self.generate_url = to_streamed_response_wrapper( + oauth2.generate_url, + ) + + @cached_property + def dropbox(self) -> DropboxResourceWithStreamingResponse: + return DropboxResourceWithStreamingResponse(self._oauth2.dropbox) + + +class AsyncOauth2ResourceWithStreamingResponse: + def __init__(self, oauth2: AsyncOauth2Resource) -> None: + self._oauth2 = oauth2 + + self.generate_url = async_to_streamed_response_wrapper( + oauth2.generate_url, + ) + + @cached_property + def dropbox(self) -> AsyncDropboxResourceWithStreamingResponse: + return AsyncDropboxResourceWithStreamingResponse(self._oauth2.dropbox) diff --git a/src/gradient/resources/agents/evaluation_metrics/openai/__init__.py b/src/gradient/resources/agents/evaluation_metrics/openai/__init__.py new file mode 100644 index 00000000..66d8ca7a --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/openai/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .keys import ( + KeysResource, + AsyncKeysResource, + KeysResourceWithRawResponse, + AsyncKeysResourceWithRawResponse, + KeysResourceWithStreamingResponse, + AsyncKeysResourceWithStreamingResponse, +) +from .openai import ( + OpenAIResource, + AsyncOpenAIResource, + OpenAIResourceWithRawResponse, + AsyncOpenAIResourceWithRawResponse, + OpenAIResourceWithStreamingResponse, + AsyncOpenAIResourceWithStreamingResponse, +) + +__all__ = [ + "KeysResource", + "AsyncKeysResource", + "KeysResourceWithRawResponse", + "AsyncKeysResourceWithRawResponse", + "KeysResourceWithStreamingResponse", + "AsyncKeysResourceWithStreamingResponse", + "OpenAIResource", + "AsyncOpenAIResource", + "OpenAIResourceWithRawResponse", + "AsyncOpenAIResourceWithRawResponse", + "OpenAIResourceWithStreamingResponse", + "AsyncOpenAIResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/agents/evaluation_metrics/openai/keys.py b/src/gradient/resources/agents/evaluation_metrics/openai/keys.py new file mode 100644 index 00000000..9ab5cbad --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/openai/keys.py @@ -0,0 +1,707 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....._base_client import make_request_options +from .....types.agents.evaluation_metrics.openai import ( + key_list_params, + key_create_params, + key_update_params, + key_list_agents_params, +) +from .....types.agents.evaluation_metrics.openai.key_list_response import KeyListResponse +from .....types.agents.evaluation_metrics.openai.key_create_response import KeyCreateResponse +from .....types.agents.evaluation_metrics.openai.key_delete_response import KeyDeleteResponse +from .....types.agents.evaluation_metrics.openai.key_update_response import KeyUpdateResponse +from .....types.agents.evaluation_metrics.openai.key_retrieve_response import KeyRetrieveResponse +from .....types.agents.evaluation_metrics.openai.key_list_agents_response import KeyListAgentsResponse + +__all__ = ["KeysResource", "AsyncKeysResource"] + + +class KeysResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> KeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return KeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> KeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return KeysResourceWithStreamingResponse(self) + + def create( + self, + *, + api_key: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyCreateResponse: + """ + To create an OpenAI API key, send a POST request to `/v2/gen-ai/openai/keys`. + + Args: + api_key: OpenAI API key + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/openai/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/openai/keys", + body=maybe_transform( + { + "api_key": api_key, + "name": name, + }, + key_create_params.KeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyCreateResponse, + ) + + def retrieve( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyRetrieveResponse: + """ + To retrieve details of an OpenAI API key, send a GET request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._get( + f"/v2/gen-ai/openai/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyRetrieveResponse, + ) + + def update( + self, + path_api_key_uuid: str, + *, + api_key: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyUpdateResponse: + """ + To update an OpenAI API key, send a PUT request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + api_key: OpenAI API key + + body_api_key_uuid: API key ID + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return self._put( + f"/v2/gen-ai/openai/keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{path_api_key_uuid}", + body=maybe_transform( + { + "api_key": api_key, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + key_update_params.KeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListResponse: + """ + To list all OpenAI API keys, send a GET request to `/v2/gen-ai/openai/keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/openai/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/openai/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_params.KeyListParams, + ), + ), + cast_to=KeyListResponse, + ) + + def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyDeleteResponse: + """ + To delete an OpenAI API key, send a DELETE request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._delete( + f"/v2/gen-ai/openai/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyDeleteResponse, + ) + + def list_agents( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListAgentsResponse: + """ + List Agents by OpenAI Key. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/openai/keys/{uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_agents_params.KeyListAgentsParams, + ), + ), + cast_to=KeyListAgentsResponse, + ) + + +class AsyncKeysResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncKeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncKeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncKeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncKeysResourceWithStreamingResponse(self) + + async def create( + self, + *, + api_key: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyCreateResponse: + """ + To create an OpenAI API key, send a POST request to `/v2/gen-ai/openai/keys`. + + Args: + api_key: OpenAI API key + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/openai/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/openai/keys", + body=await async_maybe_transform( + { + "api_key": api_key, + "name": name, + }, + key_create_params.KeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyCreateResponse, + ) + + async def retrieve( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyRetrieveResponse: + """ + To retrieve details of an OpenAI API key, send a GET request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._get( + f"/v2/gen-ai/openai/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyRetrieveResponse, + ) + + async def update( + self, + path_api_key_uuid: str, + *, + api_key: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyUpdateResponse: + """ + To update an OpenAI API key, send a PUT request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + api_key: OpenAI API key + + body_api_key_uuid: API key ID + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return await self._put( + f"/v2/gen-ai/openai/keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{path_api_key_uuid}", + body=await async_maybe_transform( + { + "api_key": api_key, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + key_update_params.KeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListResponse: + """ + To list all OpenAI API keys, send a GET request to `/v2/gen-ai/openai/keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/openai/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/openai/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_params.KeyListParams, + ), + ), + cast_to=KeyListResponse, + ) + + async def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyDeleteResponse: + """ + To delete an OpenAI API key, send a DELETE request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._delete( + f"/v2/gen-ai/openai/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyDeleteResponse, + ) + + async def list_agents( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListAgentsResponse: + """ + List Agents by OpenAI Key. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/openai/keys/{uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_agents_params.KeyListAgentsParams, + ), + ), + cast_to=KeyListAgentsResponse, + ) + + +class KeysResourceWithRawResponse: + def __init__(self, keys: KeysResource) -> None: + self._keys = keys + + self.create = to_raw_response_wrapper( + keys.create, + ) + self.retrieve = to_raw_response_wrapper( + keys.retrieve, + ) + self.update = to_raw_response_wrapper( + keys.update, + ) + self.list = to_raw_response_wrapper( + keys.list, + ) + self.delete = to_raw_response_wrapper( + keys.delete, + ) + self.list_agents = to_raw_response_wrapper( + keys.list_agents, + ) + + +class AsyncKeysResourceWithRawResponse: + def __init__(self, keys: AsyncKeysResource) -> None: + self._keys = keys + + self.create = async_to_raw_response_wrapper( + keys.create, + ) + self.retrieve = async_to_raw_response_wrapper( + keys.retrieve, + ) + self.update = async_to_raw_response_wrapper( + keys.update, + ) + self.list = async_to_raw_response_wrapper( + keys.list, + ) + self.delete = async_to_raw_response_wrapper( + keys.delete, + ) + self.list_agents = async_to_raw_response_wrapper( + keys.list_agents, + ) + + +class KeysResourceWithStreamingResponse: + def __init__(self, keys: KeysResource) -> None: + self._keys = keys + + self.create = to_streamed_response_wrapper( + keys.create, + ) + self.retrieve = to_streamed_response_wrapper( + keys.retrieve, + ) + self.update = to_streamed_response_wrapper( + keys.update, + ) + self.list = to_streamed_response_wrapper( + keys.list, + ) + self.delete = to_streamed_response_wrapper( + keys.delete, + ) + self.list_agents = to_streamed_response_wrapper( + keys.list_agents, + ) + + +class AsyncKeysResourceWithStreamingResponse: + def __init__(self, keys: AsyncKeysResource) -> None: + self._keys = keys + + self.create = async_to_streamed_response_wrapper( + keys.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + keys.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + keys.update, + ) + self.list = async_to_streamed_response_wrapper( + keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + keys.delete, + ) + self.list_agents = async_to_streamed_response_wrapper( + keys.list_agents, + ) diff --git a/src/gradient/resources/agents/evaluation_metrics/openai/openai.py b/src/gradient/resources/agents/evaluation_metrics/openai/openai.py new file mode 100644 index 00000000..00fd8a7d --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/openai/openai.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .keys import ( + KeysResource, + AsyncKeysResource, + KeysResourceWithRawResponse, + AsyncKeysResourceWithRawResponse, + KeysResourceWithStreamingResponse, + AsyncKeysResourceWithStreamingResponse, +) +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["OpenAIResource", "AsyncOpenAIResource"] + + +class OpenAIResource(SyncAPIResource): + @cached_property + def keys(self) -> KeysResource: + return KeysResource(self._client) + + @cached_property + def with_raw_response(self) -> OpenAIResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return OpenAIResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OpenAIResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return OpenAIResourceWithStreamingResponse(self) + + +class AsyncOpenAIResource(AsyncAPIResource): + @cached_property + def keys(self) -> AsyncKeysResource: + return AsyncKeysResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncOpenAIResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncOpenAIResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOpenAIResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncOpenAIResourceWithStreamingResponse(self) + + +class OpenAIResourceWithRawResponse: + def __init__(self, openai: OpenAIResource) -> None: + self._openai = openai + + @cached_property + def keys(self) -> KeysResourceWithRawResponse: + return KeysResourceWithRawResponse(self._openai.keys) + + +class AsyncOpenAIResourceWithRawResponse: + def __init__(self, openai: AsyncOpenAIResource) -> None: + self._openai = openai + + @cached_property + def keys(self) -> AsyncKeysResourceWithRawResponse: + return AsyncKeysResourceWithRawResponse(self._openai.keys) + + +class OpenAIResourceWithStreamingResponse: + def __init__(self, openai: OpenAIResource) -> None: + self._openai = openai + + @cached_property + def keys(self) -> KeysResourceWithStreamingResponse: + return KeysResourceWithStreamingResponse(self._openai.keys) + + +class AsyncOpenAIResourceWithStreamingResponse: + def __init__(self, openai: AsyncOpenAIResource) -> None: + self._openai = openai + + @cached_property + def keys(self) -> AsyncKeysResourceWithStreamingResponse: + return AsyncKeysResourceWithStreamingResponse(self._openai.keys) diff --git a/src/gradient/resources/agents/evaluation_metrics/scheduled_indexing.py b/src/gradient/resources/agents/evaluation_metrics/scheduled_indexing.py new file mode 100644 index 00000000..e346f7ae --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/scheduled_indexing.py @@ -0,0 +1,377 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.agents.evaluation_metrics import scheduled_indexing_create_params +from ....types.agents.evaluation_metrics.scheduled_indexing_create_response import ScheduledIndexingCreateResponse +from ....types.agents.evaluation_metrics.scheduled_indexing_delete_response import ScheduledIndexingDeleteResponse +from ....types.agents.evaluation_metrics.scheduled_indexing_retrieve_response import ScheduledIndexingRetrieveResponse + +__all__ = ["ScheduledIndexingResource", "AsyncScheduledIndexingResource"] + + +class ScheduledIndexingResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ScheduledIndexingResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ScheduledIndexingResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ScheduledIndexingResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ScheduledIndexingResourceWithStreamingResponse(self) + + def create( + self, + *, + days: Iterable[int] | Omit = omit, + knowledge_base_uuid: str | Omit = omit, + time: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScheduledIndexingCreateResponse: + """ + To create scheduled indexing for a knowledge base, send a POST request to + `/v2/gen-ai/scheduled-indexing`. + + Args: + days: Days for execution (day is represented same as in a cron expression, e.g. Monday + begins with 1 ) + + knowledge_base_uuid: Knowledge base uuid for which the schedule is created + + time: Time of execution (HH:MM) UTC + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/scheduled-indexing" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/scheduled-indexing", + body=maybe_transform( + { + "days": days, + "knowledge_base_uuid": knowledge_base_uuid, + "time": time, + }, + scheduled_indexing_create_params.ScheduledIndexingCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScheduledIndexingCreateResponse, + ) + + def retrieve( + self, + knowledge_base_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScheduledIndexingRetrieveResponse: + """ + Get Scheduled Indexing for knowledge base using knoweldge base uuid, send a GET + request to `/v2/gen-ai/scheduled-indexing/knowledge-base/{knowledge_base_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return self._get( + f"/v2/gen-ai/scheduled-indexing/knowledge-base/{knowledge_base_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/scheduled-indexing/knowledge-base/{knowledge_base_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScheduledIndexingRetrieveResponse, + ) + + def delete( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScheduledIndexingDeleteResponse: + """ + Delete Scheduled Indexing for knowledge base, send a DELETE request to + `/v2/gen-ai/scheduled-indexing/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._delete( + f"/v2/gen-ai/scheduled-indexing/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/scheduled-indexing/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScheduledIndexingDeleteResponse, + ) + + +class AsyncScheduledIndexingResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncScheduledIndexingResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncScheduledIndexingResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncScheduledIndexingResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncScheduledIndexingResourceWithStreamingResponse(self) + + async def create( + self, + *, + days: Iterable[int] | Omit = omit, + knowledge_base_uuid: str | Omit = omit, + time: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScheduledIndexingCreateResponse: + """ + To create scheduled indexing for a knowledge base, send a POST request to + `/v2/gen-ai/scheduled-indexing`. + + Args: + days: Days for execution (day is represented same as in a cron expression, e.g. Monday + begins with 1 ) + + knowledge_base_uuid: Knowledge base uuid for which the schedule is created + + time: Time of execution (HH:MM) UTC + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/scheduled-indexing" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/scheduled-indexing", + body=await async_maybe_transform( + { + "days": days, + "knowledge_base_uuid": knowledge_base_uuid, + "time": time, + }, + scheduled_indexing_create_params.ScheduledIndexingCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScheduledIndexingCreateResponse, + ) + + async def retrieve( + self, + knowledge_base_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScheduledIndexingRetrieveResponse: + """ + Get Scheduled Indexing for knowledge base using knoweldge base uuid, send a GET + request to `/v2/gen-ai/scheduled-indexing/knowledge-base/{knowledge_base_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return await self._get( + f"/v2/gen-ai/scheduled-indexing/knowledge-base/{knowledge_base_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/scheduled-indexing/knowledge-base/{knowledge_base_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScheduledIndexingRetrieveResponse, + ) + + async def delete( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScheduledIndexingDeleteResponse: + """ + Delete Scheduled Indexing for knowledge base, send a DELETE request to + `/v2/gen-ai/scheduled-indexing/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._delete( + f"/v2/gen-ai/scheduled-indexing/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/scheduled-indexing/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScheduledIndexingDeleteResponse, + ) + + +class ScheduledIndexingResourceWithRawResponse: + def __init__(self, scheduled_indexing: ScheduledIndexingResource) -> None: + self._scheduled_indexing = scheduled_indexing + + self.create = to_raw_response_wrapper( + scheduled_indexing.create, + ) + self.retrieve = to_raw_response_wrapper( + scheduled_indexing.retrieve, + ) + self.delete = to_raw_response_wrapper( + scheduled_indexing.delete, + ) + + +class AsyncScheduledIndexingResourceWithRawResponse: + def __init__(self, scheduled_indexing: AsyncScheduledIndexingResource) -> None: + self._scheduled_indexing = scheduled_indexing + + self.create = async_to_raw_response_wrapper( + scheduled_indexing.create, + ) + self.retrieve = async_to_raw_response_wrapper( + scheduled_indexing.retrieve, + ) + self.delete = async_to_raw_response_wrapper( + scheduled_indexing.delete, + ) + + +class ScheduledIndexingResourceWithStreamingResponse: + def __init__(self, scheduled_indexing: ScheduledIndexingResource) -> None: + self._scheduled_indexing = scheduled_indexing + + self.create = to_streamed_response_wrapper( + scheduled_indexing.create, + ) + self.retrieve = to_streamed_response_wrapper( + scheduled_indexing.retrieve, + ) + self.delete = to_streamed_response_wrapper( + scheduled_indexing.delete, + ) + + +class AsyncScheduledIndexingResourceWithStreamingResponse: + def __init__(self, scheduled_indexing: AsyncScheduledIndexingResource) -> None: + self._scheduled_indexing = scheduled_indexing + + self.create = async_to_streamed_response_wrapper( + scheduled_indexing.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + scheduled_indexing.retrieve, + ) + self.delete = async_to_streamed_response_wrapper( + scheduled_indexing.delete, + ) diff --git a/src/gradient/resources/agents/evaluation_metrics/workspaces/__init__.py b/src/gradient/resources/agents/evaluation_metrics/workspaces/__init__.py new file mode 100644 index 00000000..79d75f90 --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/workspaces/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .agents import ( + AgentsResource, + AsyncAgentsResource, + AgentsResourceWithRawResponse, + AsyncAgentsResourceWithRawResponse, + AgentsResourceWithStreamingResponse, + AsyncAgentsResourceWithStreamingResponse, +) +from .workspaces import ( + WorkspacesResource, + AsyncWorkspacesResource, + WorkspacesResourceWithRawResponse, + AsyncWorkspacesResourceWithRawResponse, + WorkspacesResourceWithStreamingResponse, + AsyncWorkspacesResourceWithStreamingResponse, +) + +__all__ = [ + "AgentsResource", + "AsyncAgentsResource", + "AgentsResourceWithRawResponse", + "AsyncAgentsResourceWithRawResponse", + "AgentsResourceWithStreamingResponse", + "AsyncAgentsResourceWithStreamingResponse", + "WorkspacesResource", + "AsyncWorkspacesResource", + "WorkspacesResourceWithRawResponse", + "AsyncWorkspacesResourceWithRawResponse", + "WorkspacesResourceWithStreamingResponse", + "AsyncWorkspacesResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/agents/evaluation_metrics/workspaces/agents.py b/src/gradient/resources/agents/evaluation_metrics/workspaces/agents.py new file mode 100644 index 00000000..7f9a766a --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/workspaces/agents.py @@ -0,0 +1,326 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....._base_client import make_request_options +from .....types.agents.evaluation_metrics.workspaces import agent_list_params, agent_move_params +from .....types.agents.evaluation_metrics.workspaces.agent_list_response import AgentListResponse +from .....types.agents.evaluation_metrics.workspaces.agent_move_response import AgentMoveResponse + +__all__ = ["AgentsResource", "AsyncAgentsResource"] + + +class AgentsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AgentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AgentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AgentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AgentsResourceWithStreamingResponse(self) + + def list( + self, + workspace_uuid: str, + *, + only_deployed: bool | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentListResponse: + """ + To list all agents by a Workspace, send a GET request to + `/v2/gen-ai/workspaces/{workspace_uuid}/agents`. + + Args: + only_deployed: Only list agents that are deployed. + + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_uuid: + raise ValueError(f"Expected a non-empty value for `workspace_uuid` but received {workspace_uuid!r}") + return self._get( + f"/v2/gen-ai/workspaces/{workspace_uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{workspace_uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "only_deployed": only_deployed, + "page": page, + "per_page": per_page, + }, + agent_list_params.AgentListParams, + ), + ), + cast_to=AgentListResponse, + ) + + def move( + self, + path_workspace_uuid: str, + *, + agent_uuids: SequenceNotStr[str] | Omit = omit, + body_workspace_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentMoveResponse: + """ + To move all listed agents a given workspace, send a PUT request to + `/v2/gen-ai/workspaces/{workspace_uuid}/agents`. + + Args: + agent_uuids: Agent uuids + + body_workspace_uuid: Workspace uuid to move agents to + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_workspace_uuid: + raise ValueError( + f"Expected a non-empty value for `path_workspace_uuid` but received {path_workspace_uuid!r}" + ) + return self._put( + f"/v2/gen-ai/workspaces/{path_workspace_uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{path_workspace_uuid}/agents", + body=maybe_transform( + { + "agent_uuids": agent_uuids, + "body_workspace_uuid": body_workspace_uuid, + }, + agent_move_params.AgentMoveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentMoveResponse, + ) + + +class AsyncAgentsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAgentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncAgentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAgentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncAgentsResourceWithStreamingResponse(self) + + async def list( + self, + workspace_uuid: str, + *, + only_deployed: bool | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentListResponse: + """ + To list all agents by a Workspace, send a GET request to + `/v2/gen-ai/workspaces/{workspace_uuid}/agents`. + + Args: + only_deployed: Only list agents that are deployed. + + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_uuid: + raise ValueError(f"Expected a non-empty value for `workspace_uuid` but received {workspace_uuid!r}") + return await self._get( + f"/v2/gen-ai/workspaces/{workspace_uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{workspace_uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "only_deployed": only_deployed, + "page": page, + "per_page": per_page, + }, + agent_list_params.AgentListParams, + ), + ), + cast_to=AgentListResponse, + ) + + async def move( + self, + path_workspace_uuid: str, + *, + agent_uuids: SequenceNotStr[str] | Omit = omit, + body_workspace_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentMoveResponse: + """ + To move all listed agents a given workspace, send a PUT request to + `/v2/gen-ai/workspaces/{workspace_uuid}/agents`. + + Args: + agent_uuids: Agent uuids + + body_workspace_uuid: Workspace uuid to move agents to + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_workspace_uuid: + raise ValueError( + f"Expected a non-empty value for `path_workspace_uuid` but received {path_workspace_uuid!r}" + ) + return await self._put( + f"/v2/gen-ai/workspaces/{path_workspace_uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{path_workspace_uuid}/agents", + body=await async_maybe_transform( + { + "agent_uuids": agent_uuids, + "body_workspace_uuid": body_workspace_uuid, + }, + agent_move_params.AgentMoveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AgentMoveResponse, + ) + + +class AgentsResourceWithRawResponse: + def __init__(self, agents: AgentsResource) -> None: + self._agents = agents + + self.list = to_raw_response_wrapper( + agents.list, + ) + self.move = to_raw_response_wrapper( + agents.move, + ) + + +class AsyncAgentsResourceWithRawResponse: + def __init__(self, agents: AsyncAgentsResource) -> None: + self._agents = agents + + self.list = async_to_raw_response_wrapper( + agents.list, + ) + self.move = async_to_raw_response_wrapper( + agents.move, + ) + + +class AgentsResourceWithStreamingResponse: + def __init__(self, agents: AgentsResource) -> None: + self._agents = agents + + self.list = to_streamed_response_wrapper( + agents.list, + ) + self.move = to_streamed_response_wrapper( + agents.move, + ) + + +class AsyncAgentsResourceWithStreamingResponse: + def __init__(self, agents: AsyncAgentsResource) -> None: + self._agents = agents + + self.list = async_to_streamed_response_wrapper( + agents.list, + ) + self.move = async_to_streamed_response_wrapper( + agents.move, + ) diff --git a/src/gradient/resources/agents/evaluation_metrics/workspaces/workspaces.py b/src/gradient/resources/agents/evaluation_metrics/workspaces/workspaces.py new file mode 100644 index 00000000..73539bbd --- /dev/null +++ b/src/gradient/resources/agents/evaluation_metrics/workspaces/workspaces.py @@ -0,0 +1,672 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .agents import ( + AgentsResource, + AsyncAgentsResource, + AgentsResourceWithRawResponse, + AsyncAgentsResourceWithRawResponse, + AgentsResourceWithStreamingResponse, + AsyncAgentsResourceWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....._base_client import make_request_options +from .....types.agents.evaluation_metrics import workspace_create_params, workspace_update_params +from .....types.agents.evaluation_metrics.workspace_list_response import WorkspaceListResponse +from .....types.agents.evaluation_metrics.workspace_create_response import WorkspaceCreateResponse +from .....types.agents.evaluation_metrics.workspace_delete_response import WorkspaceDeleteResponse +from .....types.agents.evaluation_metrics.workspace_update_response import WorkspaceUpdateResponse +from .....types.agents.evaluation_metrics.workspace_retrieve_response import WorkspaceRetrieveResponse +from .....types.agents.evaluation_metrics.workspace_list_evaluation_test_cases_response import ( + WorkspaceListEvaluationTestCasesResponse, +) + +__all__ = ["WorkspacesResource", "AsyncWorkspacesResource"] + + +class WorkspacesResource(SyncAPIResource): + @cached_property + def agents(self) -> AgentsResource: + return AgentsResource(self._client) + + @cached_property + def with_raw_response(self) -> WorkspacesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return WorkspacesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> WorkspacesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return WorkspacesResourceWithStreamingResponse(self) + + def create( + self, + *, + agent_uuids: SequenceNotStr[str] | Omit = omit, + description: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceCreateResponse: + """To create a new workspace, send a POST request to `/v2/gen-ai/workspaces`. + + The + response body contains a JSON object with the newly created workspace object. + + Args: + agent_uuids: Ids of the agents(s) to attach to the workspace + + description: Description of the workspace + + name: Name of the workspace + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/workspaces" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/workspaces", + body=maybe_transform( + { + "agent_uuids": agent_uuids, + "description": description, + "name": name, + }, + workspace_create_params.WorkspaceCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceCreateResponse, + ) + + def retrieve( + self, + workspace_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceRetrieveResponse: + """ + To retrieve details of a workspace, GET request to + `/v2/gen-ai/workspaces/{workspace_uuid}`. The response body is a JSON object + containing the workspace. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_uuid: + raise ValueError(f"Expected a non-empty value for `workspace_uuid` but received {workspace_uuid!r}") + return self._get( + f"/v2/gen-ai/workspaces/{workspace_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{workspace_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceRetrieveResponse, + ) + + def update( + self, + path_workspace_uuid: str, + *, + description: str | Omit = omit, + name: str | Omit = omit, + body_workspace_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceUpdateResponse: + """ + To update a workspace, send a PUT request to + `/v2/gen-ai/workspaces/{workspace_uuid}`. The response body is a JSON object + containing the workspace. + + Args: + description: The new description of the workspace + + name: The new name of the workspace + + body_workspace_uuid: Workspace UUID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_workspace_uuid: + raise ValueError( + f"Expected a non-empty value for `path_workspace_uuid` but received {path_workspace_uuid!r}" + ) + return self._put( + f"/v2/gen-ai/workspaces/{path_workspace_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{path_workspace_uuid}", + body=maybe_transform( + { + "description": description, + "name": name, + "body_workspace_uuid": body_workspace_uuid, + }, + workspace_update_params.WorkspaceUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceUpdateResponse, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceListResponse: + """To list all workspaces, send a GET request to `/v2/gen-ai/workspaces`.""" + return self._get( + "/v2/gen-ai/workspaces" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/workspaces", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceListResponse, + ) + + def delete( + self, + workspace_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceDeleteResponse: + """ + To delete a workspace, send a DELETE request to + `/v2/gen-ai/workspace/{workspace_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_uuid: + raise ValueError(f"Expected a non-empty value for `workspace_uuid` but received {workspace_uuid!r}") + return self._delete( + f"/v2/gen-ai/workspaces/{workspace_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{workspace_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceDeleteResponse, + ) + + def list_evaluation_test_cases( + self, + workspace_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceListEvaluationTestCasesResponse: + """ + To list all evaluation test cases by a workspace, send a GET request to + `/v2/gen-ai/workspaces/{workspace_uuid}/evaluation_test_cases`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_uuid: + raise ValueError(f"Expected a non-empty value for `workspace_uuid` but received {workspace_uuid!r}") + return self._get( + f"/v2/gen-ai/workspaces/{workspace_uuid}/evaluation_test_cases" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{workspace_uuid}/evaluation_test_cases", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceListEvaluationTestCasesResponse, + ) + + +class AsyncWorkspacesResource(AsyncAPIResource): + @cached_property + def agents(self) -> AsyncAgentsResource: + return AsyncAgentsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncWorkspacesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncWorkspacesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncWorkspacesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncWorkspacesResourceWithStreamingResponse(self) + + async def create( + self, + *, + agent_uuids: SequenceNotStr[str] | Omit = omit, + description: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceCreateResponse: + """To create a new workspace, send a POST request to `/v2/gen-ai/workspaces`. + + The + response body contains a JSON object with the newly created workspace object. + + Args: + agent_uuids: Ids of the agents(s) to attach to the workspace + + description: Description of the workspace + + name: Name of the workspace + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/workspaces" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/workspaces", + body=await async_maybe_transform( + { + "agent_uuids": agent_uuids, + "description": description, + "name": name, + }, + workspace_create_params.WorkspaceCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceCreateResponse, + ) + + async def retrieve( + self, + workspace_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceRetrieveResponse: + """ + To retrieve details of a workspace, GET request to + `/v2/gen-ai/workspaces/{workspace_uuid}`. The response body is a JSON object + containing the workspace. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_uuid: + raise ValueError(f"Expected a non-empty value for `workspace_uuid` but received {workspace_uuid!r}") + return await self._get( + f"/v2/gen-ai/workspaces/{workspace_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{workspace_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceRetrieveResponse, + ) + + async def update( + self, + path_workspace_uuid: str, + *, + description: str | Omit = omit, + name: str | Omit = omit, + body_workspace_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceUpdateResponse: + """ + To update a workspace, send a PUT request to + `/v2/gen-ai/workspaces/{workspace_uuid}`. The response body is a JSON object + containing the workspace. + + Args: + description: The new description of the workspace + + name: The new name of the workspace + + body_workspace_uuid: Workspace UUID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_workspace_uuid: + raise ValueError( + f"Expected a non-empty value for `path_workspace_uuid` but received {path_workspace_uuid!r}" + ) + return await self._put( + f"/v2/gen-ai/workspaces/{path_workspace_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{path_workspace_uuid}", + body=await async_maybe_transform( + { + "description": description, + "name": name, + "body_workspace_uuid": body_workspace_uuid, + }, + workspace_update_params.WorkspaceUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceUpdateResponse, + ) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceListResponse: + """To list all workspaces, send a GET request to `/v2/gen-ai/workspaces`.""" + return await self._get( + "/v2/gen-ai/workspaces" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/workspaces", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceListResponse, + ) + + async def delete( + self, + workspace_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceDeleteResponse: + """ + To delete a workspace, send a DELETE request to + `/v2/gen-ai/workspace/{workspace_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_uuid: + raise ValueError(f"Expected a non-empty value for `workspace_uuid` but received {workspace_uuid!r}") + return await self._delete( + f"/v2/gen-ai/workspaces/{workspace_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{workspace_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceDeleteResponse, + ) + + async def list_evaluation_test_cases( + self, + workspace_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceListEvaluationTestCasesResponse: + """ + To list all evaluation test cases by a workspace, send a GET request to + `/v2/gen-ai/workspaces/{workspace_uuid}/evaluation_test_cases`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_uuid: + raise ValueError(f"Expected a non-empty value for `workspace_uuid` but received {workspace_uuid!r}") + return await self._get( + f"/v2/gen-ai/workspaces/{workspace_uuid}/evaluation_test_cases" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/workspaces/{workspace_uuid}/evaluation_test_cases", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceListEvaluationTestCasesResponse, + ) + + +class WorkspacesResourceWithRawResponse: + def __init__(self, workspaces: WorkspacesResource) -> None: + self._workspaces = workspaces + + self.create = to_raw_response_wrapper( + workspaces.create, + ) + self.retrieve = to_raw_response_wrapper( + workspaces.retrieve, + ) + self.update = to_raw_response_wrapper( + workspaces.update, + ) + self.list = to_raw_response_wrapper( + workspaces.list, + ) + self.delete = to_raw_response_wrapper( + workspaces.delete, + ) + self.list_evaluation_test_cases = to_raw_response_wrapper( + workspaces.list_evaluation_test_cases, + ) + + @cached_property + def agents(self) -> AgentsResourceWithRawResponse: + return AgentsResourceWithRawResponse(self._workspaces.agents) + + +class AsyncWorkspacesResourceWithRawResponse: + def __init__(self, workspaces: AsyncWorkspacesResource) -> None: + self._workspaces = workspaces + + self.create = async_to_raw_response_wrapper( + workspaces.create, + ) + self.retrieve = async_to_raw_response_wrapper( + workspaces.retrieve, + ) + self.update = async_to_raw_response_wrapper( + workspaces.update, + ) + self.list = async_to_raw_response_wrapper( + workspaces.list, + ) + self.delete = async_to_raw_response_wrapper( + workspaces.delete, + ) + self.list_evaluation_test_cases = async_to_raw_response_wrapper( + workspaces.list_evaluation_test_cases, + ) + + @cached_property + def agents(self) -> AsyncAgentsResourceWithRawResponse: + return AsyncAgentsResourceWithRawResponse(self._workspaces.agents) + + +class WorkspacesResourceWithStreamingResponse: + def __init__(self, workspaces: WorkspacesResource) -> None: + self._workspaces = workspaces + + self.create = to_streamed_response_wrapper( + workspaces.create, + ) + self.retrieve = to_streamed_response_wrapper( + workspaces.retrieve, + ) + self.update = to_streamed_response_wrapper( + workspaces.update, + ) + self.list = to_streamed_response_wrapper( + workspaces.list, + ) + self.delete = to_streamed_response_wrapper( + workspaces.delete, + ) + self.list_evaluation_test_cases = to_streamed_response_wrapper( + workspaces.list_evaluation_test_cases, + ) + + @cached_property + def agents(self) -> AgentsResourceWithStreamingResponse: + return AgentsResourceWithStreamingResponse(self._workspaces.agents) + + +class AsyncWorkspacesResourceWithStreamingResponse: + def __init__(self, workspaces: AsyncWorkspacesResource) -> None: + self._workspaces = workspaces + + self.create = async_to_streamed_response_wrapper( + workspaces.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + workspaces.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + workspaces.update, + ) + self.list = async_to_streamed_response_wrapper( + workspaces.list, + ) + self.delete = async_to_streamed_response_wrapper( + workspaces.delete, + ) + self.list_evaluation_test_cases = async_to_streamed_response_wrapper( + workspaces.list_evaluation_test_cases, + ) + + @cached_property + def agents(self) -> AsyncAgentsResourceWithStreamingResponse: + return AsyncAgentsResourceWithStreamingResponse(self._workspaces.agents) diff --git a/src/gradient/resources/agents/evaluation_runs.py b/src/gradient/resources/agents/evaluation_runs.py new file mode 100644 index 00000000..8506b00f --- /dev/null +++ b/src/gradient/resources/agents/evaluation_runs.py @@ -0,0 +1,500 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.agents import evaluation_run_create_params, evaluation_run_list_results_params +from ...types.agents.evaluation_run_create_response import EvaluationRunCreateResponse +from ...types.agents.evaluation_run_retrieve_response import EvaluationRunRetrieveResponse +from ...types.agents.evaluation_run_list_results_response import EvaluationRunListResultsResponse +from ...types.agents.evaluation_run_retrieve_results_response import EvaluationRunRetrieveResultsResponse + +__all__ = ["EvaluationRunsResource", "AsyncEvaluationRunsResource"] + + +class EvaluationRunsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EvaluationRunsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return EvaluationRunsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EvaluationRunsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return EvaluationRunsResourceWithStreamingResponse(self) + + def create( + self, + *, + agent_uuids: SequenceNotStr[str] | Omit = omit, + run_name: str | Omit = omit, + test_case_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationRunCreateResponse: + """ + To run an evaluation test case, send a POST request to + `/v2/gen-ai/evaluation_runs`. + + Args: + agent_uuids: Agent UUIDs to run the test case against. + + run_name: The name of the run. + + test_case_uuid: Test-case UUID to run + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/evaluation_runs" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_runs", + body=maybe_transform( + { + "agent_uuids": agent_uuids, + "run_name": run_name, + "test_case_uuid": test_case_uuid, + }, + evaluation_run_create_params.EvaluationRunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationRunCreateResponse, + ) + + def retrieve( + self, + evaluation_run_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationRunRetrieveResponse: + """ + To retrive information about an existing evaluation run, send a GET request to + `/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not evaluation_run_uuid: + raise ValueError( + f"Expected a non-empty value for `evaluation_run_uuid` but received {evaluation_run_uuid!r}" + ) + return self._get( + f"/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationRunRetrieveResponse, + ) + + def list_results( + self, + evaluation_run_uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationRunListResultsResponse: + """ + To retrieve results of an evaluation run, send a GET request to + `/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not evaluation_run_uuid: + raise ValueError( + f"Expected a non-empty value for `evaluation_run_uuid` but received {evaluation_run_uuid!r}" + ) + return self._get( + f"/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + evaluation_run_list_results_params.EvaluationRunListResultsParams, + ), + ), + cast_to=EvaluationRunListResultsResponse, + ) + + def retrieve_results( + self, + prompt_id: int, + *, + evaluation_run_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationRunRetrieveResultsResponse: + """ + To retrieve results of an evaluation run, send a GET request to + `/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results/{prompt_id}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not evaluation_run_uuid: + raise ValueError( + f"Expected a non-empty value for `evaluation_run_uuid` but received {evaluation_run_uuid!r}" + ) + return self._get( + f"/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results/{prompt_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results/{prompt_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationRunRetrieveResultsResponse, + ) + + +class AsyncEvaluationRunsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEvaluationRunsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncEvaluationRunsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEvaluationRunsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncEvaluationRunsResourceWithStreamingResponse(self) + + async def create( + self, + *, + agent_uuids: SequenceNotStr[str] | Omit = omit, + run_name: str | Omit = omit, + test_case_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationRunCreateResponse: + """ + To run an evaluation test case, send a POST request to + `/v2/gen-ai/evaluation_runs`. + + Args: + agent_uuids: Agent UUIDs to run the test case against. + + run_name: The name of the run. + + test_case_uuid: Test-case UUID to run + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/evaluation_runs" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_runs", + body=await async_maybe_transform( + { + "agent_uuids": agent_uuids, + "run_name": run_name, + "test_case_uuid": test_case_uuid, + }, + evaluation_run_create_params.EvaluationRunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationRunCreateResponse, + ) + + async def retrieve( + self, + evaluation_run_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationRunRetrieveResponse: + """ + To retrive information about an existing evaluation run, send a GET request to + `/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not evaluation_run_uuid: + raise ValueError( + f"Expected a non-empty value for `evaluation_run_uuid` but received {evaluation_run_uuid!r}" + ) + return await self._get( + f"/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationRunRetrieveResponse, + ) + + async def list_results( + self, + evaluation_run_uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationRunListResultsResponse: + """ + To retrieve results of an evaluation run, send a GET request to + `/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not evaluation_run_uuid: + raise ValueError( + f"Expected a non-empty value for `evaluation_run_uuid` but received {evaluation_run_uuid!r}" + ) + return await self._get( + f"/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + evaluation_run_list_results_params.EvaluationRunListResultsParams, + ), + ), + cast_to=EvaluationRunListResultsResponse, + ) + + async def retrieve_results( + self, + prompt_id: int, + *, + evaluation_run_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationRunRetrieveResultsResponse: + """ + To retrieve results of an evaluation run, send a GET request to + `/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results/{prompt_id}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not evaluation_run_uuid: + raise ValueError( + f"Expected a non-empty value for `evaluation_run_uuid` but received {evaluation_run_uuid!r}" + ) + return await self._get( + f"/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results/{prompt_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_runs/{evaluation_run_uuid}/results/{prompt_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationRunRetrieveResultsResponse, + ) + + +class EvaluationRunsResourceWithRawResponse: + def __init__(self, evaluation_runs: EvaluationRunsResource) -> None: + self._evaluation_runs = evaluation_runs + + self.create = to_raw_response_wrapper( + evaluation_runs.create, + ) + self.retrieve = to_raw_response_wrapper( + evaluation_runs.retrieve, + ) + self.list_results = to_raw_response_wrapper( + evaluation_runs.list_results, + ) + self.retrieve_results = to_raw_response_wrapper( + evaluation_runs.retrieve_results, + ) + + +class AsyncEvaluationRunsResourceWithRawResponse: + def __init__(self, evaluation_runs: AsyncEvaluationRunsResource) -> None: + self._evaluation_runs = evaluation_runs + + self.create = async_to_raw_response_wrapper( + evaluation_runs.create, + ) + self.retrieve = async_to_raw_response_wrapper( + evaluation_runs.retrieve, + ) + self.list_results = async_to_raw_response_wrapper( + evaluation_runs.list_results, + ) + self.retrieve_results = async_to_raw_response_wrapper( + evaluation_runs.retrieve_results, + ) + + +class EvaluationRunsResourceWithStreamingResponse: + def __init__(self, evaluation_runs: EvaluationRunsResource) -> None: + self._evaluation_runs = evaluation_runs + + self.create = to_streamed_response_wrapper( + evaluation_runs.create, + ) + self.retrieve = to_streamed_response_wrapper( + evaluation_runs.retrieve, + ) + self.list_results = to_streamed_response_wrapper( + evaluation_runs.list_results, + ) + self.retrieve_results = to_streamed_response_wrapper( + evaluation_runs.retrieve_results, + ) + + +class AsyncEvaluationRunsResourceWithStreamingResponse: + def __init__(self, evaluation_runs: AsyncEvaluationRunsResource) -> None: + self._evaluation_runs = evaluation_runs + + self.create = async_to_streamed_response_wrapper( + evaluation_runs.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + evaluation_runs.retrieve, + ) + self.list_results = async_to_streamed_response_wrapper( + evaluation_runs.list_results, + ) + self.retrieve_results = async_to_streamed_response_wrapper( + evaluation_runs.retrieve_results, + ) diff --git a/src/gradient/resources/agents/evaluation_test_cases.py b/src/gradient/resources/agents/evaluation_test_cases.py new file mode 100644 index 00000000..d53b8c26 --- /dev/null +++ b/src/gradient/resources/agents/evaluation_test_cases.py @@ -0,0 +1,641 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.agents import ( + evaluation_test_case_create_params, + evaluation_test_case_update_params, + evaluation_test_case_retrieve_params, + evaluation_test_case_list_evaluation_runs_params, +) +from ...types.agents.api_star_metric_param import APIStarMetricParam +from ...types.agents.evaluation_test_case_list_response import EvaluationTestCaseListResponse +from ...types.agents.evaluation_test_case_create_response import EvaluationTestCaseCreateResponse +from ...types.agents.evaluation_test_case_update_response import EvaluationTestCaseUpdateResponse +from ...types.agents.evaluation_test_case_retrieve_response import EvaluationTestCaseRetrieveResponse +from ...types.agents.evaluation_test_case_list_evaluation_runs_response import ( + EvaluationTestCaseListEvaluationRunsResponse, +) + +__all__ = ["EvaluationTestCasesResource", "AsyncEvaluationTestCasesResource"] + + +class EvaluationTestCasesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EvaluationTestCasesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return EvaluationTestCasesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EvaluationTestCasesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return EvaluationTestCasesResourceWithStreamingResponse(self) + + def create( + self, + *, + dataset_uuid: str | Omit = omit, + description: str | Omit = omit, + metrics: SequenceNotStr[str] | Omit = omit, + name: str | Omit = omit, + star_metric: APIStarMetricParam | Omit = omit, + workspace_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseCreateResponse: + """ + To create an evaluation test-case send a POST request to + `/v2/gen-ai/evaluation_test_cases`. + + Args: + dataset_uuid: Dataset against which the test‑case is executed. + + description: Description of the test case. + + metrics: Full metric list to use for evaluation test case. + + name: Name of the test case. + + workspace_uuid: The workspace uuid. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/evaluation_test_cases" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases", + body=maybe_transform( + { + "dataset_uuid": dataset_uuid, + "description": description, + "metrics": metrics, + "name": name, + "star_metric": star_metric, + "workspace_uuid": workspace_uuid, + }, + evaluation_test_case_create_params.EvaluationTestCaseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationTestCaseCreateResponse, + ) + + def retrieve( + self, + test_case_uuid: str, + *, + evaluation_test_case_version: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseRetrieveResponse: + """ + To retrive information about an existing evaluation test case, send a GET + request to `/v2/gen-ai/evaluation_test_case/{test_case_uuid}`. + + Args: + evaluation_test_case_version: Version of the test case. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not test_case_uuid: + raise ValueError(f"Expected a non-empty value for `test_case_uuid` but received {test_case_uuid!r}") + return self._get( + f"/v2/gen-ai/evaluation_test_cases/{test_case_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases/{test_case_uuid}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"evaluation_test_case_version": evaluation_test_case_version}, + evaluation_test_case_retrieve_params.EvaluationTestCaseRetrieveParams, + ), + ), + cast_to=EvaluationTestCaseRetrieveResponse, + ) + + def update( + self, + path_test_case_uuid: str, + *, + dataset_uuid: str | Omit = omit, + description: str | Omit = omit, + metrics: evaluation_test_case_update_params.Metrics | Omit = omit, + name: str | Omit = omit, + star_metric: APIStarMetricParam | Omit = omit, + body_test_case_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseUpdateResponse: + """ + To update an evaluation test-case send a PUT request to + `/v2/gen-ai/evaluation_test_cases/{test_case_uuid}`. + + Args: + dataset_uuid: Dataset against which the test‑case is executed. + + description: Description of the test case. + + name: Name of the test case. + + body_test_case_uuid: Test-case UUID to update + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_test_case_uuid: + raise ValueError( + f"Expected a non-empty value for `path_test_case_uuid` but received {path_test_case_uuid!r}" + ) + return self._put( + f"/v2/gen-ai/evaluation_test_cases/{path_test_case_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases/{path_test_case_uuid}", + body=maybe_transform( + { + "dataset_uuid": dataset_uuid, + "description": description, + "metrics": metrics, + "name": name, + "star_metric": star_metric, + "body_test_case_uuid": body_test_case_uuid, + }, + evaluation_test_case_update_params.EvaluationTestCaseUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationTestCaseUpdateResponse, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseListResponse: + """ + To list all evaluation test cases, send a GET request to + `/v2/gen-ai/evaluation_test_cases`. + """ + return self._get( + "/v2/gen-ai/evaluation_test_cases" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationTestCaseListResponse, + ) + + def list_evaluation_runs( + self, + evaluation_test_case_uuid: str, + *, + evaluation_test_case_version: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseListEvaluationRunsResponse: + """ + To list all evaluation runs by test case, send a GET request to + `/v2/gen-ai/evaluation_test_cases/{evaluation_test_case_uuid}/evaluation_runs`. + + Args: + evaluation_test_case_version: Version of the test case. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not evaluation_test_case_uuid: + raise ValueError( + f"Expected a non-empty value for `evaluation_test_case_uuid` but received {evaluation_test_case_uuid!r}" + ) + return self._get( + f"/v2/gen-ai/evaluation_test_cases/{evaluation_test_case_uuid}/evaluation_runs" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases/{evaluation_test_case_uuid}/evaluation_runs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"evaluation_test_case_version": evaluation_test_case_version}, + evaluation_test_case_list_evaluation_runs_params.EvaluationTestCaseListEvaluationRunsParams, + ), + ), + cast_to=EvaluationTestCaseListEvaluationRunsResponse, + ) + + +class AsyncEvaluationTestCasesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEvaluationTestCasesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncEvaluationTestCasesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEvaluationTestCasesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncEvaluationTestCasesResourceWithStreamingResponse(self) + + async def create( + self, + *, + dataset_uuid: str | Omit = omit, + description: str | Omit = omit, + metrics: SequenceNotStr[str] | Omit = omit, + name: str | Omit = omit, + star_metric: APIStarMetricParam | Omit = omit, + workspace_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseCreateResponse: + """ + To create an evaluation test-case send a POST request to + `/v2/gen-ai/evaluation_test_cases`. + + Args: + dataset_uuid: Dataset against which the test‑case is executed. + + description: Description of the test case. + + metrics: Full metric list to use for evaluation test case. + + name: Name of the test case. + + workspace_uuid: The workspace uuid. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/evaluation_test_cases" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases", + body=await async_maybe_transform( + { + "dataset_uuid": dataset_uuid, + "description": description, + "metrics": metrics, + "name": name, + "star_metric": star_metric, + "workspace_uuid": workspace_uuid, + }, + evaluation_test_case_create_params.EvaluationTestCaseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationTestCaseCreateResponse, + ) + + async def retrieve( + self, + test_case_uuid: str, + *, + evaluation_test_case_version: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseRetrieveResponse: + """ + To retrive information about an existing evaluation test case, send a GET + request to `/v2/gen-ai/evaluation_test_case/{test_case_uuid}`. + + Args: + evaluation_test_case_version: Version of the test case. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not test_case_uuid: + raise ValueError(f"Expected a non-empty value for `test_case_uuid` but received {test_case_uuid!r}") + return await self._get( + f"/v2/gen-ai/evaluation_test_cases/{test_case_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases/{test_case_uuid}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"evaluation_test_case_version": evaluation_test_case_version}, + evaluation_test_case_retrieve_params.EvaluationTestCaseRetrieveParams, + ), + ), + cast_to=EvaluationTestCaseRetrieveResponse, + ) + + async def update( + self, + path_test_case_uuid: str, + *, + dataset_uuid: str | Omit = omit, + description: str | Omit = omit, + metrics: evaluation_test_case_update_params.Metrics | Omit = omit, + name: str | Omit = omit, + star_metric: APIStarMetricParam | Omit = omit, + body_test_case_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseUpdateResponse: + """ + To update an evaluation test-case send a PUT request to + `/v2/gen-ai/evaluation_test_cases/{test_case_uuid}`. + + Args: + dataset_uuid: Dataset against which the test‑case is executed. + + description: Description of the test case. + + name: Name of the test case. + + body_test_case_uuid: Test-case UUID to update + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_test_case_uuid: + raise ValueError( + f"Expected a non-empty value for `path_test_case_uuid` but received {path_test_case_uuid!r}" + ) + return await self._put( + f"/v2/gen-ai/evaluation_test_cases/{path_test_case_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases/{path_test_case_uuid}", + body=await async_maybe_transform( + { + "dataset_uuid": dataset_uuid, + "description": description, + "metrics": metrics, + "name": name, + "star_metric": star_metric, + "body_test_case_uuid": body_test_case_uuid, + }, + evaluation_test_case_update_params.EvaluationTestCaseUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationTestCaseUpdateResponse, + ) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseListResponse: + """ + To list all evaluation test cases, send a GET request to + `/v2/gen-ai/evaluation_test_cases`. + """ + return await self._get( + "/v2/gen-ai/evaluation_test_cases" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvaluationTestCaseListResponse, + ) + + async def list_evaluation_runs( + self, + evaluation_test_case_uuid: str, + *, + evaluation_test_case_version: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluationTestCaseListEvaluationRunsResponse: + """ + To list all evaluation runs by test case, send a GET request to + `/v2/gen-ai/evaluation_test_cases/{evaluation_test_case_uuid}/evaluation_runs`. + + Args: + evaluation_test_case_version: Version of the test case. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not evaluation_test_case_uuid: + raise ValueError( + f"Expected a non-empty value for `evaluation_test_case_uuid` but received {evaluation_test_case_uuid!r}" + ) + return await self._get( + f"/v2/gen-ai/evaluation_test_cases/{evaluation_test_case_uuid}/evaluation_runs" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/evaluation_test_cases/{evaluation_test_case_uuid}/evaluation_runs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"evaluation_test_case_version": evaluation_test_case_version}, + evaluation_test_case_list_evaluation_runs_params.EvaluationTestCaseListEvaluationRunsParams, + ), + ), + cast_to=EvaluationTestCaseListEvaluationRunsResponse, + ) + + +class EvaluationTestCasesResourceWithRawResponse: + def __init__(self, evaluation_test_cases: EvaluationTestCasesResource) -> None: + self._evaluation_test_cases = evaluation_test_cases + + self.create = to_raw_response_wrapper( + evaluation_test_cases.create, + ) + self.retrieve = to_raw_response_wrapper( + evaluation_test_cases.retrieve, + ) + self.update = to_raw_response_wrapper( + evaluation_test_cases.update, + ) + self.list = to_raw_response_wrapper( + evaluation_test_cases.list, + ) + self.list_evaluation_runs = to_raw_response_wrapper( + evaluation_test_cases.list_evaluation_runs, + ) + + +class AsyncEvaluationTestCasesResourceWithRawResponse: + def __init__(self, evaluation_test_cases: AsyncEvaluationTestCasesResource) -> None: + self._evaluation_test_cases = evaluation_test_cases + + self.create = async_to_raw_response_wrapper( + evaluation_test_cases.create, + ) + self.retrieve = async_to_raw_response_wrapper( + evaluation_test_cases.retrieve, + ) + self.update = async_to_raw_response_wrapper( + evaluation_test_cases.update, + ) + self.list = async_to_raw_response_wrapper( + evaluation_test_cases.list, + ) + self.list_evaluation_runs = async_to_raw_response_wrapper( + evaluation_test_cases.list_evaluation_runs, + ) + + +class EvaluationTestCasesResourceWithStreamingResponse: + def __init__(self, evaluation_test_cases: EvaluationTestCasesResource) -> None: + self._evaluation_test_cases = evaluation_test_cases + + self.create = to_streamed_response_wrapper( + evaluation_test_cases.create, + ) + self.retrieve = to_streamed_response_wrapper( + evaluation_test_cases.retrieve, + ) + self.update = to_streamed_response_wrapper( + evaluation_test_cases.update, + ) + self.list = to_streamed_response_wrapper( + evaluation_test_cases.list, + ) + self.list_evaluation_runs = to_streamed_response_wrapper( + evaluation_test_cases.list_evaluation_runs, + ) + + +class AsyncEvaluationTestCasesResourceWithStreamingResponse: + def __init__(self, evaluation_test_cases: AsyncEvaluationTestCasesResource) -> None: + self._evaluation_test_cases = evaluation_test_cases + + self.create = async_to_streamed_response_wrapper( + evaluation_test_cases.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + evaluation_test_cases.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + evaluation_test_cases.update, + ) + self.list = async_to_streamed_response_wrapper( + evaluation_test_cases.list, + ) + self.list_evaluation_runs = async_to_streamed_response_wrapper( + evaluation_test_cases.list_evaluation_runs, + ) diff --git a/src/gradient/resources/agents/functions.py b/src/gradient/resources/agents/functions.py new file mode 100644 index 00000000..3d995d24 --- /dev/null +++ b/src/gradient/resources/agents/functions.py @@ -0,0 +1,493 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.agents import function_create_params, function_update_params +from ...types.agents.function_create_response import FunctionCreateResponse +from ...types.agents.function_delete_response import FunctionDeleteResponse +from ...types.agents.function_update_response import FunctionUpdateResponse + +__all__ = ["FunctionsResource", "AsyncFunctionsResource"] + + +class FunctionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FunctionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return FunctionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FunctionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return FunctionsResourceWithStreamingResponse(self) + + def create( + self, + path_agent_uuid: str, + *, + body_agent_uuid: str | Omit = omit, + description: str | Omit = omit, + faas_name: str | Omit = omit, + faas_namespace: str | Omit = omit, + function_name: str | Omit = omit, + input_schema: object | Omit = omit, + output_schema: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FunctionCreateResponse: + """ + To create a function route for an agent, send a POST request to + `/v2/gen-ai/agents/{agent_uuid}/functions`. + + Args: + body_agent_uuid: Agent id + + description: Function description + + faas_name: The name of the function in the DigitalOcean functions platform + + faas_namespace: The namespace of the function in the DigitalOcean functions platform + + function_name: Function name + + input_schema: Describe the input schema for the function so the agent may call it + + output_schema: Describe the output schema for the function so the agent handle its response + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_agent_uuid: + raise ValueError(f"Expected a non-empty value for `path_agent_uuid` but received {path_agent_uuid!r}") + return self._post( + f"/v2/gen-ai/agents/{path_agent_uuid}/functions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_agent_uuid}/functions", + body=maybe_transform( + { + "body_agent_uuid": body_agent_uuid, + "description": description, + "faas_name": faas_name, + "faas_namespace": faas_namespace, + "function_name": function_name, + "input_schema": input_schema, + "output_schema": output_schema, + }, + function_create_params.FunctionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FunctionCreateResponse, + ) + + def update( + self, + path_function_uuid: str, + *, + path_agent_uuid: str, + body_agent_uuid: str | Omit = omit, + description: str | Omit = omit, + faas_name: str | Omit = omit, + faas_namespace: str | Omit = omit, + function_name: str | Omit = omit, + body_function_uuid: str | Omit = omit, + input_schema: object | Omit = omit, + output_schema: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FunctionUpdateResponse: + """ + To update the function route, send a PUT request to + `/v2/gen-ai/agents/{agent_uuid}/functions/{function_uuid}`. + + Args: + body_agent_uuid: Agent id + + description: Funciton description + + faas_name: The name of the function in the DigitalOcean functions platform + + faas_namespace: The namespace of the function in the DigitalOcean functions platform + + function_name: Function name + + body_function_uuid: Function id + + input_schema: Describe the input schema for the function so the agent may call it + + output_schema: Describe the output schema for the function so the agent handle its response + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_agent_uuid: + raise ValueError(f"Expected a non-empty value for `path_agent_uuid` but received {path_agent_uuid!r}") + if not path_function_uuid: + raise ValueError(f"Expected a non-empty value for `path_function_uuid` but received {path_function_uuid!r}") + return self._put( + f"/v2/gen-ai/agents/{path_agent_uuid}/functions/{path_function_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_agent_uuid}/functions/{path_function_uuid}", + body=maybe_transform( + { + "body_agent_uuid": body_agent_uuid, + "description": description, + "faas_name": faas_name, + "faas_namespace": faas_namespace, + "function_name": function_name, + "body_function_uuid": body_function_uuid, + "input_schema": input_schema, + "output_schema": output_schema, + }, + function_update_params.FunctionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FunctionUpdateResponse, + ) + + def delete( + self, + function_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FunctionDeleteResponse: + """ + To delete a function route from an agent, send a DELETE request to + `/v2/gen-ai/agents/{agent_uuid}/functions/{function_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not function_uuid: + raise ValueError(f"Expected a non-empty value for `function_uuid` but received {function_uuid!r}") + return self._delete( + f"/v2/gen-ai/agents/{agent_uuid}/functions/{function_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/functions/{function_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FunctionDeleteResponse, + ) + + +class AsyncFunctionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFunctionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncFunctionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFunctionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncFunctionsResourceWithStreamingResponse(self) + + async def create( + self, + path_agent_uuid: str, + *, + body_agent_uuid: str | Omit = omit, + description: str | Omit = omit, + faas_name: str | Omit = omit, + faas_namespace: str | Omit = omit, + function_name: str | Omit = omit, + input_schema: object | Omit = omit, + output_schema: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FunctionCreateResponse: + """ + To create a function route for an agent, send a POST request to + `/v2/gen-ai/agents/{agent_uuid}/functions`. + + Args: + body_agent_uuid: Agent id + + description: Function description + + faas_name: The name of the function in the DigitalOcean functions platform + + faas_namespace: The namespace of the function in the DigitalOcean functions platform + + function_name: Function name + + input_schema: Describe the input schema for the function so the agent may call it + + output_schema: Describe the output schema for the function so the agent handle its response + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_agent_uuid: + raise ValueError(f"Expected a non-empty value for `path_agent_uuid` but received {path_agent_uuid!r}") + return await self._post( + f"/v2/gen-ai/agents/{path_agent_uuid}/functions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_agent_uuid}/functions", + body=await async_maybe_transform( + { + "body_agent_uuid": body_agent_uuid, + "description": description, + "faas_name": faas_name, + "faas_namespace": faas_namespace, + "function_name": function_name, + "input_schema": input_schema, + "output_schema": output_schema, + }, + function_create_params.FunctionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FunctionCreateResponse, + ) + + async def update( + self, + path_function_uuid: str, + *, + path_agent_uuid: str, + body_agent_uuid: str | Omit = omit, + description: str | Omit = omit, + faas_name: str | Omit = omit, + faas_namespace: str | Omit = omit, + function_name: str | Omit = omit, + body_function_uuid: str | Omit = omit, + input_schema: object | Omit = omit, + output_schema: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FunctionUpdateResponse: + """ + To update the function route, send a PUT request to + `/v2/gen-ai/agents/{agent_uuid}/functions/{function_uuid}`. + + Args: + body_agent_uuid: Agent id + + description: Funciton description + + faas_name: The name of the function in the DigitalOcean functions platform + + faas_namespace: The namespace of the function in the DigitalOcean functions platform + + function_name: Function name + + body_function_uuid: Function id + + input_schema: Describe the input schema for the function so the agent may call it + + output_schema: Describe the output schema for the function so the agent handle its response + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_agent_uuid: + raise ValueError(f"Expected a non-empty value for `path_agent_uuid` but received {path_agent_uuid!r}") + if not path_function_uuid: + raise ValueError(f"Expected a non-empty value for `path_function_uuid` but received {path_function_uuid!r}") + return await self._put( + f"/v2/gen-ai/agents/{path_agent_uuid}/functions/{path_function_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_agent_uuid}/functions/{path_function_uuid}", + body=await async_maybe_transform( + { + "body_agent_uuid": body_agent_uuid, + "description": description, + "faas_name": faas_name, + "faas_namespace": faas_namespace, + "function_name": function_name, + "body_function_uuid": body_function_uuid, + "input_schema": input_schema, + "output_schema": output_schema, + }, + function_update_params.FunctionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FunctionUpdateResponse, + ) + + async def delete( + self, + function_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FunctionDeleteResponse: + """ + To delete a function route from an agent, send a DELETE request to + `/v2/gen-ai/agents/{agent_uuid}/functions/{function_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not function_uuid: + raise ValueError(f"Expected a non-empty value for `function_uuid` but received {function_uuid!r}") + return await self._delete( + f"/v2/gen-ai/agents/{agent_uuid}/functions/{function_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/functions/{function_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FunctionDeleteResponse, + ) + + +class FunctionsResourceWithRawResponse: + def __init__(self, functions: FunctionsResource) -> None: + self._functions = functions + + self.create = to_raw_response_wrapper( + functions.create, + ) + self.update = to_raw_response_wrapper( + functions.update, + ) + self.delete = to_raw_response_wrapper( + functions.delete, + ) + + +class AsyncFunctionsResourceWithRawResponse: + def __init__(self, functions: AsyncFunctionsResource) -> None: + self._functions = functions + + self.create = async_to_raw_response_wrapper( + functions.create, + ) + self.update = async_to_raw_response_wrapper( + functions.update, + ) + self.delete = async_to_raw_response_wrapper( + functions.delete, + ) + + +class FunctionsResourceWithStreamingResponse: + def __init__(self, functions: FunctionsResource) -> None: + self._functions = functions + + self.create = to_streamed_response_wrapper( + functions.create, + ) + self.update = to_streamed_response_wrapper( + functions.update, + ) + self.delete = to_streamed_response_wrapper( + functions.delete, + ) + + +class AsyncFunctionsResourceWithStreamingResponse: + def __init__(self, functions: AsyncFunctionsResource) -> None: + self._functions = functions + + self.create = async_to_streamed_response_wrapper( + functions.create, + ) + self.update = async_to_streamed_response_wrapper( + functions.update, + ) + self.delete = async_to_streamed_response_wrapper( + functions.delete, + ) diff --git a/src/gradient/resources/agents/knowledge_bases.py b/src/gradient/resources/agents/knowledge_bases.py new file mode 100644 index 00000000..deefd123 --- /dev/null +++ b/src/gradient/resources/agents/knowledge_bases.py @@ -0,0 +1,358 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.agents.api_link_knowledge_base_output import APILinkKnowledgeBaseOutput +from ...types.agents.knowledge_base_detach_response import KnowledgeBaseDetachResponse + +__all__ = ["KnowledgeBasesResource", "AsyncKnowledgeBasesResource"] + + +class KnowledgeBasesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> KnowledgeBasesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return KnowledgeBasesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> KnowledgeBasesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return KnowledgeBasesResourceWithStreamingResponse(self) + + def attach( + self, + agent_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APILinkKnowledgeBaseOutput: + """ + To attach knowledge bases to an agent, send a POST request to + `/v2/gen-ai/agents/{agent_uuid}/knowledge_bases` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + return self._post( + f"/v2/gen-ai/agents/{agent_uuid}/knowledge_bases" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/knowledge_bases", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APILinkKnowledgeBaseOutput, + ) + + def attach_single( + self, + knowledge_base_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APILinkKnowledgeBaseOutput: + """ + To attach a knowledge base to an agent, send a POST request to + `/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return self._post( + f"/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APILinkKnowledgeBaseOutput, + ) + + def detach( + self, + knowledge_base_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseDetachResponse: + """ + To detach a knowledge base from an agent, send a DELETE request to + `/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return self._delete( + f"/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseDetachResponse, + ) + + +class AsyncKnowledgeBasesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncKnowledgeBasesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncKnowledgeBasesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncKnowledgeBasesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncKnowledgeBasesResourceWithStreamingResponse(self) + + async def attach( + self, + agent_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APILinkKnowledgeBaseOutput: + """ + To attach knowledge bases to an agent, send a POST request to + `/v2/gen-ai/agents/{agent_uuid}/knowledge_bases` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + return await self._post( + f"/v2/gen-ai/agents/{agent_uuid}/knowledge_bases" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/knowledge_bases", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APILinkKnowledgeBaseOutput, + ) + + async def attach_single( + self, + knowledge_base_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APILinkKnowledgeBaseOutput: + """ + To attach a knowledge base to an agent, send a POST request to + `/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return await self._post( + f"/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APILinkKnowledgeBaseOutput, + ) + + async def detach( + self, + knowledge_base_uuid: str, + *, + agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseDetachResponse: + """ + To detach a knowledge base from an agent, send a DELETE request to + `/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_uuid: + raise ValueError(f"Expected a non-empty value for `agent_uuid` but received {agent_uuid!r}") + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return await self._delete( + f"/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{agent_uuid}/knowledge_bases/{knowledge_base_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseDetachResponse, + ) + + +class KnowledgeBasesResourceWithRawResponse: + def __init__(self, knowledge_bases: KnowledgeBasesResource) -> None: + self._knowledge_bases = knowledge_bases + + self.attach = to_raw_response_wrapper( + knowledge_bases.attach, + ) + self.attach_single = to_raw_response_wrapper( + knowledge_bases.attach_single, + ) + self.detach = to_raw_response_wrapper( + knowledge_bases.detach, + ) + + +class AsyncKnowledgeBasesResourceWithRawResponse: + def __init__(self, knowledge_bases: AsyncKnowledgeBasesResource) -> None: + self._knowledge_bases = knowledge_bases + + self.attach = async_to_raw_response_wrapper( + knowledge_bases.attach, + ) + self.attach_single = async_to_raw_response_wrapper( + knowledge_bases.attach_single, + ) + self.detach = async_to_raw_response_wrapper( + knowledge_bases.detach, + ) + + +class KnowledgeBasesResourceWithStreamingResponse: + def __init__(self, knowledge_bases: KnowledgeBasesResource) -> None: + self._knowledge_bases = knowledge_bases + + self.attach = to_streamed_response_wrapper( + knowledge_bases.attach, + ) + self.attach_single = to_streamed_response_wrapper( + knowledge_bases.attach_single, + ) + self.detach = to_streamed_response_wrapper( + knowledge_bases.detach, + ) + + +class AsyncKnowledgeBasesResourceWithStreamingResponse: + def __init__(self, knowledge_bases: AsyncKnowledgeBasesResource) -> None: + self._knowledge_bases = knowledge_bases + + self.attach = async_to_streamed_response_wrapper( + knowledge_bases.attach, + ) + self.attach_single = async_to_streamed_response_wrapper( + knowledge_bases.attach_single, + ) + self.detach = async_to_streamed_response_wrapper( + knowledge_bases.detach, + ) diff --git a/src/gradient/resources/agents/routes.py b/src/gradient/resources/agents/routes.py new file mode 100644 index 00000000..dc37b7d2 --- /dev/null +++ b/src/gradient/resources/agents/routes.py @@ -0,0 +1,548 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.agents import route_add_params, route_update_params +from ...types.agents.route_add_response import RouteAddResponse +from ...types.agents.route_view_response import RouteViewResponse +from ...types.agents.route_delete_response import RouteDeleteResponse +from ...types.agents.route_update_response import RouteUpdateResponse + +__all__ = ["RoutesResource", "AsyncRoutesResource"] + + +class RoutesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RoutesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return RoutesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RoutesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return RoutesResourceWithStreamingResponse(self) + + def update( + self, + path_child_agent_uuid: str, + *, + path_parent_agent_uuid: str, + body_child_agent_uuid: str | Omit = omit, + if_case: str | Omit = omit, + body_parent_agent_uuid: str | Omit = omit, + route_name: str | Omit = omit, + uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RouteUpdateResponse: + """ + To update an agent route for an agent, send a PUT request to + `/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}`. + + Args: + body_child_agent_uuid: Routed agent id + + if_case: Describes the case in which the child agent should be used + + body_parent_agent_uuid: A unique identifier for the parent agent. + + route_name: Route name + + uuid: Unique id of linkage + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_parent_agent_uuid: + raise ValueError( + f"Expected a non-empty value for `path_parent_agent_uuid` but received {path_parent_agent_uuid!r}" + ) + if not path_child_agent_uuid: + raise ValueError( + f"Expected a non-empty value for `path_child_agent_uuid` but received {path_child_agent_uuid!r}" + ) + return self._put( + f"/v2/gen-ai/agents/{path_parent_agent_uuid}/child_agents/{path_child_agent_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_parent_agent_uuid}/child_agents/{path_child_agent_uuid}", + body=maybe_transform( + { + "body_child_agent_uuid": body_child_agent_uuid, + "if_case": if_case, + "body_parent_agent_uuid": body_parent_agent_uuid, + "route_name": route_name, + "uuid": uuid, + }, + route_update_params.RouteUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RouteUpdateResponse, + ) + + def delete( + self, + child_agent_uuid: str, + *, + parent_agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RouteDeleteResponse: + """ + To delete an agent route from a parent agent, send a DELETE request to + `/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not parent_agent_uuid: + raise ValueError(f"Expected a non-empty value for `parent_agent_uuid` but received {parent_agent_uuid!r}") + if not child_agent_uuid: + raise ValueError(f"Expected a non-empty value for `child_agent_uuid` but received {child_agent_uuid!r}") + return self._delete( + f"/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RouteDeleteResponse, + ) + + def add( + self, + path_child_agent_uuid: str, + *, + path_parent_agent_uuid: str, + body_child_agent_uuid: str | Omit = omit, + if_case: str | Omit = omit, + body_parent_agent_uuid: str | Omit = omit, + route_name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RouteAddResponse: + """ + To add an agent route to an agent, send a POST request to + `/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}`. + + Args: + body_child_agent_uuid: Routed agent id + + body_parent_agent_uuid: A unique identifier for the parent agent. + + route_name: Name of route + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_parent_agent_uuid: + raise ValueError( + f"Expected a non-empty value for `path_parent_agent_uuid` but received {path_parent_agent_uuid!r}" + ) + if not path_child_agent_uuid: + raise ValueError( + f"Expected a non-empty value for `path_child_agent_uuid` but received {path_child_agent_uuid!r}" + ) + return self._post( + f"/v2/gen-ai/agents/{path_parent_agent_uuid}/child_agents/{path_child_agent_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_parent_agent_uuid}/child_agents/{path_child_agent_uuid}", + body=maybe_transform( + { + "body_child_agent_uuid": body_child_agent_uuid, + "if_case": if_case, + "body_parent_agent_uuid": body_parent_agent_uuid, + "route_name": route_name, + }, + route_add_params.RouteAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RouteAddResponse, + ) + + def view( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RouteViewResponse: + """ + To view agent routes for an agent, send a GET requtest to + `/v2/gen-ai/agents/{uuid}/child_agents`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/agents/{uuid}/child_agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}/child_agents", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RouteViewResponse, + ) + + +class AsyncRoutesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRoutesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncRoutesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRoutesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncRoutesResourceWithStreamingResponse(self) + + async def update( + self, + path_child_agent_uuid: str, + *, + path_parent_agent_uuid: str, + body_child_agent_uuid: str | Omit = omit, + if_case: str | Omit = omit, + body_parent_agent_uuid: str | Omit = omit, + route_name: str | Omit = omit, + uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RouteUpdateResponse: + """ + To update an agent route for an agent, send a PUT request to + `/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}`. + + Args: + body_child_agent_uuid: Routed agent id + + if_case: Describes the case in which the child agent should be used + + body_parent_agent_uuid: A unique identifier for the parent agent. + + route_name: Route name + + uuid: Unique id of linkage + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_parent_agent_uuid: + raise ValueError( + f"Expected a non-empty value for `path_parent_agent_uuid` but received {path_parent_agent_uuid!r}" + ) + if not path_child_agent_uuid: + raise ValueError( + f"Expected a non-empty value for `path_child_agent_uuid` but received {path_child_agent_uuid!r}" + ) + return await self._put( + f"/v2/gen-ai/agents/{path_parent_agent_uuid}/child_agents/{path_child_agent_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_parent_agent_uuid}/child_agents/{path_child_agent_uuid}", + body=await async_maybe_transform( + { + "body_child_agent_uuid": body_child_agent_uuid, + "if_case": if_case, + "body_parent_agent_uuid": body_parent_agent_uuid, + "route_name": route_name, + "uuid": uuid, + }, + route_update_params.RouteUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RouteUpdateResponse, + ) + + async def delete( + self, + child_agent_uuid: str, + *, + parent_agent_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RouteDeleteResponse: + """ + To delete an agent route from a parent agent, send a DELETE request to + `/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not parent_agent_uuid: + raise ValueError(f"Expected a non-empty value for `parent_agent_uuid` but received {parent_agent_uuid!r}") + if not child_agent_uuid: + raise ValueError(f"Expected a non-empty value for `child_agent_uuid` but received {child_agent_uuid!r}") + return await self._delete( + f"/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RouteDeleteResponse, + ) + + async def add( + self, + path_child_agent_uuid: str, + *, + path_parent_agent_uuid: str, + body_child_agent_uuid: str | Omit = omit, + if_case: str | Omit = omit, + body_parent_agent_uuid: str | Omit = omit, + route_name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RouteAddResponse: + """ + To add an agent route to an agent, send a POST request to + `/v2/gen-ai/agents/{parent_agent_uuid}/child_agents/{child_agent_uuid}`. + + Args: + body_child_agent_uuid: Routed agent id + + body_parent_agent_uuid: A unique identifier for the parent agent. + + route_name: Name of route + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_parent_agent_uuid: + raise ValueError( + f"Expected a non-empty value for `path_parent_agent_uuid` but received {path_parent_agent_uuid!r}" + ) + if not path_child_agent_uuid: + raise ValueError( + f"Expected a non-empty value for `path_child_agent_uuid` but received {path_child_agent_uuid!r}" + ) + return await self._post( + f"/v2/gen-ai/agents/{path_parent_agent_uuid}/child_agents/{path_child_agent_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_parent_agent_uuid}/child_agents/{path_child_agent_uuid}", + body=await async_maybe_transform( + { + "body_child_agent_uuid": body_child_agent_uuid, + "if_case": if_case, + "body_parent_agent_uuid": body_parent_agent_uuid, + "route_name": route_name, + }, + route_add_params.RouteAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RouteAddResponse, + ) + + async def view( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RouteViewResponse: + """ + To view agent routes for an agent, send a GET requtest to + `/v2/gen-ai/agents/{uuid}/child_agents`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/agents/{uuid}/child_agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}/child_agents", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RouteViewResponse, + ) + + +class RoutesResourceWithRawResponse: + def __init__(self, routes: RoutesResource) -> None: + self._routes = routes + + self.update = to_raw_response_wrapper( + routes.update, + ) + self.delete = to_raw_response_wrapper( + routes.delete, + ) + self.add = to_raw_response_wrapper( + routes.add, + ) + self.view = to_raw_response_wrapper( + routes.view, + ) + + +class AsyncRoutesResourceWithRawResponse: + def __init__(self, routes: AsyncRoutesResource) -> None: + self._routes = routes + + self.update = async_to_raw_response_wrapper( + routes.update, + ) + self.delete = async_to_raw_response_wrapper( + routes.delete, + ) + self.add = async_to_raw_response_wrapper( + routes.add, + ) + self.view = async_to_raw_response_wrapper( + routes.view, + ) + + +class RoutesResourceWithStreamingResponse: + def __init__(self, routes: RoutesResource) -> None: + self._routes = routes + + self.update = to_streamed_response_wrapper( + routes.update, + ) + self.delete = to_streamed_response_wrapper( + routes.delete, + ) + self.add = to_streamed_response_wrapper( + routes.add, + ) + self.view = to_streamed_response_wrapper( + routes.view, + ) + + +class AsyncRoutesResourceWithStreamingResponse: + def __init__(self, routes: AsyncRoutesResource) -> None: + self._routes = routes + + self.update = async_to_streamed_response_wrapper( + routes.update, + ) + self.delete = async_to_streamed_response_wrapper( + routes.delete, + ) + self.add = async_to_streamed_response_wrapper( + routes.add, + ) + self.view = async_to_streamed_response_wrapper( + routes.view, + ) diff --git a/src/gradient/resources/agents/versions.py b/src/gradient/resources/agents/versions.py new file mode 100644 index 00000000..0331344a --- /dev/null +++ b/src/gradient/resources/agents/versions.py @@ -0,0 +1,314 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.agents import version_list_params, version_update_params +from ...types.agents.version_list_response import VersionListResponse +from ...types.agents.version_update_response import VersionUpdateResponse + +__all__ = ["VersionsResource", "AsyncVersionsResource"] + + +class VersionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> VersionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return VersionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VersionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return VersionsResourceWithStreamingResponse(self) + + def update( + self, + path_uuid: str, + *, + body_uuid: str | Omit = omit, + version_hash: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VersionUpdateResponse: + """ + To update to a specific agent version, send a PUT request to + `/v2/gen-ai/agents/{uuid}/versions`. + + Args: + body_uuid: Agent unique identifier + + version_hash: Unique identifier + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return self._put( + f"/v2/gen-ai/agents/{path_uuid}/versions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_uuid}/versions", + body=maybe_transform( + { + "body_uuid": body_uuid, + "version_hash": version_hash, + }, + version_update_params.VersionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VersionUpdateResponse, + ) + + def list( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VersionListResponse: + """ + To list all agent versions, send a GET request to + `/v2/gen-ai/agents/{uuid}/versions`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/agents/{uuid}/versions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}/versions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + version_list_params.VersionListParams, + ), + ), + cast_to=VersionListResponse, + ) + + +class AsyncVersionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncVersionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncVersionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVersionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncVersionsResourceWithStreamingResponse(self) + + async def update( + self, + path_uuid: str, + *, + body_uuid: str | Omit = omit, + version_hash: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VersionUpdateResponse: + """ + To update to a specific agent version, send a PUT request to + `/v2/gen-ai/agents/{uuid}/versions`. + + Args: + body_uuid: Agent unique identifier + + version_hash: Unique identifier + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return await self._put( + f"/v2/gen-ai/agents/{path_uuid}/versions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{path_uuid}/versions", + body=await async_maybe_transform( + { + "body_uuid": body_uuid, + "version_hash": version_hash, + }, + version_update_params.VersionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VersionUpdateResponse, + ) + + async def list( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VersionListResponse: + """ + To list all agent versions, send a GET request to + `/v2/gen-ai/agents/{uuid}/versions`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/agents/{uuid}/versions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/agents/{uuid}/versions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + version_list_params.VersionListParams, + ), + ), + cast_to=VersionListResponse, + ) + + +class VersionsResourceWithRawResponse: + def __init__(self, versions: VersionsResource) -> None: + self._versions = versions + + self.update = to_raw_response_wrapper( + versions.update, + ) + self.list = to_raw_response_wrapper( + versions.list, + ) + + +class AsyncVersionsResourceWithRawResponse: + def __init__(self, versions: AsyncVersionsResource) -> None: + self._versions = versions + + self.update = async_to_raw_response_wrapper( + versions.update, + ) + self.list = async_to_raw_response_wrapper( + versions.list, + ) + + +class VersionsResourceWithStreamingResponse: + def __init__(self, versions: VersionsResource) -> None: + self._versions = versions + + self.update = to_streamed_response_wrapper( + versions.update, + ) + self.list = to_streamed_response_wrapper( + versions.list, + ) + + +class AsyncVersionsResourceWithStreamingResponse: + def __init__(self, versions: AsyncVersionsResource) -> None: + self._versions = versions + + self.update = async_to_streamed_response_wrapper( + versions.update, + ) + self.list = async_to_streamed_response_wrapper( + versions.list, + ) diff --git a/src/gradient/resources/chat/__init__.py b/src/gradient/resources/chat/__init__.py new file mode 100644 index 00000000..ec960eb4 --- /dev/null +++ b/src/gradient/resources/chat/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) +from .completions import ( + CompletionsResource, + AsyncCompletionsResource, + CompletionsResourceWithRawResponse, + AsyncCompletionsResourceWithRawResponse, + CompletionsResourceWithStreamingResponse, + AsyncCompletionsResourceWithStreamingResponse, +) + +__all__ = [ + "CompletionsResource", + "AsyncCompletionsResource", + "CompletionsResourceWithRawResponse", + "AsyncCompletionsResourceWithRawResponse", + "CompletionsResourceWithStreamingResponse", + "AsyncCompletionsResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/chat/chat.py b/src/gradient/resources/chat/chat.py new file mode 100644 index 00000000..ac933129 --- /dev/null +++ b/src/gradient/resources/chat/chat.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .completions import ( + CompletionsResource, + AsyncCompletionsResource, + CompletionsResourceWithRawResponse, + AsyncCompletionsResourceWithRawResponse, + CompletionsResourceWithStreamingResponse, + AsyncCompletionsResourceWithStreamingResponse, +) + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def completions(self) -> CompletionsResource: + return CompletionsResource(self._client) + + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def completions(self) -> AsyncCompletionsResource: + return AsyncCompletionsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> CompletionsResourceWithRawResponse: + return CompletionsResourceWithRawResponse(self._chat.completions) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsResourceWithRawResponse: + return AsyncCompletionsResourceWithRawResponse(self._chat.completions) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> CompletionsResourceWithStreamingResponse: + return CompletionsResourceWithStreamingResponse(self._chat.completions) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsResourceWithStreamingResponse: + return AsyncCompletionsResourceWithStreamingResponse(self._chat.completions) diff --git a/src/gradient/resources/chat/completions.py b/src/gradient/resources/chat/completions.py new file mode 100644 index 00000000..e2292bcb --- /dev/null +++ b/src/gradient/resources/chat/completions.py @@ -0,0 +1,1008 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, overload + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._streaming import Stream, AsyncStream +from ...types.chat import completion_create_params +from ..._base_client import make_request_options +from ...types.shared.chat_completion_chunk import ChatCompletionChunk +from ...types.chat.completion_create_response import CompletionCreateResponse + +__all__ = ["CompletionsResource", "AsyncCompletionsResource"] + + +class CompletionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CompletionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return CompletionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CompletionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return CompletionsResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + stream: Literal[True], + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + stream: bool, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse | Stream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["messages", "model"], ["messages", "model", "stream"]) + def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse | Stream[ChatCompletionChunk]: + return self._post( + "/chat/completions" + if self._client._base_url_overridden + else f"{self._client.inference_endpoint}/v1/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "metadata": metadata, + "n": n, + "presence_penalty": presence_penalty, + "stop": stop, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CompletionCreateResponse, + stream=stream or False, + stream_cls=Stream[ChatCompletionChunk], + ) + + +class AsyncCompletionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCompletionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncCompletionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCompletionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncCompletionsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + stream: Literal[True], + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + stream: bool, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse | AsyncStream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + + model: Model ID used to generate the response. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + + max_tokens: The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. + + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["messages", "model"], ["messages", "model", "stream"]) + async def create( + self, + *, + messages: Iterable[completion_create_params.Message], + model: str, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[completion_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: completion_create_params.ToolChoice | Omit = omit, + tools: Iterable[completion_create_params.Tool] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompletionCreateResponse | AsyncStream[ChatCompletionChunk]: + return await self._post( + "/chat/completions" + if self._client._base_url_overridden + else f"{self._client.inference_endpoint}/v1/chat/completions", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "metadata": metadata, + "n": n, + "presence_penalty": presence_penalty, + "stop": stop, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CompletionCreateResponse, + stream=stream or False, + stream_cls=AsyncStream[ChatCompletionChunk], + ) + + +class CompletionsResourceWithRawResponse: + def __init__(self, completions: CompletionsResource) -> None: + self._completions = completions + + self.create = to_raw_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsResourceWithRawResponse: + def __init__(self, completions: AsyncCompletionsResource) -> None: + self._completions = completions + + self.create = async_to_raw_response_wrapper( + completions.create, + ) + + +class CompletionsResourceWithStreamingResponse: + def __init__(self, completions: CompletionsResource) -> None: + self._completions = completions + + self.create = to_streamed_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsResourceWithStreamingResponse: + def __init__(self, completions: AsyncCompletionsResource) -> None: + self._completions = completions + + self.create = async_to_streamed_response_wrapper( + completions.create, + ) diff --git a/src/gradient/resources/databases/__init__.py b/src/gradient/resources/databases/__init__.py new file mode 100644 index 00000000..40c62ed8 --- /dev/null +++ b/src/gradient/resources/databases/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .databases import ( + DatabasesResource, + AsyncDatabasesResource, + DatabasesResourceWithRawResponse, + AsyncDatabasesResourceWithRawResponse, + DatabasesResourceWithStreamingResponse, + AsyncDatabasesResourceWithStreamingResponse, +) +from .schema_registry import ( + SchemaRegistryResource, + AsyncSchemaRegistryResource, + SchemaRegistryResourceWithRawResponse, + AsyncSchemaRegistryResourceWithRawResponse, + SchemaRegistryResourceWithStreamingResponse, + AsyncSchemaRegistryResourceWithStreamingResponse, +) + +__all__ = [ + "SchemaRegistryResource", + "AsyncSchemaRegistryResource", + "SchemaRegistryResourceWithRawResponse", + "AsyncSchemaRegistryResourceWithRawResponse", + "SchemaRegistryResourceWithStreamingResponse", + "AsyncSchemaRegistryResourceWithStreamingResponse", + "DatabasesResource", + "AsyncDatabasesResource", + "DatabasesResourceWithRawResponse", + "AsyncDatabasesResourceWithRawResponse", + "DatabasesResourceWithStreamingResponse", + "AsyncDatabasesResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/databases/databases.py b/src/gradient/resources/databases/databases.py new file mode 100644 index 00000000..120ab91f --- /dev/null +++ b/src/gradient/resources/databases/databases.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .schema_registry.schema_registry import ( + SchemaRegistryResource, + AsyncSchemaRegistryResource, + SchemaRegistryResourceWithRawResponse, + AsyncSchemaRegistryResourceWithRawResponse, + SchemaRegistryResourceWithStreamingResponse, + AsyncSchemaRegistryResourceWithStreamingResponse, +) + +__all__ = ["DatabasesResource", "AsyncDatabasesResource"] + + +class DatabasesResource(SyncAPIResource): + @cached_property + def schema_registry(self) -> SchemaRegistryResource: + return SchemaRegistryResource(self._client) + + @cached_property + def with_raw_response(self) -> DatabasesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return DatabasesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DatabasesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return DatabasesResourceWithStreamingResponse(self) + + +class AsyncDatabasesResource(AsyncAPIResource): + @cached_property + def schema_registry(self) -> AsyncSchemaRegistryResource: + return AsyncSchemaRegistryResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncDatabasesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncDatabasesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDatabasesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncDatabasesResourceWithStreamingResponse(self) + + +class DatabasesResourceWithRawResponse: + def __init__(self, databases: DatabasesResource) -> None: + self._databases = databases + + @cached_property + def schema_registry(self) -> SchemaRegistryResourceWithRawResponse: + return SchemaRegistryResourceWithRawResponse(self._databases.schema_registry) + + +class AsyncDatabasesResourceWithRawResponse: + def __init__(self, databases: AsyncDatabasesResource) -> None: + self._databases = databases + + @cached_property + def schema_registry(self) -> AsyncSchemaRegistryResourceWithRawResponse: + return AsyncSchemaRegistryResourceWithRawResponse(self._databases.schema_registry) + + +class DatabasesResourceWithStreamingResponse: + def __init__(self, databases: DatabasesResource) -> None: + self._databases = databases + + @cached_property + def schema_registry(self) -> SchemaRegistryResourceWithStreamingResponse: + return SchemaRegistryResourceWithStreamingResponse(self._databases.schema_registry) + + +class AsyncDatabasesResourceWithStreamingResponse: + def __init__(self, databases: AsyncDatabasesResource) -> None: + self._databases = databases + + @cached_property + def schema_registry(self) -> AsyncSchemaRegistryResourceWithStreamingResponse: + return AsyncSchemaRegistryResourceWithStreamingResponse(self._databases.schema_registry) diff --git a/src/gradient/resources/databases/schema_registry/__init__.py b/src/gradient/resources/databases/schema_registry/__init__.py new file mode 100644 index 00000000..2015e4d4 --- /dev/null +++ b/src/gradient/resources/databases/schema_registry/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .config import ( + ConfigResource, + AsyncConfigResource, + ConfigResourceWithRawResponse, + AsyncConfigResourceWithRawResponse, + ConfigResourceWithStreamingResponse, + AsyncConfigResourceWithStreamingResponse, +) +from .schema_registry import ( + SchemaRegistryResource, + AsyncSchemaRegistryResource, + SchemaRegistryResourceWithRawResponse, + AsyncSchemaRegistryResourceWithRawResponse, + SchemaRegistryResourceWithStreamingResponse, + AsyncSchemaRegistryResourceWithStreamingResponse, +) + +__all__ = [ + "ConfigResource", + "AsyncConfigResource", + "ConfigResourceWithRawResponse", + "AsyncConfigResourceWithRawResponse", + "ConfigResourceWithStreamingResponse", + "AsyncConfigResourceWithStreamingResponse", + "SchemaRegistryResource", + "AsyncSchemaRegistryResource", + "SchemaRegistryResourceWithRawResponse", + "AsyncSchemaRegistryResourceWithRawResponse", + "SchemaRegistryResourceWithStreamingResponse", + "AsyncSchemaRegistryResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/databases/schema_registry/config.py b/src/gradient/resources/databases/schema_registry/config.py new file mode 100644 index 00000000..e012dd77 --- /dev/null +++ b/src/gradient/resources/databases/schema_registry/config.py @@ -0,0 +1,506 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.databases.schema_registry import config_update_params, config_update_subject_params +from ....types.databases.schema_registry.config_update_response import ConfigUpdateResponse +from ....types.databases.schema_registry.config_retrieve_response import ConfigRetrieveResponse +from ....types.databases.schema_registry.config_update_subject_response import ConfigUpdateSubjectResponse +from ....types.databases.schema_registry.config_retrieve_subject_response import ConfigRetrieveSubjectResponse + +__all__ = ["ConfigResource", "AsyncConfigResource"] + + +class ConfigResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ConfigResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ConfigResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ConfigResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ConfigResourceWithStreamingResponse(self) + + def retrieve( + self, + database_cluster_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConfigRetrieveResponse: + """ + To retrieve the Schema Registry configuration for a Kafka cluster, send a GET + request to `/v2/databases/$DATABASE_ID/schema-registry/config`. The response is + a JSON object with a `compatibility_level` key, which is set to an object + containing any database configuration parameters. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not database_cluster_uuid: + raise ValueError( + f"Expected a non-empty value for `database_cluster_uuid` but received {database_cluster_uuid!r}" + ) + return self._get( + f"/v2/databases/{database_cluster_uuid}/schema-registry/config" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/databases/{database_cluster_uuid}/schema-registry/config", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConfigRetrieveResponse, + ) + + def update( + self, + database_cluster_uuid: str, + *, + compatibility_level: Literal[ + "NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE" + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConfigUpdateResponse: + """ + To update the Schema Registry configuration for a Kafka cluster, send a PUT + request to `/v2/databases/$DATABASE_ID/schema-registry/config`. The response is + a JSON object with a `compatibility_level` key, which is set to an object + containing any database configuration parameters. + + Args: + compatibility_level: The compatibility level of the schema registry. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not database_cluster_uuid: + raise ValueError( + f"Expected a non-empty value for `database_cluster_uuid` but received {database_cluster_uuid!r}" + ) + return self._put( + f"/v2/databases/{database_cluster_uuid}/schema-registry/config" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/databases/{database_cluster_uuid}/schema-registry/config", + body=maybe_transform({"compatibility_level": compatibility_level}, config_update_params.ConfigUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConfigUpdateResponse, + ) + + def retrieve_subject( + self, + subject_name: str, + *, + database_cluster_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConfigRetrieveSubjectResponse: + """ + To retrieve the Schema Registry configuration for a Subject of a Kafka cluster, + send a GET request to + `/v2/databases/$DATABASE_ID/schema-registry/config/$SUBJECT_NAME`. The response + is a JSON object with a `compatibility_level` key, which is set to an object + containing any database configuration parameters. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not database_cluster_uuid: + raise ValueError( + f"Expected a non-empty value for `database_cluster_uuid` but received {database_cluster_uuid!r}" + ) + if not subject_name: + raise ValueError(f"Expected a non-empty value for `subject_name` but received {subject_name!r}") + return self._get( + f"/v2/databases/{database_cluster_uuid}/schema-registry/config/{subject_name}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/databases/{database_cluster_uuid}/schema-registry/config/{subject_name}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConfigRetrieveSubjectResponse, + ) + + def update_subject( + self, + subject_name: str, + *, + database_cluster_uuid: str, + compatibility_level: Literal[ + "NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE" + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConfigUpdateSubjectResponse: + """ + To update the Schema Registry configuration for a Subject of a Kafka cluster, + send a PUT request to + `/v2/databases/$DATABASE_ID/schema-registry/config/$SUBJECT_NAME`. The response + is a JSON object with a `compatibility_level` key, which is set to an object + containing any database configuration parameters. + + Args: + compatibility_level: The compatibility level of the schema registry. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not database_cluster_uuid: + raise ValueError( + f"Expected a non-empty value for `database_cluster_uuid` but received {database_cluster_uuid!r}" + ) + if not subject_name: + raise ValueError(f"Expected a non-empty value for `subject_name` but received {subject_name!r}") + return self._put( + f"/v2/databases/{database_cluster_uuid}/schema-registry/config/{subject_name}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/databases/{database_cluster_uuid}/schema-registry/config/{subject_name}", + body=maybe_transform( + {"compatibility_level": compatibility_level}, config_update_subject_params.ConfigUpdateSubjectParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConfigUpdateSubjectResponse, + ) + + +class AsyncConfigResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncConfigResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncConfigResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncConfigResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncConfigResourceWithStreamingResponse(self) + + async def retrieve( + self, + database_cluster_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConfigRetrieveResponse: + """ + To retrieve the Schema Registry configuration for a Kafka cluster, send a GET + request to `/v2/databases/$DATABASE_ID/schema-registry/config`. The response is + a JSON object with a `compatibility_level` key, which is set to an object + containing any database configuration parameters. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not database_cluster_uuid: + raise ValueError( + f"Expected a non-empty value for `database_cluster_uuid` but received {database_cluster_uuid!r}" + ) + return await self._get( + f"/v2/databases/{database_cluster_uuid}/schema-registry/config" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/databases/{database_cluster_uuid}/schema-registry/config", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConfigRetrieveResponse, + ) + + async def update( + self, + database_cluster_uuid: str, + *, + compatibility_level: Literal[ + "NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE" + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConfigUpdateResponse: + """ + To update the Schema Registry configuration for a Kafka cluster, send a PUT + request to `/v2/databases/$DATABASE_ID/schema-registry/config`. The response is + a JSON object with a `compatibility_level` key, which is set to an object + containing any database configuration parameters. + + Args: + compatibility_level: The compatibility level of the schema registry. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not database_cluster_uuid: + raise ValueError( + f"Expected a non-empty value for `database_cluster_uuid` but received {database_cluster_uuid!r}" + ) + return await self._put( + f"/v2/databases/{database_cluster_uuid}/schema-registry/config" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/databases/{database_cluster_uuid}/schema-registry/config", + body=await async_maybe_transform( + {"compatibility_level": compatibility_level}, config_update_params.ConfigUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConfigUpdateResponse, + ) + + async def retrieve_subject( + self, + subject_name: str, + *, + database_cluster_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConfigRetrieveSubjectResponse: + """ + To retrieve the Schema Registry configuration for a Subject of a Kafka cluster, + send a GET request to + `/v2/databases/$DATABASE_ID/schema-registry/config/$SUBJECT_NAME`. The response + is a JSON object with a `compatibility_level` key, which is set to an object + containing any database configuration parameters. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not database_cluster_uuid: + raise ValueError( + f"Expected a non-empty value for `database_cluster_uuid` but received {database_cluster_uuid!r}" + ) + if not subject_name: + raise ValueError(f"Expected a non-empty value for `subject_name` but received {subject_name!r}") + return await self._get( + f"/v2/databases/{database_cluster_uuid}/schema-registry/config/{subject_name}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/databases/{database_cluster_uuid}/schema-registry/config/{subject_name}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConfigRetrieveSubjectResponse, + ) + + async def update_subject( + self, + subject_name: str, + *, + database_cluster_uuid: str, + compatibility_level: Literal[ + "NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE" + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConfigUpdateSubjectResponse: + """ + To update the Schema Registry configuration for a Subject of a Kafka cluster, + send a PUT request to + `/v2/databases/$DATABASE_ID/schema-registry/config/$SUBJECT_NAME`. The response + is a JSON object with a `compatibility_level` key, which is set to an object + containing any database configuration parameters. + + Args: + compatibility_level: The compatibility level of the schema registry. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not database_cluster_uuid: + raise ValueError( + f"Expected a non-empty value for `database_cluster_uuid` but received {database_cluster_uuid!r}" + ) + if not subject_name: + raise ValueError(f"Expected a non-empty value for `subject_name` but received {subject_name!r}") + return await self._put( + f"/v2/databases/{database_cluster_uuid}/schema-registry/config/{subject_name}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/databases/{database_cluster_uuid}/schema-registry/config/{subject_name}", + body=await async_maybe_transform( + {"compatibility_level": compatibility_level}, config_update_subject_params.ConfigUpdateSubjectParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConfigUpdateSubjectResponse, + ) + + +class ConfigResourceWithRawResponse: + def __init__(self, config: ConfigResource) -> None: + self._config = config + + self.retrieve = to_raw_response_wrapper( + config.retrieve, + ) + self.update = to_raw_response_wrapper( + config.update, + ) + self.retrieve_subject = to_raw_response_wrapper( + config.retrieve_subject, + ) + self.update_subject = to_raw_response_wrapper( + config.update_subject, + ) + + +class AsyncConfigResourceWithRawResponse: + def __init__(self, config: AsyncConfigResource) -> None: + self._config = config + + self.retrieve = async_to_raw_response_wrapper( + config.retrieve, + ) + self.update = async_to_raw_response_wrapper( + config.update, + ) + self.retrieve_subject = async_to_raw_response_wrapper( + config.retrieve_subject, + ) + self.update_subject = async_to_raw_response_wrapper( + config.update_subject, + ) + + +class ConfigResourceWithStreamingResponse: + def __init__(self, config: ConfigResource) -> None: + self._config = config + + self.retrieve = to_streamed_response_wrapper( + config.retrieve, + ) + self.update = to_streamed_response_wrapper( + config.update, + ) + self.retrieve_subject = to_streamed_response_wrapper( + config.retrieve_subject, + ) + self.update_subject = to_streamed_response_wrapper( + config.update_subject, + ) + + +class AsyncConfigResourceWithStreamingResponse: + def __init__(self, config: AsyncConfigResource) -> None: + self._config = config + + self.retrieve = async_to_streamed_response_wrapper( + config.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + config.update, + ) + self.retrieve_subject = async_to_streamed_response_wrapper( + config.retrieve_subject, + ) + self.update_subject = async_to_streamed_response_wrapper( + config.update_subject, + ) diff --git a/src/gradient/resources/databases/schema_registry/schema_registry.py b/src/gradient/resources/databases/schema_registry/schema_registry.py new file mode 100644 index 00000000..dd7d3dbe --- /dev/null +++ b/src/gradient/resources/databases/schema_registry/schema_registry.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .config import ( + ConfigResource, + AsyncConfigResource, + ConfigResourceWithRawResponse, + AsyncConfigResourceWithRawResponse, + ConfigResourceWithStreamingResponse, + AsyncConfigResourceWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["SchemaRegistryResource", "AsyncSchemaRegistryResource"] + + +class SchemaRegistryResource(SyncAPIResource): + @cached_property + def config(self) -> ConfigResource: + return ConfigResource(self._client) + + @cached_property + def with_raw_response(self) -> SchemaRegistryResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return SchemaRegistryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SchemaRegistryResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return SchemaRegistryResourceWithStreamingResponse(self) + + +class AsyncSchemaRegistryResource(AsyncAPIResource): + @cached_property + def config(self) -> AsyncConfigResource: + return AsyncConfigResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSchemaRegistryResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncSchemaRegistryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSchemaRegistryResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncSchemaRegistryResourceWithStreamingResponse(self) + + +class SchemaRegistryResourceWithRawResponse: + def __init__(self, schema_registry: SchemaRegistryResource) -> None: + self._schema_registry = schema_registry + + @cached_property + def config(self) -> ConfigResourceWithRawResponse: + return ConfigResourceWithRawResponse(self._schema_registry.config) + + +class AsyncSchemaRegistryResourceWithRawResponse: + def __init__(self, schema_registry: AsyncSchemaRegistryResource) -> None: + self._schema_registry = schema_registry + + @cached_property + def config(self) -> AsyncConfigResourceWithRawResponse: + return AsyncConfigResourceWithRawResponse(self._schema_registry.config) + + +class SchemaRegistryResourceWithStreamingResponse: + def __init__(self, schema_registry: SchemaRegistryResource) -> None: + self._schema_registry = schema_registry + + @cached_property + def config(self) -> ConfigResourceWithStreamingResponse: + return ConfigResourceWithStreamingResponse(self._schema_registry.config) + + +class AsyncSchemaRegistryResourceWithStreamingResponse: + def __init__(self, schema_registry: AsyncSchemaRegistryResource) -> None: + self._schema_registry = schema_registry + + @cached_property + def config(self) -> AsyncConfigResourceWithStreamingResponse: + return AsyncConfigResourceWithStreamingResponse(self._schema_registry.config) diff --git a/src/gradient/resources/gpu_droplets/__init__.py b/src/gradient/resources/gpu_droplets/__init__.py new file mode 100644 index 00000000..064a36ce --- /dev/null +++ b/src/gradient/resources/gpu_droplets/__init__.py @@ -0,0 +1,187 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .sizes import ( + SizesResource, + AsyncSizesResource, + SizesResourceWithRawResponse, + AsyncSizesResourceWithRawResponse, + SizesResourceWithStreamingResponse, + AsyncSizesResourceWithStreamingResponse, +) +from .images import ( + ImagesResource, + AsyncImagesResource, + ImagesResourceWithRawResponse, + AsyncImagesResourceWithRawResponse, + ImagesResourceWithStreamingResponse, + AsyncImagesResourceWithStreamingResponse, +) +from .account import ( + AccountResource, + AsyncAccountResource, + AccountResourceWithRawResponse, + AsyncAccountResourceWithRawResponse, + AccountResourceWithStreamingResponse, + AsyncAccountResourceWithStreamingResponse, +) +from .actions import ( + ActionsResource, + AsyncActionsResource, + ActionsResourceWithRawResponse, + AsyncActionsResourceWithRawResponse, + ActionsResourceWithStreamingResponse, + AsyncActionsResourceWithStreamingResponse, +) +from .backups import ( + BackupsResource, + AsyncBackupsResource, + BackupsResourceWithRawResponse, + AsyncBackupsResourceWithRawResponse, + BackupsResourceWithStreamingResponse, + AsyncBackupsResourceWithStreamingResponse, +) +from .volumes import ( + VolumesResource, + AsyncVolumesResource, + VolumesResourceWithRawResponse, + AsyncVolumesResourceWithRawResponse, + VolumesResourceWithStreamingResponse, + AsyncVolumesResourceWithStreamingResponse, +) +from .autoscale import ( + AutoscaleResource, + AsyncAutoscaleResource, + AutoscaleResourceWithRawResponse, + AsyncAutoscaleResourceWithRawResponse, + AutoscaleResourceWithStreamingResponse, + AsyncAutoscaleResourceWithStreamingResponse, +) +from .firewalls import ( + FirewallsResource, + AsyncFirewallsResource, + FirewallsResourceWithRawResponse, + AsyncFirewallsResourceWithRawResponse, + FirewallsResourceWithStreamingResponse, + AsyncFirewallsResourceWithStreamingResponse, +) +from .snapshots import ( + SnapshotsResource, + AsyncSnapshotsResource, + SnapshotsResourceWithRawResponse, + AsyncSnapshotsResourceWithRawResponse, + SnapshotsResourceWithStreamingResponse, + AsyncSnapshotsResourceWithStreamingResponse, +) +from .floating_ips import ( + FloatingIPsResource, + AsyncFloatingIPsResource, + FloatingIPsResourceWithRawResponse, + AsyncFloatingIPsResourceWithRawResponse, + FloatingIPsResourceWithStreamingResponse, + AsyncFloatingIPsResourceWithStreamingResponse, +) +from .gpu_droplets import ( + GPUDropletsResource, + AsyncGPUDropletsResource, + GPUDropletsResourceWithRawResponse, + AsyncGPUDropletsResourceWithRawResponse, + GPUDropletsResourceWithStreamingResponse, + AsyncGPUDropletsResourceWithStreamingResponse, +) +from .load_balancers import ( + LoadBalancersResource, + AsyncLoadBalancersResource, + LoadBalancersResourceWithRawResponse, + AsyncLoadBalancersResourceWithRawResponse, + LoadBalancersResourceWithStreamingResponse, + AsyncLoadBalancersResourceWithStreamingResponse, +) +from .destroy_with_associated_resources import ( + DestroyWithAssociatedResourcesResource, + AsyncDestroyWithAssociatedResourcesResource, + DestroyWithAssociatedResourcesResourceWithRawResponse, + AsyncDestroyWithAssociatedResourcesResourceWithRawResponse, + DestroyWithAssociatedResourcesResourceWithStreamingResponse, + AsyncDestroyWithAssociatedResourcesResourceWithStreamingResponse, +) + +__all__ = [ + "BackupsResource", + "AsyncBackupsResource", + "BackupsResourceWithRawResponse", + "AsyncBackupsResourceWithRawResponse", + "BackupsResourceWithStreamingResponse", + "AsyncBackupsResourceWithStreamingResponse", + "ActionsResource", + "AsyncActionsResource", + "ActionsResourceWithRawResponse", + "AsyncActionsResourceWithRawResponse", + "ActionsResourceWithStreamingResponse", + "AsyncActionsResourceWithStreamingResponse", + "DestroyWithAssociatedResourcesResource", + "AsyncDestroyWithAssociatedResourcesResource", + "DestroyWithAssociatedResourcesResourceWithRawResponse", + "AsyncDestroyWithAssociatedResourcesResourceWithRawResponse", + "DestroyWithAssociatedResourcesResourceWithStreamingResponse", + "AsyncDestroyWithAssociatedResourcesResourceWithStreamingResponse", + "AutoscaleResource", + "AsyncAutoscaleResource", + "AutoscaleResourceWithRawResponse", + "AsyncAutoscaleResourceWithRawResponse", + "AutoscaleResourceWithStreamingResponse", + "AsyncAutoscaleResourceWithStreamingResponse", + "FirewallsResource", + "AsyncFirewallsResource", + "FirewallsResourceWithRawResponse", + "AsyncFirewallsResourceWithRawResponse", + "FirewallsResourceWithStreamingResponse", + "AsyncFirewallsResourceWithStreamingResponse", + "FloatingIPsResource", + "AsyncFloatingIPsResource", + "FloatingIPsResourceWithRawResponse", + "AsyncFloatingIPsResourceWithRawResponse", + "FloatingIPsResourceWithStreamingResponse", + "AsyncFloatingIPsResourceWithStreamingResponse", + "ImagesResource", + "AsyncImagesResource", + "ImagesResourceWithRawResponse", + "AsyncImagesResourceWithRawResponse", + "ImagesResourceWithStreamingResponse", + "AsyncImagesResourceWithStreamingResponse", + "LoadBalancersResource", + "AsyncLoadBalancersResource", + "LoadBalancersResourceWithRawResponse", + "AsyncLoadBalancersResourceWithRawResponse", + "LoadBalancersResourceWithStreamingResponse", + "AsyncLoadBalancersResourceWithStreamingResponse", + "SizesResource", + "AsyncSizesResource", + "SizesResourceWithRawResponse", + "AsyncSizesResourceWithRawResponse", + "SizesResourceWithStreamingResponse", + "AsyncSizesResourceWithStreamingResponse", + "SnapshotsResource", + "AsyncSnapshotsResource", + "SnapshotsResourceWithRawResponse", + "AsyncSnapshotsResourceWithRawResponse", + "SnapshotsResourceWithStreamingResponse", + "AsyncSnapshotsResourceWithStreamingResponse", + "VolumesResource", + "AsyncVolumesResource", + "VolumesResourceWithRawResponse", + "AsyncVolumesResourceWithRawResponse", + "VolumesResourceWithStreamingResponse", + "AsyncVolumesResourceWithStreamingResponse", + "AccountResource", + "AsyncAccountResource", + "AccountResourceWithRawResponse", + "AsyncAccountResourceWithRawResponse", + "AccountResourceWithStreamingResponse", + "AsyncAccountResourceWithStreamingResponse", + "GPUDropletsResource", + "AsyncGPUDropletsResource", + "GPUDropletsResourceWithRawResponse", + "AsyncGPUDropletsResourceWithRawResponse", + "GPUDropletsResourceWithStreamingResponse", + "AsyncGPUDropletsResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/gpu_droplets/account/__init__.py b/src/gradient/resources/gpu_droplets/account/__init__.py new file mode 100644 index 00000000..33286c3f --- /dev/null +++ b/src/gradient/resources/gpu_droplets/account/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .keys import ( + KeysResource, + AsyncKeysResource, + KeysResourceWithRawResponse, + AsyncKeysResourceWithRawResponse, + KeysResourceWithStreamingResponse, + AsyncKeysResourceWithStreamingResponse, +) +from .account import ( + AccountResource, + AsyncAccountResource, + AccountResourceWithRawResponse, + AsyncAccountResourceWithRawResponse, + AccountResourceWithStreamingResponse, + AsyncAccountResourceWithStreamingResponse, +) + +__all__ = [ + "KeysResource", + "AsyncKeysResource", + "KeysResourceWithRawResponse", + "AsyncKeysResourceWithRawResponse", + "KeysResourceWithStreamingResponse", + "AsyncKeysResourceWithStreamingResponse", + "AccountResource", + "AsyncAccountResource", + "AccountResourceWithRawResponse", + "AsyncAccountResourceWithRawResponse", + "AccountResourceWithStreamingResponse", + "AsyncAccountResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/gpu_droplets/account/account.py b/src/gradient/resources/gpu_droplets/account/account.py new file mode 100644 index 00000000..5bcaf269 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/account/account.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .keys import ( + KeysResource, + AsyncKeysResource, + KeysResourceWithRawResponse, + AsyncKeysResourceWithRawResponse, + KeysResourceWithStreamingResponse, + AsyncKeysResourceWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["AccountResource", "AsyncAccountResource"] + + +class AccountResource(SyncAPIResource): + @cached_property + def keys(self) -> KeysResource: + return KeysResource(self._client) + + @cached_property + def with_raw_response(self) -> AccountResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AccountResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AccountResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AccountResourceWithStreamingResponse(self) + + +class AsyncAccountResource(AsyncAPIResource): + @cached_property + def keys(self) -> AsyncKeysResource: + return AsyncKeysResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAccountResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncAccountResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAccountResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncAccountResourceWithStreamingResponse(self) + + +class AccountResourceWithRawResponse: + def __init__(self, account: AccountResource) -> None: + self._account = account + + @cached_property + def keys(self) -> KeysResourceWithRawResponse: + return KeysResourceWithRawResponse(self._account.keys) + + +class AsyncAccountResourceWithRawResponse: + def __init__(self, account: AsyncAccountResource) -> None: + self._account = account + + @cached_property + def keys(self) -> AsyncKeysResourceWithRawResponse: + return AsyncKeysResourceWithRawResponse(self._account.keys) + + +class AccountResourceWithStreamingResponse: + def __init__(self, account: AccountResource) -> None: + self._account = account + + @cached_property + def keys(self) -> KeysResourceWithStreamingResponse: + return KeysResourceWithStreamingResponse(self._account.keys) + + +class AsyncAccountResourceWithStreamingResponse: + def __init__(self, account: AsyncAccountResource) -> None: + self._account = account + + @cached_property + def keys(self) -> AsyncKeysResourceWithStreamingResponse: + return AsyncKeysResourceWithStreamingResponse(self._account.keys) diff --git a/src/gradient/resources/gpu_droplets/account/keys.py b/src/gradient/resources/gpu_droplets/account/keys.py new file mode 100644 index 00000000..f50b9945 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/account/keys.py @@ -0,0 +1,588 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union + +import httpx + +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.account import key_list_params, key_create_params, key_update_params +from ....types.gpu_droplets.account.key_list_response import KeyListResponse +from ....types.gpu_droplets.account.key_create_response import KeyCreateResponse +from ....types.gpu_droplets.account.key_update_response import KeyUpdateResponse +from ....types.gpu_droplets.account.key_retrieve_response import KeyRetrieveResponse + +__all__ = ["KeysResource", "AsyncKeysResource"] + + +class KeysResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> KeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return KeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> KeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return KeysResourceWithStreamingResponse(self) + + def create( + self, + *, + name: str, + public_key: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyCreateResponse: + """ + To add a new SSH public key to your DigitalOcean account, send a POST request to + `/v2/account/keys`. Set the `name` attribute to the name you wish to use and the + `public_key` attribute to the full public key you are adding. + + Args: + name: A human-readable display name for this key, used to easily identify the SSH keys + when they are displayed. + + public_key: The entire public key string that was uploaded. Embedded into the root user's + `authorized_keys` file if you include this key during Droplet creation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/account/keys" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/account/keys", + body=maybe_transform( + { + "name": name, + "public_key": public_key, + }, + key_create_params.KeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyCreateResponse, + ) + + def retrieve( + self, + ssh_key_identifier: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyRetrieveResponse: + """ + To get information about a key, send a GET request to `/v2/account/keys/$KEY_ID` + or `/v2/account/keys/$KEY_FINGERPRINT`. The response will be a JSON object with + the key `ssh_key` and value an ssh_key object which contains the standard + ssh_key attributes. + + Args: + ssh_key_identifier: A unique identification number for this key. Can be used to embed a specific SSH + key into a Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/account/keys/{ssh_key_identifier}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/account/keys/{ssh_key_identifier}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyRetrieveResponse, + ) + + def update( + self, + ssh_key_identifier: Union[int, str], + *, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyUpdateResponse: + """ + To update the name of an SSH key, send a PUT request to either + `/v2/account/keys/$SSH_KEY_ID` or `/v2/account/keys/$SSH_KEY_FINGERPRINT`. Set + the `name` attribute to the new name you want to use. + + Args: + ssh_key_identifier: A unique identification number for this key. Can be used to embed a specific SSH + key into a Droplet. + + name: A human-readable display name for this key, used to easily identify the SSH keys + when they are displayed. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._put( + f"/v2/account/keys/{ssh_key_identifier}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/account/keys/{ssh_key_identifier}", + body=maybe_transform({"name": name}, key_update_params.KeyUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListResponse: + """ + To list all of the keys in your account, send a GET request to + `/v2/account/keys`. The response will be a JSON object with a key set to + `ssh_keys`. The value of this will be an array of ssh_key objects, each of which + contains the standard ssh_key attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/account/keys" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/account/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_params.KeyListParams, + ), + ), + cast_to=KeyListResponse, + ) + + def delete( + self, + ssh_key_identifier: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy a public SSH key that you have in your account, send a DELETE request + to `/v2/account/keys/$KEY_ID` or `/v2/account/keys/$KEY_FINGERPRINT`. A 204 + status will be returned, indicating that the action was successful and that the + response body is empty. + + Args: + ssh_key_identifier: A unique identification number for this key. Can be used to embed a specific SSH + key into a Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/account/keys/{ssh_key_identifier}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/account/keys/{ssh_key_identifier}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncKeysResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncKeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncKeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncKeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncKeysResourceWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + public_key: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyCreateResponse: + """ + To add a new SSH public key to your DigitalOcean account, send a POST request to + `/v2/account/keys`. Set the `name` attribute to the name you wish to use and the + `public_key` attribute to the full public key you are adding. + + Args: + name: A human-readable display name for this key, used to easily identify the SSH keys + when they are displayed. + + public_key: The entire public key string that was uploaded. Embedded into the root user's + `authorized_keys` file if you include this key during Droplet creation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/account/keys" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/account/keys", + body=await async_maybe_transform( + { + "name": name, + "public_key": public_key, + }, + key_create_params.KeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyCreateResponse, + ) + + async def retrieve( + self, + ssh_key_identifier: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyRetrieveResponse: + """ + To get information about a key, send a GET request to `/v2/account/keys/$KEY_ID` + or `/v2/account/keys/$KEY_FINGERPRINT`. The response will be a JSON object with + the key `ssh_key` and value an ssh_key object which contains the standard + ssh_key attributes. + + Args: + ssh_key_identifier: A unique identification number for this key. Can be used to embed a specific SSH + key into a Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/account/keys/{ssh_key_identifier}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/account/keys/{ssh_key_identifier}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyRetrieveResponse, + ) + + async def update( + self, + ssh_key_identifier: Union[int, str], + *, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyUpdateResponse: + """ + To update the name of an SSH key, send a PUT request to either + `/v2/account/keys/$SSH_KEY_ID` or `/v2/account/keys/$SSH_KEY_FINGERPRINT`. Set + the `name` attribute to the new name you want to use. + + Args: + ssh_key_identifier: A unique identification number for this key. Can be used to embed a specific SSH + key into a Droplet. + + name: A human-readable display name for this key, used to easily identify the SSH keys + when they are displayed. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._put( + f"/v2/account/keys/{ssh_key_identifier}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/account/keys/{ssh_key_identifier}", + body=await async_maybe_transform({"name": name}, key_update_params.KeyUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KeyUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KeyListResponse: + """ + To list all of the keys in your account, send a GET request to + `/v2/account/keys`. The response will be a JSON object with a key set to + `ssh_keys`. The value of this will be an array of ssh_key objects, each of which + contains the standard ssh_key attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/account/keys" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/account/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + key_list_params.KeyListParams, + ), + ), + cast_to=KeyListResponse, + ) + + async def delete( + self, + ssh_key_identifier: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy a public SSH key that you have in your account, send a DELETE request + to `/v2/account/keys/$KEY_ID` or `/v2/account/keys/$KEY_FINGERPRINT`. A 204 + status will be returned, indicating that the action was successful and that the + response body is empty. + + Args: + ssh_key_identifier: A unique identification number for this key. Can be used to embed a specific SSH + key into a Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/account/keys/{ssh_key_identifier}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/account/keys/{ssh_key_identifier}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class KeysResourceWithRawResponse: + def __init__(self, keys: KeysResource) -> None: + self._keys = keys + + self.create = to_raw_response_wrapper( + keys.create, + ) + self.retrieve = to_raw_response_wrapper( + keys.retrieve, + ) + self.update = to_raw_response_wrapper( + keys.update, + ) + self.list = to_raw_response_wrapper( + keys.list, + ) + self.delete = to_raw_response_wrapper( + keys.delete, + ) + + +class AsyncKeysResourceWithRawResponse: + def __init__(self, keys: AsyncKeysResource) -> None: + self._keys = keys + + self.create = async_to_raw_response_wrapper( + keys.create, + ) + self.retrieve = async_to_raw_response_wrapper( + keys.retrieve, + ) + self.update = async_to_raw_response_wrapper( + keys.update, + ) + self.list = async_to_raw_response_wrapper( + keys.list, + ) + self.delete = async_to_raw_response_wrapper( + keys.delete, + ) + + +class KeysResourceWithStreamingResponse: + def __init__(self, keys: KeysResource) -> None: + self._keys = keys + + self.create = to_streamed_response_wrapper( + keys.create, + ) + self.retrieve = to_streamed_response_wrapper( + keys.retrieve, + ) + self.update = to_streamed_response_wrapper( + keys.update, + ) + self.list = to_streamed_response_wrapper( + keys.list, + ) + self.delete = to_streamed_response_wrapper( + keys.delete, + ) + + +class AsyncKeysResourceWithStreamingResponse: + def __init__(self, keys: AsyncKeysResource) -> None: + self._keys = keys + + self.create = async_to_streamed_response_wrapper( + keys.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + keys.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + keys.update, + ) + self.list = async_to_streamed_response_wrapper( + keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + keys.delete, + ) diff --git a/src/gradient/resources/gpu_droplets/actions.py b/src/gradient/resources/gpu_droplets/actions.py new file mode 100644 index 00000000..a708fb67 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/actions.py @@ -0,0 +1,2048 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, overload + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.gpu_droplets import action_list_params, action_initiate_params, action_bulk_initiate_params +from ...types.droplet_backup_policy_param import DropletBackupPolicyParam +from ...types.gpu_droplets.action_list_response import ActionListResponse +from ...types.gpu_droplets.action_initiate_response import ActionInitiateResponse +from ...types.gpu_droplets.action_retrieve_response import ActionRetrieveResponse +from ...types.gpu_droplets.action_bulk_initiate_response import ActionBulkInitiateResponse + +__all__ = ["ActionsResource", "AsyncActionsResource"] + + +class ActionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ActionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ActionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ActionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ActionsResourceWithStreamingResponse(self) + + def retrieve( + self, + action_id: int, + *, + droplet_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionRetrieveResponse: + """ + To retrieve a Droplet action, send a GET request to + `/v2/droplets/$DROPLET_ID/actions/$ACTION_ID`. + + The response will be a JSON object with a key called `action`. The value will be + a Droplet action object. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/actions/{action_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions/{action_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionRetrieveResponse, + ) + + def list( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionListResponse: + """ + To retrieve a list of all actions that have been executed for a Droplet, send a + GET request to `/v2/droplets/$DROPLET_ID/actions`. + + The results will be returned as a JSON object with an `actions` key. This will + be set to an array filled with `action` objects containing the standard `action` + attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_list_params.ActionListParams, + ), + ), + cast_to=ActionListResponse, + ) + + @overload + def bulk_initiate( + self, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + tag_name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionBulkInitiateResponse: + """Some actions can be performed in bulk on tagged Droplets. + + The actions can be + initiated by sending a POST to `/v2/droplets/actions?tag_name=$TAG_NAME` with + the action arguments. + + Only a sub-set of action types are supported: + + - `power_cycle` + - `power_on` + - `power_off` + - `shutdown` + - `enable_ipv6` + - `enable_backups` + - `disable_backups` + - `snapshot` (also requires `image:create` permission) + + Args: + type: The type of action to initiate for the Droplet. + + tag_name: Used to filter Droplets by a specific tag. Can not be combined with `name` or + `type`. Requires `tag:read` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def bulk_initiate( + self, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + tag_name: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionBulkInitiateResponse: + """Some actions can be performed in bulk on tagged Droplets. + + The actions can be + initiated by sending a POST to `/v2/droplets/actions?tag_name=$TAG_NAME` with + the action arguments. + + Only a sub-set of action types are supported: + + - `power_cycle` + - `power_on` + - `power_off` + - `shutdown` + - `enable_ipv6` + - `enable_backups` + - `disable_backups` + - `snapshot` (also requires `image:create` permission) + + Args: + type: The type of action to initiate for the Droplet. + + tag_name: Used to filter Droplets by a specific tag. Can not be combined with `name` or + `type`. Requires `tag:read` scope. + + name: The name to give the new snapshot of the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["type"]) + def bulk_initiate( + self, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + tag_name: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionBulkInitiateResponse: + return self._post( + "/v2/droplets/actions" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/actions", + body=maybe_transform( + { + "type": type, + "name": name, + }, + action_bulk_initiate_params.ActionBulkInitiateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"tag_name": tag_name}, action_bulk_initiate_params.ActionBulkInitiateParams), + ), + cast_to=ActionBulkInitiateResponse, + ) + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + backup_policy: DropletBackupPolicyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + backup_policy: An object specifying the backup policy for the Droplet. If omitted, the backup + plan will default to daily. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + backup_policy: DropletBackupPolicyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + backup_policy: An object specifying the backup policy for the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + image: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + image: The ID of a backup of the current Droplet instance to restore from. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + disk: bool | Omit = omit, + size: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + disk: When `true`, the Droplet's disk will be resized in addition to its RAM and CPU. + This is a permanent change and cannot be reversed as a Droplet's disk size + cannot be decreased. + + size: The slug identifier for the size to which you wish to resize the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + image: Union[str, int] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + image: The image ID of a public or private image or the slug identifier for a public + image. The Droplet will be rebuilt using this image as its base. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + name: The new name for the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + kernel: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + kernel: A unique number used to identify and reference a specific kernel. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + name: The name to give the new snapshot of the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["type"]) + def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + backup_policy: DropletBackupPolicyParam | Omit = omit, + image: int | Union[str, int] | Omit = omit, + disk: bool | Omit = omit, + size: str | Omit = omit, + name: str | Omit = omit, + kernel: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + return self._post( + f"/v2/droplets/{droplet_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions", + body=maybe_transform( + { + "type": type, + "backup_policy": backup_policy, + "image": image, + "disk": disk, + "size": size, + "name": name, + "kernel": kernel, + }, + action_initiate_params.ActionInitiateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionInitiateResponse, + ) + + +class AsyncActionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncActionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncActionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncActionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncActionsResourceWithStreamingResponse(self) + + async def retrieve( + self, + action_id: int, + *, + droplet_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionRetrieveResponse: + """ + To retrieve a Droplet action, send a GET request to + `/v2/droplets/$DROPLET_ID/actions/$ACTION_ID`. + + The response will be a JSON object with a key called `action`. The value will be + a Droplet action object. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/actions/{action_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions/{action_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionRetrieveResponse, + ) + + async def list( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionListResponse: + """ + To retrieve a list of all actions that have been executed for a Droplet, send a + GET request to `/v2/droplets/$DROPLET_ID/actions`. + + The results will be returned as a JSON object with an `actions` key. This will + be set to an array filled with `action` objects containing the standard `action` + attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_list_params.ActionListParams, + ), + ), + cast_to=ActionListResponse, + ) + + @overload + async def bulk_initiate( + self, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + tag_name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionBulkInitiateResponse: + """Some actions can be performed in bulk on tagged Droplets. + + The actions can be + initiated by sending a POST to `/v2/droplets/actions?tag_name=$TAG_NAME` with + the action arguments. + + Only a sub-set of action types are supported: + + - `power_cycle` + - `power_on` + - `power_off` + - `shutdown` + - `enable_ipv6` + - `enable_backups` + - `disable_backups` + - `snapshot` (also requires `image:create` permission) + + Args: + type: The type of action to initiate for the Droplet. + + tag_name: Used to filter Droplets by a specific tag. Can not be combined with `name` or + `type`. Requires `tag:read` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def bulk_initiate( + self, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + tag_name: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionBulkInitiateResponse: + """Some actions can be performed in bulk on tagged Droplets. + + The actions can be + initiated by sending a POST to `/v2/droplets/actions?tag_name=$TAG_NAME` with + the action arguments. + + Only a sub-set of action types are supported: + + - `power_cycle` + - `power_on` + - `power_off` + - `shutdown` + - `enable_ipv6` + - `enable_backups` + - `disable_backups` + - `snapshot` (also requires `image:create` permission) + + Args: + type: The type of action to initiate for the Droplet. + + tag_name: Used to filter Droplets by a specific tag. Can not be combined with `name` or + `type`. Requires `tag:read` scope. + + name: The name to give the new snapshot of the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["type"]) + async def bulk_initiate( + self, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + tag_name: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionBulkInitiateResponse: + return await self._post( + "/v2/droplets/actions" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/actions", + body=await async_maybe_transform( + { + "type": type, + "name": name, + }, + action_bulk_initiate_params.ActionBulkInitiateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"tag_name": tag_name}, action_bulk_initiate_params.ActionBulkInitiateParams + ), + ), + cast_to=ActionBulkInitiateResponse, + ) + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + backup_policy: DropletBackupPolicyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + backup_policy: An object specifying the backup policy for the Droplet. If omitted, the backup + plan will default to daily. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + backup_policy: DropletBackupPolicyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + backup_policy: An object specifying the backup policy for the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + image: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + image: The ID of a backup of the current Droplet instance to restore from. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + disk: bool | Omit = omit, + size: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + disk: When `true`, the Droplet's disk will be resized in addition to its RAM and CPU. + This is a permanent change and cannot be reversed as a Droplet's disk size + cannot be decreased. + + size: The slug identifier for the size to which you wish to resize the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + image: Union[str, int] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + image: The image ID of a public or private image or the slug identifier for a public + image. The Droplet will be rebuilt using this image as its base. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + name: The new name for the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + kernel: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + kernel: A unique number used to identify and reference a specific kernel. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + """ + To initiate an action on a Droplet send a POST request to + `/v2/droplets/$DROPLET_ID/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | Additionally Required Permission | + | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | + | `enable_backups` | Enables backups for a Droplet | | + | `disable_backups` | Disables backups for a Droplet | | + | `change_backup_policy` | Update the backup policy for a Droplet | | + | `reboot` | Reboots a Droplet. A `reboot` action is an attempt to reboot the Droplet in a graceful way, similar to using the `reboot` command from the console. | | + | `power_cycle` | Power cycles a Droplet. A `powercycle` action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. | | + | `shutdown` | Shutsdown a Droplet. A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the `shutdown` command from the console. Since a `shutdown` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a `power_off` action to ensure the Droplet is off. | | + | `power_off` | Powers off a Droplet. A `power_off` event is a hard shutdown and should only be used if the `shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. | | + | `power_on` | Powers on a Droplet. | | + | `restore` | Restore a Droplet using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. | droplet:admin | + | `password_reset` | Resets the root password for a Droplet. A new password will be provided via email. It must be changed after first use. | droplet:admin | + | `resize` | Resizes a Droplet. Set the `size` attribute to a size slug. If a permanent resize with disk changes included is desired, set the `disk` attribute to `true`. | droplet:create | + | `rebuild` | Rebuilds a Droplet from a new base image. Set the `image` attribute to an image ID or slug. | droplet:admin | + | `rename` | Renames a Droplet. | | + | `change_kernel` | Changes a Droplet's kernel. Only applies to Droplets with externally managed kernels. All Droplets created after March 2017 use internal kernels by default. | | + | `enable_ipv6` | Enables IPv6 for a Droplet. Once enabled for a Droplet, IPv6 can not be disabled. When enabling IPv6 on an existing Droplet, [additional OS-level configuration](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) is required. | | + | `snapshot` | Takes a snapshot of a Droplet. | image:create | + + Args: + type: The type of action to initiate for the Droplet. + + name: The name to give the new snapshot of the Droplet. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["type"]) + async def initiate( + self, + droplet_id: int, + *, + type: Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ], + backup_policy: DropletBackupPolicyParam | Omit = omit, + image: int | Union[str, int] | Omit = omit, + disk: bool | Omit = omit, + size: str | Omit = omit, + name: str | Omit = omit, + kernel: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateResponse: + return await self._post( + f"/v2/droplets/{droplet_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions", + body=await async_maybe_transform( + { + "type": type, + "backup_policy": backup_policy, + "image": image, + "disk": disk, + "size": size, + "name": name, + "kernel": kernel, + }, + action_initiate_params.ActionInitiateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionInitiateResponse, + ) + + +class ActionsResourceWithRawResponse: + def __init__(self, actions: ActionsResource) -> None: + self._actions = actions + + self.retrieve = to_raw_response_wrapper( + actions.retrieve, + ) + self.list = to_raw_response_wrapper( + actions.list, + ) + self.bulk_initiate = to_raw_response_wrapper( + actions.bulk_initiate, + ) + self.initiate = to_raw_response_wrapper( + actions.initiate, + ) + + +class AsyncActionsResourceWithRawResponse: + def __init__(self, actions: AsyncActionsResource) -> None: + self._actions = actions + + self.retrieve = async_to_raw_response_wrapper( + actions.retrieve, + ) + self.list = async_to_raw_response_wrapper( + actions.list, + ) + self.bulk_initiate = async_to_raw_response_wrapper( + actions.bulk_initiate, + ) + self.initiate = async_to_raw_response_wrapper( + actions.initiate, + ) + + +class ActionsResourceWithStreamingResponse: + def __init__(self, actions: ActionsResource) -> None: + self._actions = actions + + self.retrieve = to_streamed_response_wrapper( + actions.retrieve, + ) + self.list = to_streamed_response_wrapper( + actions.list, + ) + self.bulk_initiate = to_streamed_response_wrapper( + actions.bulk_initiate, + ) + self.initiate = to_streamed_response_wrapper( + actions.initiate, + ) + + +class AsyncActionsResourceWithStreamingResponse: + def __init__(self, actions: AsyncActionsResource) -> None: + self._actions = actions + + self.retrieve = async_to_streamed_response_wrapper( + actions.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + actions.list, + ) + self.bulk_initiate = async_to_streamed_response_wrapper( + actions.bulk_initiate, + ) + self.initiate = async_to_streamed_response_wrapper( + actions.initiate, + ) diff --git a/src/gradient/resources/gpu_droplets/autoscale.py b/src/gradient/resources/gpu_droplets/autoscale.py new file mode 100644 index 00000000..8df17f7a --- /dev/null +++ b/src/gradient/resources/gpu_droplets/autoscale.py @@ -0,0 +1,967 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.gpu_droplets import ( + autoscale_list_params, + autoscale_create_params, + autoscale_update_params, + autoscale_list_history_params, + autoscale_list_members_params, +) +from ...types.gpu_droplets.autoscale_list_response import AutoscaleListResponse +from ...types.gpu_droplets.autoscale_create_response import AutoscaleCreateResponse +from ...types.gpu_droplets.autoscale_update_response import AutoscaleUpdateResponse +from ...types.gpu_droplets.autoscale_retrieve_response import AutoscaleRetrieveResponse +from ...types.gpu_droplets.autoscale_list_history_response import AutoscaleListHistoryResponse +from ...types.gpu_droplets.autoscale_list_members_response import AutoscaleListMembersResponse +from ...types.gpu_droplets.autoscale_pool_droplet_template_param import AutoscalePoolDropletTemplateParam + +__all__ = ["AutoscaleResource", "AsyncAutoscaleResource"] + + +class AutoscaleResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AutoscaleResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AutoscaleResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AutoscaleResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AutoscaleResourceWithStreamingResponse(self) + + def create( + self, + *, + config: autoscale_create_params.Config, + droplet_template: AutoscalePoolDropletTemplateParam, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleCreateResponse: + """ + To create a new autoscale pool, send a POST request to `/v2/droplets/autoscale` + setting the required attributes. + + The response body will contain a JSON object with a key called `autoscale_pool` + containing the standard attributes for the new autoscale pool. + + Args: + config: The scaling configuration for an autoscale pool, which is how the pool scales up + and down (either by resource utilization or static configuration). + + name: The human-readable name of the autoscale pool. This field cannot be updated + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/droplets/autoscale" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/autoscale", + body=maybe_transform( + { + "config": config, + "droplet_template": droplet_template, + "name": name, + }, + autoscale_create_params.AutoscaleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutoscaleCreateResponse, + ) + + def retrieve( + self, + autoscale_pool_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleRetrieveResponse: + """ + To show information about an individual autoscale pool, send a GET request to + `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + return self._get( + f"/v2/droplets/autoscale/{autoscale_pool_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutoscaleRetrieveResponse, + ) + + def update( + self, + autoscale_pool_id: str, + *, + config: autoscale_update_params.Config, + droplet_template: AutoscalePoolDropletTemplateParam, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleUpdateResponse: + """ + To update the configuration of an existing autoscale pool, send a PUT request to + `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID`. The request must contain a full + representation of the autoscale pool including existing attributes. + + Args: + config: The scaling configuration for an autoscale pool, which is how the pool scales up + and down (either by resource utilization or static configuration). + + name: The human-readable name of the autoscale pool. This field cannot be updated + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + return self._put( + f"/v2/droplets/autoscale/{autoscale_pool_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}", + body=maybe_transform( + { + "config": config, + "droplet_template": droplet_template, + "name": name, + }, + autoscale_update_params.AutoscaleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutoscaleUpdateResponse, + ) + + def list( + self, + *, + name: str | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleListResponse: + """ + To list all autoscale pools in your team, send a GET request to + `/v2/droplets/autoscale`. The response body will be a JSON object with a key of + `autoscale_pools` containing an array of autoscale pool objects. These each + contain the standard autoscale pool attributes. + + Args: + name: The name of the autoscale pool + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/droplets/autoscale" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/autoscale", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "name": name, + "page": page, + "per_page": per_page, + }, + autoscale_list_params.AutoscaleListParams, + ), + ), + cast_to=AutoscaleListResponse, + ) + + def delete( + self, + autoscale_pool_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy an autoscale pool, send a DELETE request to the + `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID` endpoint. + + A successful response will include a 202 response code and no content. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/droplets/autoscale/{autoscale_pool_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def delete_dangerous( + self, + autoscale_pool_id: str, + *, + x_dangerous: bool, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy an autoscale pool and its associated resources (Droplets), send a + DELETE request to the `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID/dangerous` + endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers.update({"X-Dangerous": ("true" if x_dangerous else "false")}) + return self._delete( + f"/v2/droplets/autoscale/{autoscale_pool_id}/dangerous" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}/dangerous", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def list_history( + self, + autoscale_pool_id: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleListHistoryResponse: + """ + To list all of the scaling history events of an autoscale pool, send a GET + request to `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID/history`. + + The response body will be a JSON object with a key of `history`. This will be + set to an array containing objects each representing a history event. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + return self._get( + f"/v2/droplets/autoscale/{autoscale_pool_id}/history" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}/history", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + autoscale_list_history_params.AutoscaleListHistoryParams, + ), + ), + cast_to=AutoscaleListHistoryResponse, + ) + + def list_members( + self, + autoscale_pool_id: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleListMembersResponse: + """ + To list the Droplets in an autoscale pool, send a GET request to + `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID/members`. + + The response body will be a JSON object with a key of `droplets`. This will be + set to an array containing information about each of the Droplets in the + autoscale pool. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + return self._get( + f"/v2/droplets/autoscale/{autoscale_pool_id}/members" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}/members", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + autoscale_list_members_params.AutoscaleListMembersParams, + ), + ), + cast_to=AutoscaleListMembersResponse, + ) + + +class AsyncAutoscaleResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAutoscaleResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncAutoscaleResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAutoscaleResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncAutoscaleResourceWithStreamingResponse(self) + + async def create( + self, + *, + config: autoscale_create_params.Config, + droplet_template: AutoscalePoolDropletTemplateParam, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleCreateResponse: + """ + To create a new autoscale pool, send a POST request to `/v2/droplets/autoscale` + setting the required attributes. + + The response body will contain a JSON object with a key called `autoscale_pool` + containing the standard attributes for the new autoscale pool. + + Args: + config: The scaling configuration for an autoscale pool, which is how the pool scales up + and down (either by resource utilization or static configuration). + + name: The human-readable name of the autoscale pool. This field cannot be updated + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/droplets/autoscale" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/autoscale", + body=await async_maybe_transform( + { + "config": config, + "droplet_template": droplet_template, + "name": name, + }, + autoscale_create_params.AutoscaleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutoscaleCreateResponse, + ) + + async def retrieve( + self, + autoscale_pool_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleRetrieveResponse: + """ + To show information about an individual autoscale pool, send a GET request to + `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + return await self._get( + f"/v2/droplets/autoscale/{autoscale_pool_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutoscaleRetrieveResponse, + ) + + async def update( + self, + autoscale_pool_id: str, + *, + config: autoscale_update_params.Config, + droplet_template: AutoscalePoolDropletTemplateParam, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleUpdateResponse: + """ + To update the configuration of an existing autoscale pool, send a PUT request to + `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID`. The request must contain a full + representation of the autoscale pool including existing attributes. + + Args: + config: The scaling configuration for an autoscale pool, which is how the pool scales up + and down (either by resource utilization or static configuration). + + name: The human-readable name of the autoscale pool. This field cannot be updated + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + return await self._put( + f"/v2/droplets/autoscale/{autoscale_pool_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}", + body=await async_maybe_transform( + { + "config": config, + "droplet_template": droplet_template, + "name": name, + }, + autoscale_update_params.AutoscaleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AutoscaleUpdateResponse, + ) + + async def list( + self, + *, + name: str | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleListResponse: + """ + To list all autoscale pools in your team, send a GET request to + `/v2/droplets/autoscale`. The response body will be a JSON object with a key of + `autoscale_pools` containing an array of autoscale pool objects. These each + contain the standard autoscale pool attributes. + + Args: + name: The name of the autoscale pool + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/droplets/autoscale" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/autoscale", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "name": name, + "page": page, + "per_page": per_page, + }, + autoscale_list_params.AutoscaleListParams, + ), + ), + cast_to=AutoscaleListResponse, + ) + + async def delete( + self, + autoscale_pool_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy an autoscale pool, send a DELETE request to the + `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID` endpoint. + + A successful response will include a 202 response code and no content. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/droplets/autoscale/{autoscale_pool_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def delete_dangerous( + self, + autoscale_pool_id: str, + *, + x_dangerous: bool, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy an autoscale pool and its associated resources (Droplets), send a + DELETE request to the `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID/dangerous` + endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers.update({"X-Dangerous": ("true" if x_dangerous else "false")}) + return await self._delete( + f"/v2/droplets/autoscale/{autoscale_pool_id}/dangerous" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}/dangerous", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def list_history( + self, + autoscale_pool_id: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleListHistoryResponse: + """ + To list all of the scaling history events of an autoscale pool, send a GET + request to `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID/history`. + + The response body will be a JSON object with a key of `history`. This will be + set to an array containing objects each representing a history event. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + return await self._get( + f"/v2/droplets/autoscale/{autoscale_pool_id}/history" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}/history", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + autoscale_list_history_params.AutoscaleListHistoryParams, + ), + ), + cast_to=AutoscaleListHistoryResponse, + ) + + async def list_members( + self, + autoscale_pool_id: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AutoscaleListMembersResponse: + """ + To list the Droplets in an autoscale pool, send a GET request to + `/v2/droplets/autoscale/$AUTOSCALE_POOL_ID/members`. + + The response body will be a JSON object with a key of `droplets`. This will be + set to an array containing information about each of the Droplets in the + autoscale pool. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not autoscale_pool_id: + raise ValueError(f"Expected a non-empty value for `autoscale_pool_id` but received {autoscale_pool_id!r}") + return await self._get( + f"/v2/droplets/autoscale/{autoscale_pool_id}/members" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/autoscale/{autoscale_pool_id}/members", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + autoscale_list_members_params.AutoscaleListMembersParams, + ), + ), + cast_to=AutoscaleListMembersResponse, + ) + + +class AutoscaleResourceWithRawResponse: + def __init__(self, autoscale: AutoscaleResource) -> None: + self._autoscale = autoscale + + self.create = to_raw_response_wrapper( + autoscale.create, + ) + self.retrieve = to_raw_response_wrapper( + autoscale.retrieve, + ) + self.update = to_raw_response_wrapper( + autoscale.update, + ) + self.list = to_raw_response_wrapper( + autoscale.list, + ) + self.delete = to_raw_response_wrapper( + autoscale.delete, + ) + self.delete_dangerous = to_raw_response_wrapper( + autoscale.delete_dangerous, + ) + self.list_history = to_raw_response_wrapper( + autoscale.list_history, + ) + self.list_members = to_raw_response_wrapper( + autoscale.list_members, + ) + + +class AsyncAutoscaleResourceWithRawResponse: + def __init__(self, autoscale: AsyncAutoscaleResource) -> None: + self._autoscale = autoscale + + self.create = async_to_raw_response_wrapper( + autoscale.create, + ) + self.retrieve = async_to_raw_response_wrapper( + autoscale.retrieve, + ) + self.update = async_to_raw_response_wrapper( + autoscale.update, + ) + self.list = async_to_raw_response_wrapper( + autoscale.list, + ) + self.delete = async_to_raw_response_wrapper( + autoscale.delete, + ) + self.delete_dangerous = async_to_raw_response_wrapper( + autoscale.delete_dangerous, + ) + self.list_history = async_to_raw_response_wrapper( + autoscale.list_history, + ) + self.list_members = async_to_raw_response_wrapper( + autoscale.list_members, + ) + + +class AutoscaleResourceWithStreamingResponse: + def __init__(self, autoscale: AutoscaleResource) -> None: + self._autoscale = autoscale + + self.create = to_streamed_response_wrapper( + autoscale.create, + ) + self.retrieve = to_streamed_response_wrapper( + autoscale.retrieve, + ) + self.update = to_streamed_response_wrapper( + autoscale.update, + ) + self.list = to_streamed_response_wrapper( + autoscale.list, + ) + self.delete = to_streamed_response_wrapper( + autoscale.delete, + ) + self.delete_dangerous = to_streamed_response_wrapper( + autoscale.delete_dangerous, + ) + self.list_history = to_streamed_response_wrapper( + autoscale.list_history, + ) + self.list_members = to_streamed_response_wrapper( + autoscale.list_members, + ) + + +class AsyncAutoscaleResourceWithStreamingResponse: + def __init__(self, autoscale: AsyncAutoscaleResource) -> None: + self._autoscale = autoscale + + self.create = async_to_streamed_response_wrapper( + autoscale.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + autoscale.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + autoscale.update, + ) + self.list = async_to_streamed_response_wrapper( + autoscale.list, + ) + self.delete = async_to_streamed_response_wrapper( + autoscale.delete, + ) + self.delete_dangerous = async_to_streamed_response_wrapper( + autoscale.delete_dangerous, + ) + self.list_history = async_to_streamed_response_wrapper( + autoscale.list_history, + ) + self.list_members = async_to_streamed_response_wrapper( + autoscale.list_members, + ) diff --git a/src/gradient/resources/gpu_droplets/backups.py b/src/gradient/resources/gpu_droplets/backups.py new file mode 100644 index 00000000..065aa3d1 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/backups.py @@ -0,0 +1,460 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.gpu_droplets import backup_list_params, backup_list_policies_params +from ...types.gpu_droplets.backup_list_response import BackupListResponse +from ...types.gpu_droplets.backup_list_policies_response import BackupListPoliciesResponse +from ...types.gpu_droplets.backup_retrieve_policy_response import BackupRetrievePolicyResponse +from ...types.gpu_droplets.backup_list_supported_policies_response import BackupListSupportedPoliciesResponse + +__all__ = ["BackupsResource", "AsyncBackupsResource"] + + +class BackupsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BackupsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return BackupsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BackupsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return BackupsResourceWithStreamingResponse(self) + + def list( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BackupListResponse: + """ + To retrieve any backups associated with a Droplet, send a GET request to + `/v2/droplets/$DROPLET_ID/backups`. + + You will get back a JSON object that has a `backups` key. This will be set to an + array of backup objects, each of which contain the standard Droplet backup + attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/backups" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/backups", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + backup_list_params.BackupListParams, + ), + ), + cast_to=BackupListResponse, + ) + + def list_policies( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BackupListPoliciesResponse: + """ + To list information about the backup policies for all Droplets in the account, + send a GET request to `/v2/droplets/backups/policies`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/droplets/backups/policies" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/backups/policies", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + backup_list_policies_params.BackupListPoliciesParams, + ), + ), + cast_to=BackupListPoliciesResponse, + ) + + def list_supported_policies( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BackupListSupportedPoliciesResponse: + """ + To retrieve a list of all supported Droplet backup policies, send a GET request + to `/v2/droplets/backups/supported_policies`. + """ + return self._get( + "/v2/droplets/backups/supported_policies" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/backups/supported_policies", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BackupListSupportedPoliciesResponse, + ) + + def retrieve_policy( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BackupRetrievePolicyResponse: + """ + To show information about an individual Droplet's backup policy, send a GET + request to `/v2/droplets/$DROPLET_ID/backups/policy`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/backups/policy" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/backups/policy", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BackupRetrievePolicyResponse, + ) + + +class AsyncBackupsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBackupsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncBackupsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBackupsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncBackupsResourceWithStreamingResponse(self) + + async def list( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BackupListResponse: + """ + To retrieve any backups associated with a Droplet, send a GET request to + `/v2/droplets/$DROPLET_ID/backups`. + + You will get back a JSON object that has a `backups` key. This will be set to an + array of backup objects, each of which contain the standard Droplet backup + attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/backups" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/backups", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + backup_list_params.BackupListParams, + ), + ), + cast_to=BackupListResponse, + ) + + async def list_policies( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BackupListPoliciesResponse: + """ + To list information about the backup policies for all Droplets in the account, + send a GET request to `/v2/droplets/backups/policies`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/droplets/backups/policies" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/backups/policies", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + backup_list_policies_params.BackupListPoliciesParams, + ), + ), + cast_to=BackupListPoliciesResponse, + ) + + async def list_supported_policies( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BackupListSupportedPoliciesResponse: + """ + To retrieve a list of all supported Droplet backup policies, send a GET request + to `/v2/droplets/backups/supported_policies`. + """ + return await self._get( + "/v2/droplets/backups/supported_policies" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/droplets/backups/supported_policies", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BackupListSupportedPoliciesResponse, + ) + + async def retrieve_policy( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BackupRetrievePolicyResponse: + """ + To show information about an individual Droplet's backup policy, send a GET + request to `/v2/droplets/$DROPLET_ID/backups/policy`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/backups/policy" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/backups/policy", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BackupRetrievePolicyResponse, + ) + + +class BackupsResourceWithRawResponse: + def __init__(self, backups: BackupsResource) -> None: + self._backups = backups + + self.list = to_raw_response_wrapper( + backups.list, + ) + self.list_policies = to_raw_response_wrapper( + backups.list_policies, + ) + self.list_supported_policies = to_raw_response_wrapper( + backups.list_supported_policies, + ) + self.retrieve_policy = to_raw_response_wrapper( + backups.retrieve_policy, + ) + + +class AsyncBackupsResourceWithRawResponse: + def __init__(self, backups: AsyncBackupsResource) -> None: + self._backups = backups + + self.list = async_to_raw_response_wrapper( + backups.list, + ) + self.list_policies = async_to_raw_response_wrapper( + backups.list_policies, + ) + self.list_supported_policies = async_to_raw_response_wrapper( + backups.list_supported_policies, + ) + self.retrieve_policy = async_to_raw_response_wrapper( + backups.retrieve_policy, + ) + + +class BackupsResourceWithStreamingResponse: + def __init__(self, backups: BackupsResource) -> None: + self._backups = backups + + self.list = to_streamed_response_wrapper( + backups.list, + ) + self.list_policies = to_streamed_response_wrapper( + backups.list_policies, + ) + self.list_supported_policies = to_streamed_response_wrapper( + backups.list_supported_policies, + ) + self.retrieve_policy = to_streamed_response_wrapper( + backups.retrieve_policy, + ) + + +class AsyncBackupsResourceWithStreamingResponse: + def __init__(self, backups: AsyncBackupsResource) -> None: + self._backups = backups + + self.list = async_to_streamed_response_wrapper( + backups.list, + ) + self.list_policies = async_to_streamed_response_wrapper( + backups.list_policies, + ) + self.list_supported_policies = async_to_streamed_response_wrapper( + backups.list_supported_policies, + ) + self.retrieve_policy = async_to_streamed_response_wrapper( + backups.retrieve_policy, + ) diff --git a/src/gradient/resources/gpu_droplets/destroy_with_associated_resources.py b/src/gradient/resources/gpu_droplets/destroy_with_associated_resources.py new file mode 100644 index 00000000..2ccad852 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/destroy_with_associated_resources.py @@ -0,0 +1,622 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.gpu_droplets import destroy_with_associated_resource_delete_selective_params +from ...types.gpu_droplets.destroy_with_associated_resource_list_response import ( + DestroyWithAssociatedResourceListResponse, +) +from ...types.gpu_droplets.destroy_with_associated_resource_check_status_response import ( + DestroyWithAssociatedResourceCheckStatusResponse, +) + +__all__ = ["DestroyWithAssociatedResourcesResource", "AsyncDestroyWithAssociatedResourcesResource"] + + +class DestroyWithAssociatedResourcesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DestroyWithAssociatedResourcesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return DestroyWithAssociatedResourcesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DestroyWithAssociatedResourcesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return DestroyWithAssociatedResourcesResourceWithStreamingResponse(self) + + def list( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DestroyWithAssociatedResourceListResponse: + """ + To list the associated billable resources that can be destroyed along with a + Droplet, send a GET request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources` endpoint. + + This endpoint will only return resources that you are authorized to see. For + example, to see associated Reserved IPs, include the `reserved_ip:read` scope. + + The response will be a JSON object containing `snapshots`, `volumes`, and + `volume_snapshots` keys. Each will be set to an array of objects containing + information about the associated resources. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DestroyWithAssociatedResourceListResponse, + ) + + def check_status( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DestroyWithAssociatedResourceCheckStatusResponse: + """ + To check on the status of a request to destroy a Droplet with its associated + resources, send a GET request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources/status` endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources/status" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources/status", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DestroyWithAssociatedResourceCheckStatusResponse, + ) + + def delete_dangerous( + self, + droplet_id: int, + *, + x_dangerous: bool, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy a Droplet along with all of its associated resources, send a DELETE + request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources/dangerous` endpoint. + The headers of this request must include an `X-Dangerous` key set to `true`. To + preview which resources will be destroyed, first query the Droplet's associated + resources. This operation _can not_ be reverse and should be used with caution. + + A successful response will include a 202 response code and no content. Use the + status endpoint to check on the success or failure of the destruction of the + individual resources. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers.update({"X-Dangerous": ("true" if x_dangerous else "false")}) + return self._delete( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources/dangerous" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources/dangerous", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def delete_selective( + self, + droplet_id: int, + *, + floating_ips: SequenceNotStr[str] | Omit = omit, + reserved_ips: SequenceNotStr[str] | Omit = omit, + snapshots: SequenceNotStr[str] | Omit = omit, + volume_snapshots: SequenceNotStr[str] | Omit = omit, + volumes: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy a Droplet along with a sub-set of its associated resources, send a + DELETE request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources/selective` endpoint. + The JSON body of the request should include `reserved_ips`, `snapshots`, + `volumes`, or `volume_snapshots` keys each set to an array of IDs for the + associated resources to be destroyed. The IDs can be found by querying the + Droplet's associated resources. Any associated resource not included in the + request will remain and continue to accrue changes on your account. + + A successful response will include a 202 response code and no content. Use the + status endpoint to check on the success or failure of the destruction of the + individual resources. + + Args: + floating_ips: An array of unique identifiers for the floating IPs to be scheduled for + deletion. + + reserved_ips: An array of unique identifiers for the reserved IPs to be scheduled for + deletion. + + snapshots: An array of unique identifiers for the snapshots to be scheduled for deletion. + + volume_snapshots: An array of unique identifiers for the volume snapshots to be scheduled for + deletion. + + volumes: An array of unique identifiers for the volumes to be scheduled for deletion. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources/selective" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources/selective", + body=maybe_transform( + { + "floating_ips": floating_ips, + "reserved_ips": reserved_ips, + "snapshots": snapshots, + "volume_snapshots": volume_snapshots, + "volumes": volumes, + }, + destroy_with_associated_resource_delete_selective_params.DestroyWithAssociatedResourceDeleteSelectiveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def retry( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + If the status of a request to destroy a Droplet with its associated resources + reported any errors, it can be retried by sending a POST request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources/retry` endpoint. + + Only one destroy can be active at a time per Droplet. If a retry is issued while + another destroy is in progress for the Droplet a 409 status code will be + returned. A successful response will include a 202 response code and no content. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources/retry" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources/retry", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncDestroyWithAssociatedResourcesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDestroyWithAssociatedResourcesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncDestroyWithAssociatedResourcesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDestroyWithAssociatedResourcesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncDestroyWithAssociatedResourcesResourceWithStreamingResponse(self) + + async def list( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DestroyWithAssociatedResourceListResponse: + """ + To list the associated billable resources that can be destroyed along with a + Droplet, send a GET request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources` endpoint. + + This endpoint will only return resources that you are authorized to see. For + example, to see associated Reserved IPs, include the `reserved_ip:read` scope. + + The response will be a JSON object containing `snapshots`, `volumes`, and + `volume_snapshots` keys. Each will be set to an array of objects containing + information about the associated resources. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DestroyWithAssociatedResourceListResponse, + ) + + async def check_status( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DestroyWithAssociatedResourceCheckStatusResponse: + """ + To check on the status of a request to destroy a Droplet with its associated + resources, send a GET request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources/status` endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources/status" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources/status", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DestroyWithAssociatedResourceCheckStatusResponse, + ) + + async def delete_dangerous( + self, + droplet_id: int, + *, + x_dangerous: bool, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy a Droplet along with all of its associated resources, send a DELETE + request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources/dangerous` endpoint. + The headers of this request must include an `X-Dangerous` key set to `true`. To + preview which resources will be destroyed, first query the Droplet's associated + resources. This operation _can not_ be reverse and should be used with caution. + + A successful response will include a 202 response code and no content. Use the + status endpoint to check on the success or failure of the destruction of the + individual resources. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers.update({"X-Dangerous": ("true" if x_dangerous else "false")}) + return await self._delete( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources/dangerous" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources/dangerous", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def delete_selective( + self, + droplet_id: int, + *, + floating_ips: SequenceNotStr[str] | Omit = omit, + reserved_ips: SequenceNotStr[str] | Omit = omit, + snapshots: SequenceNotStr[str] | Omit = omit, + volume_snapshots: SequenceNotStr[str] | Omit = omit, + volumes: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To destroy a Droplet along with a sub-set of its associated resources, send a + DELETE request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources/selective` endpoint. + The JSON body of the request should include `reserved_ips`, `snapshots`, + `volumes`, or `volume_snapshots` keys each set to an array of IDs for the + associated resources to be destroyed. The IDs can be found by querying the + Droplet's associated resources. Any associated resource not included in the + request will remain and continue to accrue changes on your account. + + A successful response will include a 202 response code and no content. Use the + status endpoint to check on the success or failure of the destruction of the + individual resources. + + Args: + floating_ips: An array of unique identifiers for the floating IPs to be scheduled for + deletion. + + reserved_ips: An array of unique identifiers for the reserved IPs to be scheduled for + deletion. + + snapshots: An array of unique identifiers for the snapshots to be scheduled for deletion. + + volume_snapshots: An array of unique identifiers for the volume snapshots to be scheduled for + deletion. + + volumes: An array of unique identifiers for the volumes to be scheduled for deletion. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources/selective" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources/selective", + body=await async_maybe_transform( + { + "floating_ips": floating_ips, + "reserved_ips": reserved_ips, + "snapshots": snapshots, + "volume_snapshots": volume_snapshots, + "volumes": volumes, + }, + destroy_with_associated_resource_delete_selective_params.DestroyWithAssociatedResourceDeleteSelectiveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def retry( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + If the status of a request to destroy a Droplet with its associated resources + reported any errors, it can be retried by sending a POST request to the + `/v2/droplets/$DROPLET_ID/destroy_with_associated_resources/retry` endpoint. + + Only one destroy can be active at a time per Droplet. If a retry is issued while + another destroy is in progress for the Droplet a 409 status code will be + returned. A successful response will include a 202 response code and no content. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/v2/droplets/{droplet_id}/destroy_with_associated_resources/retry" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/destroy_with_associated_resources/retry", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class DestroyWithAssociatedResourcesResourceWithRawResponse: + def __init__(self, destroy_with_associated_resources: DestroyWithAssociatedResourcesResource) -> None: + self._destroy_with_associated_resources = destroy_with_associated_resources + + self.list = to_raw_response_wrapper( + destroy_with_associated_resources.list, + ) + self.check_status = to_raw_response_wrapper( + destroy_with_associated_resources.check_status, + ) + self.delete_dangerous = to_raw_response_wrapper( + destroy_with_associated_resources.delete_dangerous, + ) + self.delete_selective = to_raw_response_wrapper( + destroy_with_associated_resources.delete_selective, + ) + self.retry = to_raw_response_wrapper( + destroy_with_associated_resources.retry, + ) + + +class AsyncDestroyWithAssociatedResourcesResourceWithRawResponse: + def __init__(self, destroy_with_associated_resources: AsyncDestroyWithAssociatedResourcesResource) -> None: + self._destroy_with_associated_resources = destroy_with_associated_resources + + self.list = async_to_raw_response_wrapper( + destroy_with_associated_resources.list, + ) + self.check_status = async_to_raw_response_wrapper( + destroy_with_associated_resources.check_status, + ) + self.delete_dangerous = async_to_raw_response_wrapper( + destroy_with_associated_resources.delete_dangerous, + ) + self.delete_selective = async_to_raw_response_wrapper( + destroy_with_associated_resources.delete_selective, + ) + self.retry = async_to_raw_response_wrapper( + destroy_with_associated_resources.retry, + ) + + +class DestroyWithAssociatedResourcesResourceWithStreamingResponse: + def __init__(self, destroy_with_associated_resources: DestroyWithAssociatedResourcesResource) -> None: + self._destroy_with_associated_resources = destroy_with_associated_resources + + self.list = to_streamed_response_wrapper( + destroy_with_associated_resources.list, + ) + self.check_status = to_streamed_response_wrapper( + destroy_with_associated_resources.check_status, + ) + self.delete_dangerous = to_streamed_response_wrapper( + destroy_with_associated_resources.delete_dangerous, + ) + self.delete_selective = to_streamed_response_wrapper( + destroy_with_associated_resources.delete_selective, + ) + self.retry = to_streamed_response_wrapper( + destroy_with_associated_resources.retry, + ) + + +class AsyncDestroyWithAssociatedResourcesResourceWithStreamingResponse: + def __init__(self, destroy_with_associated_resources: AsyncDestroyWithAssociatedResourcesResource) -> None: + self._destroy_with_associated_resources = destroy_with_associated_resources + + self.list = async_to_streamed_response_wrapper( + destroy_with_associated_resources.list, + ) + self.check_status = async_to_streamed_response_wrapper( + destroy_with_associated_resources.check_status, + ) + self.delete_dangerous = async_to_streamed_response_wrapper( + destroy_with_associated_resources.delete_dangerous, + ) + self.delete_selective = async_to_streamed_response_wrapper( + destroy_with_associated_resources.delete_selective, + ) + self.retry = async_to_streamed_response_wrapper( + destroy_with_associated_resources.retry, + ) diff --git a/src/gradient/resources/gpu_droplets/firewalls/__init__.py b/src/gradient/resources/gpu_droplets/firewalls/__init__.py new file mode 100644 index 00000000..e9cb832f --- /dev/null +++ b/src/gradient/resources/gpu_droplets/firewalls/__init__.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .tags import ( + TagsResource, + AsyncTagsResource, + TagsResourceWithRawResponse, + AsyncTagsResourceWithRawResponse, + TagsResourceWithStreamingResponse, + AsyncTagsResourceWithStreamingResponse, +) +from .rules import ( + RulesResource, + AsyncRulesResource, + RulesResourceWithRawResponse, + AsyncRulesResourceWithRawResponse, + RulesResourceWithStreamingResponse, + AsyncRulesResourceWithStreamingResponse, +) +from .droplets import ( + DropletsResource, + AsyncDropletsResource, + DropletsResourceWithRawResponse, + AsyncDropletsResourceWithRawResponse, + DropletsResourceWithStreamingResponse, + AsyncDropletsResourceWithStreamingResponse, +) +from .firewalls import ( + FirewallsResource, + AsyncFirewallsResource, + FirewallsResourceWithRawResponse, + AsyncFirewallsResourceWithRawResponse, + FirewallsResourceWithStreamingResponse, + AsyncFirewallsResourceWithStreamingResponse, +) + +__all__ = [ + "DropletsResource", + "AsyncDropletsResource", + "DropletsResourceWithRawResponse", + "AsyncDropletsResourceWithRawResponse", + "DropletsResourceWithStreamingResponse", + "AsyncDropletsResourceWithStreamingResponse", + "TagsResource", + "AsyncTagsResource", + "TagsResourceWithRawResponse", + "AsyncTagsResourceWithRawResponse", + "TagsResourceWithStreamingResponse", + "AsyncTagsResourceWithStreamingResponse", + "RulesResource", + "AsyncRulesResource", + "RulesResourceWithRawResponse", + "AsyncRulesResourceWithRawResponse", + "RulesResourceWithStreamingResponse", + "AsyncRulesResourceWithStreamingResponse", + "FirewallsResource", + "AsyncFirewallsResource", + "FirewallsResourceWithRawResponse", + "AsyncFirewallsResourceWithRawResponse", + "FirewallsResourceWithStreamingResponse", + "AsyncFirewallsResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/gpu_droplets/firewalls/droplets.py b/src/gradient/resources/gpu_droplets/firewalls/droplets.py new file mode 100644 index 00000000..90bcb47e --- /dev/null +++ b/src/gradient/resources/gpu_droplets/firewalls/droplets.py @@ -0,0 +1,296 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ...._types import Body, Query, Headers, NoneType, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.firewalls import droplet_add_params, droplet_remove_params + +__all__ = ["DropletsResource", "AsyncDropletsResource"] + + +class DropletsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DropletsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return DropletsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DropletsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return DropletsResourceWithStreamingResponse(self) + + def add( + self, + firewall_id: str, + *, + droplet_ids: Iterable[int], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To assign a Droplet to a firewall, send a POST request to + `/v2/firewalls/$FIREWALL_ID/droplets`. In the body of the request, there should + be a `droplet_ids` attribute containing a list of Droplet IDs. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + droplet_ids: An array containing the IDs of the Droplets to be assigned to the firewall. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/v2/firewalls/{firewall_id}/droplets" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/droplets", + body=maybe_transform({"droplet_ids": droplet_ids}, droplet_add_params.DropletAddParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def remove( + self, + firewall_id: str, + *, + droplet_ids: Iterable[int], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove a Droplet from a firewall, send a DELETE request to + `/v2/firewalls/$FIREWALL_ID/droplets`. In the body of the request, there should + be a `droplet_ids` attribute containing a list of Droplet IDs. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + droplet_ids: An array containing the IDs of the Droplets to be removed from the firewall. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/firewalls/{firewall_id}/droplets" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/droplets", + body=maybe_transform({"droplet_ids": droplet_ids}, droplet_remove_params.DropletRemoveParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncDropletsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDropletsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncDropletsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDropletsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncDropletsResourceWithStreamingResponse(self) + + async def add( + self, + firewall_id: str, + *, + droplet_ids: Iterable[int], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To assign a Droplet to a firewall, send a POST request to + `/v2/firewalls/$FIREWALL_ID/droplets`. In the body of the request, there should + be a `droplet_ids` attribute containing a list of Droplet IDs. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + droplet_ids: An array containing the IDs of the Droplets to be assigned to the firewall. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/v2/firewalls/{firewall_id}/droplets" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/droplets", + body=await async_maybe_transform({"droplet_ids": droplet_ids}, droplet_add_params.DropletAddParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def remove( + self, + firewall_id: str, + *, + droplet_ids: Iterable[int], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove a Droplet from a firewall, send a DELETE request to + `/v2/firewalls/$FIREWALL_ID/droplets`. In the body of the request, there should + be a `droplet_ids` attribute containing a list of Droplet IDs. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + droplet_ids: An array containing the IDs of the Droplets to be removed from the firewall. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/firewalls/{firewall_id}/droplets" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/droplets", + body=await async_maybe_transform({"droplet_ids": droplet_ids}, droplet_remove_params.DropletRemoveParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class DropletsResourceWithRawResponse: + def __init__(self, droplets: DropletsResource) -> None: + self._droplets = droplets + + self.add = to_raw_response_wrapper( + droplets.add, + ) + self.remove = to_raw_response_wrapper( + droplets.remove, + ) + + +class AsyncDropletsResourceWithRawResponse: + def __init__(self, droplets: AsyncDropletsResource) -> None: + self._droplets = droplets + + self.add = async_to_raw_response_wrapper( + droplets.add, + ) + self.remove = async_to_raw_response_wrapper( + droplets.remove, + ) + + +class DropletsResourceWithStreamingResponse: + def __init__(self, droplets: DropletsResource) -> None: + self._droplets = droplets + + self.add = to_streamed_response_wrapper( + droplets.add, + ) + self.remove = to_streamed_response_wrapper( + droplets.remove, + ) + + +class AsyncDropletsResourceWithStreamingResponse: + def __init__(self, droplets: AsyncDropletsResource) -> None: + self._droplets = droplets + + self.add = async_to_streamed_response_wrapper( + droplets.add, + ) + self.remove = async_to_streamed_response_wrapper( + droplets.remove, + ) diff --git a/src/gradient/resources/gpu_droplets/firewalls/firewalls.py b/src/gradient/resources/gpu_droplets/firewalls/firewalls.py new file mode 100644 index 00000000..a5fee406 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/firewalls/firewalls.py @@ -0,0 +1,647 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .tags import ( + TagsResource, + AsyncTagsResource, + TagsResourceWithRawResponse, + AsyncTagsResourceWithRawResponse, + TagsResourceWithStreamingResponse, + AsyncTagsResourceWithStreamingResponse, +) +from .rules import ( + RulesResource, + AsyncRulesResource, + RulesResourceWithRawResponse, + AsyncRulesResourceWithRawResponse, + RulesResourceWithStreamingResponse, + AsyncRulesResourceWithStreamingResponse, +) +from .droplets import ( + DropletsResource, + AsyncDropletsResource, + DropletsResourceWithRawResponse, + AsyncDropletsResourceWithRawResponse, + DropletsResourceWithStreamingResponse, + AsyncDropletsResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets import firewall_list_params, firewall_create_params, firewall_update_params +from ....types.gpu_droplets.firewall_param import FirewallParam +from ....types.gpu_droplets.firewall_list_response import FirewallListResponse +from ....types.gpu_droplets.firewall_create_response import FirewallCreateResponse +from ....types.gpu_droplets.firewall_update_response import FirewallUpdateResponse +from ....types.gpu_droplets.firewall_retrieve_response import FirewallRetrieveResponse + +__all__ = ["FirewallsResource", "AsyncFirewallsResource"] + + +class FirewallsResource(SyncAPIResource): + @cached_property + def droplets(self) -> DropletsResource: + return DropletsResource(self._client) + + @cached_property + def tags(self) -> TagsResource: + return TagsResource(self._client) + + @cached_property + def rules(self) -> RulesResource: + return RulesResource(self._client) + + @cached_property + def with_raw_response(self) -> FirewallsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return FirewallsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FirewallsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return FirewallsResourceWithStreamingResponse(self) + + def create( + self, + *, + body: firewall_create_params.Body | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FirewallCreateResponse: + """To create a new firewall, send a POST request to `/v2/firewalls`. + + The request + must contain at least one inbound or outbound access rule. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/firewalls" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/firewalls", + body=maybe_transform(body, firewall_create_params.FirewallCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FirewallCreateResponse, + ) + + def retrieve( + self, + firewall_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FirewallRetrieveResponse: + """ + To show information about an existing firewall, send a GET request to + `/v2/firewalls/$FIREWALL_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + return self._get( + f"/v2/firewalls/{firewall_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FirewallRetrieveResponse, + ) + + def update( + self, + firewall_id: str, + *, + firewall: FirewallParam, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FirewallUpdateResponse: + """ + To update the configuration of an existing firewall, send a PUT request to + `/v2/firewalls/$FIREWALL_ID`. The request should contain a full representation + of the firewall including existing attributes. **Note that any attributes that + are not provided will be reset to their default values.** + + You must have read access (e.g. `droplet:read`) to all resources attached to the + firewall to successfully update the firewall. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + return self._put( + f"/v2/firewalls/{firewall_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}", + body=maybe_transform(firewall, firewall_update_params.FirewallUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FirewallUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FirewallListResponse: + """ + To list all of the firewalls available on your account, send a GET request to + `/v2/firewalls`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/firewalls" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/firewalls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + firewall_list_params.FirewallListParams, + ), + ), + cast_to=FirewallListResponse, + ) + + def delete( + self, + firewall_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a firewall send a DELETE request to `/v2/firewalls/$FIREWALL_ID`. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/firewalls/{firewall_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncFirewallsResource(AsyncAPIResource): + @cached_property + def droplets(self) -> AsyncDropletsResource: + return AsyncDropletsResource(self._client) + + @cached_property + def tags(self) -> AsyncTagsResource: + return AsyncTagsResource(self._client) + + @cached_property + def rules(self) -> AsyncRulesResource: + return AsyncRulesResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncFirewallsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncFirewallsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFirewallsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncFirewallsResourceWithStreamingResponse(self) + + async def create( + self, + *, + body: firewall_create_params.Body | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FirewallCreateResponse: + """To create a new firewall, send a POST request to `/v2/firewalls`. + + The request + must contain at least one inbound or outbound access rule. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/firewalls" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/firewalls", + body=await async_maybe_transform(body, firewall_create_params.FirewallCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FirewallCreateResponse, + ) + + async def retrieve( + self, + firewall_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FirewallRetrieveResponse: + """ + To show information about an existing firewall, send a GET request to + `/v2/firewalls/$FIREWALL_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + return await self._get( + f"/v2/firewalls/{firewall_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FirewallRetrieveResponse, + ) + + async def update( + self, + firewall_id: str, + *, + firewall: FirewallParam, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FirewallUpdateResponse: + """ + To update the configuration of an existing firewall, send a PUT request to + `/v2/firewalls/$FIREWALL_ID`. The request should contain a full representation + of the firewall including existing attributes. **Note that any attributes that + are not provided will be reset to their default values.** + + You must have read access (e.g. `droplet:read`) to all resources attached to the + firewall to successfully update the firewall. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + return await self._put( + f"/v2/firewalls/{firewall_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}", + body=await async_maybe_transform(firewall, firewall_update_params.FirewallUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FirewallUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FirewallListResponse: + """ + To list all of the firewalls available on your account, send a GET request to + `/v2/firewalls`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/firewalls" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/firewalls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + firewall_list_params.FirewallListParams, + ), + ), + cast_to=FirewallListResponse, + ) + + async def delete( + self, + firewall_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a firewall send a DELETE request to `/v2/firewalls/$FIREWALL_ID`. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/firewalls/{firewall_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class FirewallsResourceWithRawResponse: + def __init__(self, firewalls: FirewallsResource) -> None: + self._firewalls = firewalls + + self.create = to_raw_response_wrapper( + firewalls.create, + ) + self.retrieve = to_raw_response_wrapper( + firewalls.retrieve, + ) + self.update = to_raw_response_wrapper( + firewalls.update, + ) + self.list = to_raw_response_wrapper( + firewalls.list, + ) + self.delete = to_raw_response_wrapper( + firewalls.delete, + ) + + @cached_property + def droplets(self) -> DropletsResourceWithRawResponse: + return DropletsResourceWithRawResponse(self._firewalls.droplets) + + @cached_property + def tags(self) -> TagsResourceWithRawResponse: + return TagsResourceWithRawResponse(self._firewalls.tags) + + @cached_property + def rules(self) -> RulesResourceWithRawResponse: + return RulesResourceWithRawResponse(self._firewalls.rules) + + +class AsyncFirewallsResourceWithRawResponse: + def __init__(self, firewalls: AsyncFirewallsResource) -> None: + self._firewalls = firewalls + + self.create = async_to_raw_response_wrapper( + firewalls.create, + ) + self.retrieve = async_to_raw_response_wrapper( + firewalls.retrieve, + ) + self.update = async_to_raw_response_wrapper( + firewalls.update, + ) + self.list = async_to_raw_response_wrapper( + firewalls.list, + ) + self.delete = async_to_raw_response_wrapper( + firewalls.delete, + ) + + @cached_property + def droplets(self) -> AsyncDropletsResourceWithRawResponse: + return AsyncDropletsResourceWithRawResponse(self._firewalls.droplets) + + @cached_property + def tags(self) -> AsyncTagsResourceWithRawResponse: + return AsyncTagsResourceWithRawResponse(self._firewalls.tags) + + @cached_property + def rules(self) -> AsyncRulesResourceWithRawResponse: + return AsyncRulesResourceWithRawResponse(self._firewalls.rules) + + +class FirewallsResourceWithStreamingResponse: + def __init__(self, firewalls: FirewallsResource) -> None: + self._firewalls = firewalls + + self.create = to_streamed_response_wrapper( + firewalls.create, + ) + self.retrieve = to_streamed_response_wrapper( + firewalls.retrieve, + ) + self.update = to_streamed_response_wrapper( + firewalls.update, + ) + self.list = to_streamed_response_wrapper( + firewalls.list, + ) + self.delete = to_streamed_response_wrapper( + firewalls.delete, + ) + + @cached_property + def droplets(self) -> DropletsResourceWithStreamingResponse: + return DropletsResourceWithStreamingResponse(self._firewalls.droplets) + + @cached_property + def tags(self) -> TagsResourceWithStreamingResponse: + return TagsResourceWithStreamingResponse(self._firewalls.tags) + + @cached_property + def rules(self) -> RulesResourceWithStreamingResponse: + return RulesResourceWithStreamingResponse(self._firewalls.rules) + + +class AsyncFirewallsResourceWithStreamingResponse: + def __init__(self, firewalls: AsyncFirewallsResource) -> None: + self._firewalls = firewalls + + self.create = async_to_streamed_response_wrapper( + firewalls.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + firewalls.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + firewalls.update, + ) + self.list = async_to_streamed_response_wrapper( + firewalls.list, + ) + self.delete = async_to_streamed_response_wrapper( + firewalls.delete, + ) + + @cached_property + def droplets(self) -> AsyncDropletsResourceWithStreamingResponse: + return AsyncDropletsResourceWithStreamingResponse(self._firewalls.droplets) + + @cached_property + def tags(self) -> AsyncTagsResourceWithStreamingResponse: + return AsyncTagsResourceWithStreamingResponse(self._firewalls.tags) + + @cached_property + def rules(self) -> AsyncRulesResourceWithStreamingResponse: + return AsyncRulesResourceWithStreamingResponse(self._firewalls.rules) diff --git a/src/gradient/resources/gpu_droplets/firewalls/rules.py b/src/gradient/resources/gpu_droplets/firewalls/rules.py new file mode 100644 index 00000000..f669fc6d --- /dev/null +++ b/src/gradient/resources/gpu_droplets/firewalls/rules.py @@ -0,0 +1,320 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional + +import httpx + +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.firewalls import rule_add_params, rule_remove_params + +__all__ = ["RulesResource", "AsyncRulesResource"] + + +class RulesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RulesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return RulesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RulesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return RulesResourceWithStreamingResponse(self) + + def add( + self, + firewall_id: str, + *, + inbound_rules: Optional[Iterable[rule_add_params.InboundRule]] | Omit = omit, + outbound_rules: Optional[Iterable[rule_add_params.OutboundRule]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To add additional access rules to a firewall, send a POST request to + `/v2/firewalls/$FIREWALL_ID/rules`. The body of the request may include an + inbound_rules and/or outbound_rules attribute containing an array of rules to be + added. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/v2/firewalls/{firewall_id}/rules" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/rules", + body=maybe_transform( + { + "inbound_rules": inbound_rules, + "outbound_rules": outbound_rules, + }, + rule_add_params.RuleAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def remove( + self, + firewall_id: str, + *, + inbound_rules: Optional[Iterable[rule_remove_params.InboundRule]] | Omit = omit, + outbound_rules: Optional[Iterable[rule_remove_params.OutboundRule]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove access rules from a firewall, send a DELETE request to + `/v2/firewalls/$FIREWALL_ID/rules`. The body of the request may include an + `inbound_rules` and/or `outbound_rules` attribute containing an array of rules + to be removed. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/firewalls/{firewall_id}/rules" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/rules", + body=maybe_transform( + { + "inbound_rules": inbound_rules, + "outbound_rules": outbound_rules, + }, + rule_remove_params.RuleRemoveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncRulesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRulesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncRulesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRulesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncRulesResourceWithStreamingResponse(self) + + async def add( + self, + firewall_id: str, + *, + inbound_rules: Optional[Iterable[rule_add_params.InboundRule]] | Omit = omit, + outbound_rules: Optional[Iterable[rule_add_params.OutboundRule]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To add additional access rules to a firewall, send a POST request to + `/v2/firewalls/$FIREWALL_ID/rules`. The body of the request may include an + inbound_rules and/or outbound_rules attribute containing an array of rules to be + added. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/v2/firewalls/{firewall_id}/rules" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/rules", + body=await async_maybe_transform( + { + "inbound_rules": inbound_rules, + "outbound_rules": outbound_rules, + }, + rule_add_params.RuleAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def remove( + self, + firewall_id: str, + *, + inbound_rules: Optional[Iterable[rule_remove_params.InboundRule]] | Omit = omit, + outbound_rules: Optional[Iterable[rule_remove_params.OutboundRule]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove access rules from a firewall, send a DELETE request to + `/v2/firewalls/$FIREWALL_ID/rules`. The body of the request may include an + `inbound_rules` and/or `outbound_rules` attribute containing an array of rules + to be removed. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/firewalls/{firewall_id}/rules" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/rules", + body=await async_maybe_transform( + { + "inbound_rules": inbound_rules, + "outbound_rules": outbound_rules, + }, + rule_remove_params.RuleRemoveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class RulesResourceWithRawResponse: + def __init__(self, rules: RulesResource) -> None: + self._rules = rules + + self.add = to_raw_response_wrapper( + rules.add, + ) + self.remove = to_raw_response_wrapper( + rules.remove, + ) + + +class AsyncRulesResourceWithRawResponse: + def __init__(self, rules: AsyncRulesResource) -> None: + self._rules = rules + + self.add = async_to_raw_response_wrapper( + rules.add, + ) + self.remove = async_to_raw_response_wrapper( + rules.remove, + ) + + +class RulesResourceWithStreamingResponse: + def __init__(self, rules: RulesResource) -> None: + self._rules = rules + + self.add = to_streamed_response_wrapper( + rules.add, + ) + self.remove = to_streamed_response_wrapper( + rules.remove, + ) + + +class AsyncRulesResourceWithStreamingResponse: + def __init__(self, rules: AsyncRulesResource) -> None: + self._rules = rules + + self.add = async_to_streamed_response_wrapper( + rules.add, + ) + self.remove = async_to_streamed_response_wrapper( + rules.remove, + ) diff --git a/src/gradient/resources/gpu_droplets/firewalls/tags.py b/src/gradient/resources/gpu_droplets/firewalls/tags.py new file mode 100644 index 00000000..82d613fb --- /dev/null +++ b/src/gradient/resources/gpu_droplets/firewalls/tags.py @@ -0,0 +1,308 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ...._types import Body, Query, Headers, NoneType, NotGiven, SequenceNotStr, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.firewalls import tag_add_params, tag_remove_params + +__all__ = ["TagsResource", "AsyncTagsResource"] + + +class TagsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TagsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return TagsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TagsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return TagsResourceWithStreamingResponse(self) + + def add( + self, + firewall_id: str, + *, + tags: Optional[SequenceNotStr[str]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To assign a tag representing a group of Droplets to a firewall, send a POST + request to `/v2/firewalls/$FIREWALL_ID/tags`. In the body of the request, there + should be a `tags` attribute containing a list of tag names. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + tags: A flat array of tag names as strings to be applied to the resource. Tag names + must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/v2/firewalls/{firewall_id}/tags" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/tags", + body=maybe_transform({"tags": tags}, tag_add_params.TagAddParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def remove( + self, + firewall_id: str, + *, + tags: Optional[SequenceNotStr[str]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove a tag representing a group of Droplets from a firewall, send a DELETE + request to `/v2/firewalls/$FIREWALL_ID/tags`. In the body of the request, there + should be a `tags` attribute containing a list of tag names. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + tags: A flat array of tag names as strings to be applied to the resource. Tag names + must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/firewalls/{firewall_id}/tags" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/tags", + body=maybe_transform({"tags": tags}, tag_remove_params.TagRemoveParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncTagsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTagsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncTagsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTagsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncTagsResourceWithStreamingResponse(self) + + async def add( + self, + firewall_id: str, + *, + tags: Optional[SequenceNotStr[str]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To assign a tag representing a group of Droplets to a firewall, send a POST + request to `/v2/firewalls/$FIREWALL_ID/tags`. In the body of the request, there + should be a `tags` attribute containing a list of tag names. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + tags: A flat array of tag names as strings to be applied to the resource. Tag names + must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/v2/firewalls/{firewall_id}/tags" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/tags", + body=await async_maybe_transform({"tags": tags}, tag_add_params.TagAddParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def remove( + self, + firewall_id: str, + *, + tags: Optional[SequenceNotStr[str]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove a tag representing a group of Droplets from a firewall, send a DELETE + request to `/v2/firewalls/$FIREWALL_ID/tags`. In the body of the request, there + should be a `tags` attribute containing a list of tag names. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + tags: A flat array of tag names as strings to be applied to the resource. Tag names + must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not firewall_id: + raise ValueError(f"Expected a non-empty value for `firewall_id` but received {firewall_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/firewalls/{firewall_id}/tags" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/firewalls/{firewall_id}/tags", + body=await async_maybe_transform({"tags": tags}, tag_remove_params.TagRemoveParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class TagsResourceWithRawResponse: + def __init__(self, tags: TagsResource) -> None: + self._tags = tags + + self.add = to_raw_response_wrapper( + tags.add, + ) + self.remove = to_raw_response_wrapper( + tags.remove, + ) + + +class AsyncTagsResourceWithRawResponse: + def __init__(self, tags: AsyncTagsResource) -> None: + self._tags = tags + + self.add = async_to_raw_response_wrapper( + tags.add, + ) + self.remove = async_to_raw_response_wrapper( + tags.remove, + ) + + +class TagsResourceWithStreamingResponse: + def __init__(self, tags: TagsResource) -> None: + self._tags = tags + + self.add = to_streamed_response_wrapper( + tags.add, + ) + self.remove = to_streamed_response_wrapper( + tags.remove, + ) + + +class AsyncTagsResourceWithStreamingResponse: + def __init__(self, tags: AsyncTagsResource) -> None: + self._tags = tags + + self.add = async_to_streamed_response_wrapper( + tags.add, + ) + self.remove = async_to_streamed_response_wrapper( + tags.remove, + ) diff --git a/src/gradient/resources/gpu_droplets/floating_ips/__init__.py b/src/gradient/resources/gpu_droplets/floating_ips/__init__.py new file mode 100644 index 00000000..bf6871b1 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/floating_ips/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .actions import ( + ActionsResource, + AsyncActionsResource, + ActionsResourceWithRawResponse, + AsyncActionsResourceWithRawResponse, + ActionsResourceWithStreamingResponse, + AsyncActionsResourceWithStreamingResponse, +) +from .floating_ips import ( + FloatingIPsResource, + AsyncFloatingIPsResource, + FloatingIPsResourceWithRawResponse, + AsyncFloatingIPsResourceWithRawResponse, + FloatingIPsResourceWithStreamingResponse, + AsyncFloatingIPsResourceWithStreamingResponse, +) + +__all__ = [ + "ActionsResource", + "AsyncActionsResource", + "ActionsResourceWithRawResponse", + "AsyncActionsResourceWithRawResponse", + "ActionsResourceWithStreamingResponse", + "AsyncActionsResourceWithStreamingResponse", + "FloatingIPsResource", + "AsyncFloatingIPsResource", + "FloatingIPsResourceWithRawResponse", + "AsyncFloatingIPsResourceWithRawResponse", + "FloatingIPsResourceWithStreamingResponse", + "AsyncFloatingIPsResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/gpu_droplets/floating_ips/actions.py b/src/gradient/resources/gpu_droplets/floating_ips/actions.py new file mode 100644 index 00000000..f73d5707 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/floating_ips/actions.py @@ -0,0 +1,489 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, overload + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.floating_ips import action_create_params +from ....types.gpu_droplets.floating_ips.action_list_response import ActionListResponse +from ....types.gpu_droplets.floating_ips.action_create_response import ActionCreateResponse +from ....types.gpu_droplets.floating_ips.action_retrieve_response import ActionRetrieveResponse + +__all__ = ["ActionsResource", "AsyncActionsResource"] + + +class ActionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ActionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ActionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ActionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ActionsResourceWithStreamingResponse(self) + + @overload + def create( + self, + floating_ip: str, + *, + type: Literal["assign", "unassign"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionCreateResponse: + """ + To initiate an action on a floating IP send a POST request to + `/v2/floating_ips/$FLOATING_IP/actions`. In the JSON body to the request, set + the `type` attribute to on of the supported action types: + + | Action | Details | + | ---------- | ------------------------------------- | + | `assign` | Assigns a floating IP to a Droplet | + | `unassign` | Unassign a floating IP from a Droplet | + + Args: + type: The type of action to initiate for the floating IP. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + floating_ip: str, + *, + droplet_id: int, + type: Literal["assign", "unassign"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionCreateResponse: + """ + To initiate an action on a floating IP send a POST request to + `/v2/floating_ips/$FLOATING_IP/actions`. In the JSON body to the request, set + the `type` attribute to on of the supported action types: + + | Action | Details | + | ---------- | ------------------------------------- | + | `assign` | Assigns a floating IP to a Droplet | + | `unassign` | Unassign a floating IP from a Droplet | + + Args: + droplet_id: The ID of the Droplet that the floating IP will be assigned to. + + type: The type of action to initiate for the floating IP. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["type"], ["droplet_id", "type"]) + def create( + self, + floating_ip: str, + *, + type: Literal["assign", "unassign"], + droplet_id: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionCreateResponse: + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + return self._post( + f"/v2/floating_ips/{floating_ip}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}/actions", + body=maybe_transform( + { + "type": type, + "droplet_id": droplet_id, + }, + action_create_params.ActionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionCreateResponse, + ) + + def retrieve( + self, + action_id: int, + *, + floating_ip: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionRetrieveResponse: + """ + To retrieve the status of a floating IP action, send a GET request to + `/v2/floating_ips/$FLOATING_IP/actions/$ACTION_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + return self._get( + f"/v2/floating_ips/{floating_ip}/actions/{action_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}/actions/{action_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionRetrieveResponse, + ) + + def list( + self, + floating_ip: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionListResponse: + """ + To retrieve all actions that have been executed on a floating IP, send a GET + request to `/v2/floating_ips/$FLOATING_IP/actions`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + return self._get( + f"/v2/floating_ips/{floating_ip}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}/actions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionListResponse, + ) + + +class AsyncActionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncActionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncActionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncActionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncActionsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + floating_ip: str, + *, + type: Literal["assign", "unassign"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionCreateResponse: + """ + To initiate an action on a floating IP send a POST request to + `/v2/floating_ips/$FLOATING_IP/actions`. In the JSON body to the request, set + the `type` attribute to on of the supported action types: + + | Action | Details | + | ---------- | ------------------------------------- | + | `assign` | Assigns a floating IP to a Droplet | + | `unassign` | Unassign a floating IP from a Droplet | + + Args: + type: The type of action to initiate for the floating IP. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + floating_ip: str, + *, + droplet_id: int, + type: Literal["assign", "unassign"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionCreateResponse: + """ + To initiate an action on a floating IP send a POST request to + `/v2/floating_ips/$FLOATING_IP/actions`. In the JSON body to the request, set + the `type` attribute to on of the supported action types: + + | Action | Details | + | ---------- | ------------------------------------- | + | `assign` | Assigns a floating IP to a Droplet | + | `unassign` | Unassign a floating IP from a Droplet | + + Args: + droplet_id: The ID of the Droplet that the floating IP will be assigned to. + + type: The type of action to initiate for the floating IP. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["type"], ["droplet_id", "type"]) + async def create( + self, + floating_ip: str, + *, + type: Literal["assign", "unassign"], + droplet_id: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionCreateResponse: + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + return await self._post( + f"/v2/floating_ips/{floating_ip}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}/actions", + body=await async_maybe_transform( + { + "type": type, + "droplet_id": droplet_id, + }, + action_create_params.ActionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionCreateResponse, + ) + + async def retrieve( + self, + action_id: int, + *, + floating_ip: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionRetrieveResponse: + """ + To retrieve the status of a floating IP action, send a GET request to + `/v2/floating_ips/$FLOATING_IP/actions/$ACTION_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + return await self._get( + f"/v2/floating_ips/{floating_ip}/actions/{action_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}/actions/{action_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionRetrieveResponse, + ) + + async def list( + self, + floating_ip: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionListResponse: + """ + To retrieve all actions that have been executed on a floating IP, send a GET + request to `/v2/floating_ips/$FLOATING_IP/actions`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + return await self._get( + f"/v2/floating_ips/{floating_ip}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}/actions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionListResponse, + ) + + +class ActionsResourceWithRawResponse: + def __init__(self, actions: ActionsResource) -> None: + self._actions = actions + + self.create = to_raw_response_wrapper( + actions.create, + ) + self.retrieve = to_raw_response_wrapper( + actions.retrieve, + ) + self.list = to_raw_response_wrapper( + actions.list, + ) + + +class AsyncActionsResourceWithRawResponse: + def __init__(self, actions: AsyncActionsResource) -> None: + self._actions = actions + + self.create = async_to_raw_response_wrapper( + actions.create, + ) + self.retrieve = async_to_raw_response_wrapper( + actions.retrieve, + ) + self.list = async_to_raw_response_wrapper( + actions.list, + ) + + +class ActionsResourceWithStreamingResponse: + def __init__(self, actions: ActionsResource) -> None: + self._actions = actions + + self.create = to_streamed_response_wrapper( + actions.create, + ) + self.retrieve = to_streamed_response_wrapper( + actions.retrieve, + ) + self.list = to_streamed_response_wrapper( + actions.list, + ) + + +class AsyncActionsResourceWithStreamingResponse: + def __init__(self, actions: AsyncActionsResource) -> None: + self._actions = actions + + self.create = async_to_streamed_response_wrapper( + actions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + actions.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + actions.list, + ) diff --git a/src/gradient/resources/gpu_droplets/floating_ips/floating_ips.py b/src/gradient/resources/gpu_droplets/floating_ips/floating_ips.py new file mode 100644 index 00000000..f55bfd41 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/floating_ips/floating_ips.py @@ -0,0 +1,623 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import overload + +import httpx + +from .actions import ( + ActionsResource, + AsyncActionsResource, + ActionsResourceWithRawResponse, + AsyncActionsResourceWithRawResponse, + ActionsResourceWithStreamingResponse, + AsyncActionsResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets import floating_ip_list_params, floating_ip_create_params +from ....types.gpu_droplets.floating_ip_list_response import FloatingIPListResponse +from ....types.gpu_droplets.floating_ip_create_response import FloatingIPCreateResponse +from ....types.gpu_droplets.floating_ip_retrieve_response import FloatingIPRetrieveResponse + +__all__ = ["FloatingIPsResource", "AsyncFloatingIPsResource"] + + +class FloatingIPsResource(SyncAPIResource): + @cached_property + def actions(self) -> ActionsResource: + return ActionsResource(self._client) + + @cached_property + def with_raw_response(self) -> FloatingIPsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return FloatingIPsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FloatingIPsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return FloatingIPsResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + droplet_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPCreateResponse: + """ + On creation, a floating IP must be either assigned to a Droplet or reserved to a + region. + + - To create a new floating IP assigned to a Droplet, send a POST request to + `/v2/floating_ips` with the `droplet_id` attribute. + + - To create a new floating IP reserved to a region, send a POST request to + `/v2/floating_ips` with the `region` attribute. + + Args: + droplet_id: The ID of the Droplet that the floating IP will be assigned to. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + region: str, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPCreateResponse: + """ + On creation, a floating IP must be either assigned to a Droplet or reserved to a + region. + + - To create a new floating IP assigned to a Droplet, send a POST request to + `/v2/floating_ips` with the `droplet_id` attribute. + + - To create a new floating IP reserved to a region, send a POST request to + `/v2/floating_ips` with the `region` attribute. + + Args: + region: The slug identifier for the region the floating IP will be reserved to. + + project_id: The UUID of the project to which the floating IP will be assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["droplet_id"], ["region"]) + def create( + self, + *, + droplet_id: int | Omit = omit, + region: str | Omit = omit, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPCreateResponse: + return self._post( + "/v2/floating_ips" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/floating_ips", + body=maybe_transform( + { + "droplet_id": droplet_id, + "region": region, + "project_id": project_id, + }, + floating_ip_create_params.FloatingIPCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FloatingIPCreateResponse, + ) + + def retrieve( + self, + floating_ip: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPRetrieveResponse: + """ + To show information about a floating IP, send a GET request to + `/v2/floating_ips/$FLOATING_IP_ADDR`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + return self._get( + f"/v2/floating_ips/{floating_ip}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FloatingIPRetrieveResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPListResponse: + """ + To list all of the floating IPs available on your account, send a GET request to + `/v2/floating_ips`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/floating_ips" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/floating_ips", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + floating_ip_list_params.FloatingIPListParams, + ), + ), + cast_to=FloatingIPListResponse, + ) + + def delete( + self, + floating_ip: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a floating IP and remove it from your account, send a DELETE request + to `/v2/floating_ips/$FLOATING_IP_ADDR`. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/floating_ips/{floating_ip}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncFloatingIPsResource(AsyncAPIResource): + @cached_property + def actions(self) -> AsyncActionsResource: + return AsyncActionsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncFloatingIPsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncFloatingIPsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFloatingIPsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncFloatingIPsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + droplet_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPCreateResponse: + """ + On creation, a floating IP must be either assigned to a Droplet or reserved to a + region. + + - To create a new floating IP assigned to a Droplet, send a POST request to + `/v2/floating_ips` with the `droplet_id` attribute. + + - To create a new floating IP reserved to a region, send a POST request to + `/v2/floating_ips` with the `region` attribute. + + Args: + droplet_id: The ID of the Droplet that the floating IP will be assigned to. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + region: str, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPCreateResponse: + """ + On creation, a floating IP must be either assigned to a Droplet or reserved to a + region. + + - To create a new floating IP assigned to a Droplet, send a POST request to + `/v2/floating_ips` with the `droplet_id` attribute. + + - To create a new floating IP reserved to a region, send a POST request to + `/v2/floating_ips` with the `region` attribute. + + Args: + region: The slug identifier for the region the floating IP will be reserved to. + + project_id: The UUID of the project to which the floating IP will be assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["droplet_id"], ["region"]) + async def create( + self, + *, + droplet_id: int | Omit = omit, + region: str | Omit = omit, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPCreateResponse: + return await self._post( + "/v2/floating_ips" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/floating_ips", + body=await async_maybe_transform( + { + "droplet_id": droplet_id, + "region": region, + "project_id": project_id, + }, + floating_ip_create_params.FloatingIPCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FloatingIPCreateResponse, + ) + + async def retrieve( + self, + floating_ip: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPRetrieveResponse: + """ + To show information about a floating IP, send a GET request to + `/v2/floating_ips/$FLOATING_IP_ADDR`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + return await self._get( + f"/v2/floating_ips/{floating_ip}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FloatingIPRetrieveResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FloatingIPListResponse: + """ + To list all of the floating IPs available on your account, send a GET request to + `/v2/floating_ips`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/floating_ips" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/floating_ips", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + floating_ip_list_params.FloatingIPListParams, + ), + ), + cast_to=FloatingIPListResponse, + ) + + async def delete( + self, + floating_ip: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a floating IP and remove it from your account, send a DELETE request + to `/v2/floating_ips/$FLOATING_IP_ADDR`. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not floating_ip: + raise ValueError(f"Expected a non-empty value for `floating_ip` but received {floating_ip!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/floating_ips/{floating_ip}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/floating_ips/{floating_ip}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class FloatingIPsResourceWithRawResponse: + def __init__(self, floating_ips: FloatingIPsResource) -> None: + self._floating_ips = floating_ips + + self.create = to_raw_response_wrapper( + floating_ips.create, + ) + self.retrieve = to_raw_response_wrapper( + floating_ips.retrieve, + ) + self.list = to_raw_response_wrapper( + floating_ips.list, + ) + self.delete = to_raw_response_wrapper( + floating_ips.delete, + ) + + @cached_property + def actions(self) -> ActionsResourceWithRawResponse: + return ActionsResourceWithRawResponse(self._floating_ips.actions) + + +class AsyncFloatingIPsResourceWithRawResponse: + def __init__(self, floating_ips: AsyncFloatingIPsResource) -> None: + self._floating_ips = floating_ips + + self.create = async_to_raw_response_wrapper( + floating_ips.create, + ) + self.retrieve = async_to_raw_response_wrapper( + floating_ips.retrieve, + ) + self.list = async_to_raw_response_wrapper( + floating_ips.list, + ) + self.delete = async_to_raw_response_wrapper( + floating_ips.delete, + ) + + @cached_property + def actions(self) -> AsyncActionsResourceWithRawResponse: + return AsyncActionsResourceWithRawResponse(self._floating_ips.actions) + + +class FloatingIPsResourceWithStreamingResponse: + def __init__(self, floating_ips: FloatingIPsResource) -> None: + self._floating_ips = floating_ips + + self.create = to_streamed_response_wrapper( + floating_ips.create, + ) + self.retrieve = to_streamed_response_wrapper( + floating_ips.retrieve, + ) + self.list = to_streamed_response_wrapper( + floating_ips.list, + ) + self.delete = to_streamed_response_wrapper( + floating_ips.delete, + ) + + @cached_property + def actions(self) -> ActionsResourceWithStreamingResponse: + return ActionsResourceWithStreamingResponse(self._floating_ips.actions) + + +class AsyncFloatingIPsResourceWithStreamingResponse: + def __init__(self, floating_ips: AsyncFloatingIPsResource) -> None: + self._floating_ips = floating_ips + + self.create = async_to_streamed_response_wrapper( + floating_ips.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + floating_ips.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + floating_ips.list, + ) + self.delete = async_to_streamed_response_wrapper( + floating_ips.delete, + ) + + @cached_property + def actions(self) -> AsyncActionsResourceWithStreamingResponse: + return AsyncActionsResourceWithStreamingResponse(self._floating_ips.actions) diff --git a/src/gradient/resources/gpu_droplets/gpu_droplets.py b/src/gradient/resources/gpu_droplets/gpu_droplets.py new file mode 100644 index 00000000..c9f84747 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/gpu_droplets.py @@ -0,0 +1,2008 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, Union, Optional, cast +from typing_extensions import Literal, overload + +import httpx + +from .sizes import ( + SizesResource, + AsyncSizesResource, + SizesResourceWithRawResponse, + AsyncSizesResourceWithRawResponse, + SizesResourceWithStreamingResponse, + AsyncSizesResourceWithStreamingResponse, +) +from ...types import ( + gpu_droplet_list_params, + gpu_droplet_create_params, + gpu_droplet_list_kernels_params, + gpu_droplet_delete_by_tag_params, + gpu_droplet_list_firewalls_params, + gpu_droplet_list_snapshots_params, +) +from .actions import ( + ActionsResource, + AsyncActionsResource, + ActionsResourceWithRawResponse, + AsyncActionsResourceWithRawResponse, + ActionsResourceWithStreamingResponse, + AsyncActionsResourceWithStreamingResponse, +) +from .backups import ( + BackupsResource, + AsyncBackupsResource, + BackupsResourceWithRawResponse, + AsyncBackupsResourceWithRawResponse, + BackupsResourceWithStreamingResponse, + AsyncBackupsResourceWithStreamingResponse, +) +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from .autoscale import ( + AutoscaleResource, + AsyncAutoscaleResource, + AutoscaleResourceWithRawResponse, + AsyncAutoscaleResourceWithRawResponse, + AutoscaleResourceWithStreamingResponse, + AsyncAutoscaleResourceWithStreamingResponse, +) +from .snapshots import ( + SnapshotsResource, + AsyncSnapshotsResource, + SnapshotsResourceWithRawResponse, + AsyncSnapshotsResourceWithRawResponse, + SnapshotsResourceWithStreamingResponse, + AsyncSnapshotsResourceWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .images.images import ( + ImagesResource, + AsyncImagesResource, + ImagesResourceWithRawResponse, + AsyncImagesResourceWithRawResponse, + ImagesResourceWithStreamingResponse, + AsyncImagesResourceWithStreamingResponse, +) +from ..._base_client import make_request_options +from .account.account import ( + AccountResource, + AsyncAccountResource, + AccountResourceWithRawResponse, + AsyncAccountResourceWithRawResponse, + AccountResourceWithStreamingResponse, + AsyncAccountResourceWithStreamingResponse, +) +from .volumes.volumes import ( + VolumesResource, + AsyncVolumesResource, + VolumesResourceWithRawResponse, + AsyncVolumesResourceWithRawResponse, + VolumesResourceWithStreamingResponse, + AsyncVolumesResourceWithStreamingResponse, +) +from .firewalls.firewalls import ( + FirewallsResource, + AsyncFirewallsResource, + FirewallsResourceWithRawResponse, + AsyncFirewallsResourceWithRawResponse, + FirewallsResourceWithStreamingResponse, + AsyncFirewallsResourceWithStreamingResponse, +) +from .floating_ips.floating_ips import ( + FloatingIPsResource, + AsyncFloatingIPsResource, + FloatingIPsResourceWithRawResponse, + AsyncFloatingIPsResourceWithRawResponse, + FloatingIPsResourceWithStreamingResponse, + AsyncFloatingIPsResourceWithStreamingResponse, +) +from .load_balancers.load_balancers import ( + LoadBalancersResource, + AsyncLoadBalancersResource, + LoadBalancersResourceWithRawResponse, + AsyncLoadBalancersResourceWithRawResponse, + LoadBalancersResourceWithStreamingResponse, + AsyncLoadBalancersResourceWithStreamingResponse, +) +from ...types.gpu_droplet_list_response import GPUDropletListResponse +from .destroy_with_associated_resources import ( + DestroyWithAssociatedResourcesResource, + AsyncDestroyWithAssociatedResourcesResource, + DestroyWithAssociatedResourcesResourceWithRawResponse, + AsyncDestroyWithAssociatedResourcesResourceWithRawResponse, + DestroyWithAssociatedResourcesResourceWithStreamingResponse, + AsyncDestroyWithAssociatedResourcesResourceWithStreamingResponse, +) +from ...types.droplet_backup_policy_param import DropletBackupPolicyParam +from ...types.gpu_droplet_create_response import GPUDropletCreateResponse +from ...types.gpu_droplet_retrieve_response import GPUDropletRetrieveResponse +from ...types.gpu_droplet_list_kernels_response import GPUDropletListKernelsResponse +from ...types.gpu_droplet_list_firewalls_response import GPUDropletListFirewallsResponse +from ...types.gpu_droplet_list_neighbors_response import GPUDropletListNeighborsResponse +from ...types.gpu_droplet_list_snapshots_response import GPUDropletListSnapshotsResponse + +__all__ = ["GPUDropletsResource", "AsyncGPUDropletsResource"] + + +class GPUDropletsResource(SyncAPIResource): + @cached_property + def backups(self) -> BackupsResource: + return BackupsResource(self._client) + + @cached_property + def actions(self) -> ActionsResource: + return ActionsResource(self._client) + + @cached_property + def destroy_with_associated_resources(self) -> DestroyWithAssociatedResourcesResource: + return DestroyWithAssociatedResourcesResource(self._client) + + @cached_property + def autoscale(self) -> AutoscaleResource: + return AutoscaleResource(self._client) + + @cached_property + def firewalls(self) -> FirewallsResource: + return FirewallsResource(self._client) + + @cached_property + def floating_ips(self) -> FloatingIPsResource: + return FloatingIPsResource(self._client) + + @cached_property + def images(self) -> ImagesResource: + return ImagesResource(self._client) + + @cached_property + def load_balancers(self) -> LoadBalancersResource: + return LoadBalancersResource(self._client) + + @cached_property + def sizes(self) -> SizesResource: + return SizesResource(self._client) + + @cached_property + def snapshots(self) -> SnapshotsResource: + return SnapshotsResource(self._client) + + @cached_property + def volumes(self) -> VolumesResource: + return VolumesResource(self._client) + + @cached_property + def account(self) -> AccountResource: + return AccountResource(self._client) + + @cached_property + def with_raw_response(self) -> GPUDropletsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return GPUDropletsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GPUDropletsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return GPUDropletsResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + image: Union[str, int], + name: str, + size: str, + backup_policy: DropletBackupPolicyParam | Omit = omit, + backups: bool | Omit = omit, + ipv6: bool | Omit = omit, + monitoring: bool | Omit = omit, + private_networking: bool | Omit = omit, + region: str | Omit = omit, + ssh_keys: SequenceNotStr[Union[str, int]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + user_data: str | Omit = omit, + volumes: SequenceNotStr[str] | Omit = omit, + vpc_uuid: str | Omit = omit, + with_droplet_agent: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletCreateResponse: + """ + To create a new Droplet, send a POST request to `/v2/droplets` setting the + required attributes. + + A Droplet will be created using the provided information. The response body will + contain a JSON object with a key called `droplet`. The value will be an object + containing the standard attributes for your new Droplet. The response code, 202 + Accepted, does not indicate the success or failure of the operation, just that + the request has been accepted for processing. The `actions` returned as part of + the response's `links` object can be used to check the status of the Droplet + create event. + + ### Create Multiple Droplets + + Creating multiple Droplets is very similar to creating a single Droplet. Instead + of sending `name` as a string, send `names` as an array of strings. A Droplet + will be created for each name you send using the associated information. Up to + ten Droplets may be created this way at a time. + + Rather than returning a single Droplet, the response body will contain a JSON + array with a key called `droplets`. This will be set to an array of JSON + objects, each of which will contain the standard Droplet attributes. The + response code, 202 Accepted, does not indicate the success or failure of any + operation, just that the request has been accepted for processing. The array of + `actions` returned as part of the response's `links` object can be used to check + the status of each individual Droplet create event. + + Args: + image: The image ID of a public or private image or the slug identifier for a public + image. This image will be the base image for your Droplet. Requires `image:read` + scope. + + name: The human-readable string you wish to use when displaying the Droplet name. The + name, if set to a domain name managed in the DigitalOcean DNS management system, + will configure a PTR record for the Droplet. The name set during creation will + also determine the hostname for the Droplet in its internal configuration. + + size: The slug identifier for the size that you wish to select for this Droplet. + + backup_policy: An object specifying the backup policy for the Droplet. If omitted and `backups` + is `true`, the backup plan will default to daily. + + backups: A boolean indicating whether automated backups should be enabled for the + Droplet. + + ipv6: A boolean indicating whether to enable IPv6 on the Droplet. + + monitoring: A boolean indicating whether to install the DigitalOcean agent for monitoring. + + private_networking: This parameter has been deprecated. Use `vpc_uuid` instead to specify a VPC + network for the Droplet. If no `vpc_uuid` is provided, the Droplet will be + placed in your account's default VPC for the region. + + region: The slug identifier for the region that you wish to deploy the Droplet in. If + the specific datacenter is not not important, a slug prefix (e.g. `nyc`) can be + used to deploy the Droplet in any of the that region's locations (`nyc1`, + `nyc2`, or `nyc3`). If the region is omitted from the create request completely, + the Droplet may deploy in any region. + + ssh_keys: An array containing the IDs or fingerprints of the SSH keys that you wish to + embed in the Droplet's root account upon creation. You must add the keys to your + team before they can be embedded on a Droplet. Requires `ssh_key:read` scope. + + tags: A flat array of tag names as strings to apply to the Droplet after it is + created. Tag names can either be existing or new tags. Requires `tag:create` + scope. + + user_data: A string containing 'user data' which may be used to configure the Droplet on + first boot, often a 'cloud-config' file or Bash script. It must be plain text + and may not exceed 64 KiB in size. + + volumes: An array of IDs for block storage volumes that will be attached to the Droplet + once created. The volumes must not already be attached to an existing Droplet. + Requires `block_storage:read` scpoe. + + vpc_uuid: A string specifying the UUID of the VPC to which the Droplet will be assigned. + If excluded, the Droplet will be assigned to your account's default VPC for the + region. Requires `vpc:read` scope. + + with_droplet_agent: A boolean indicating whether to install the DigitalOcean agent used for + providing access to the Droplet web console in the control panel. By default, + the agent is installed on new Droplets but installation errors (i.e. OS not + supported) are ignored. To prevent it from being installed, set to `false`. To + make installation errors fatal, explicitly set it to `true`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + image: Union[str, int], + names: SequenceNotStr[str], + size: str, + backup_policy: DropletBackupPolicyParam | Omit = omit, + backups: bool | Omit = omit, + ipv6: bool | Omit = omit, + monitoring: bool | Omit = omit, + private_networking: bool | Omit = omit, + region: str | Omit = omit, + ssh_keys: SequenceNotStr[Union[str, int]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + user_data: str | Omit = omit, + volumes: SequenceNotStr[str] | Omit = omit, + vpc_uuid: str | Omit = omit, + with_droplet_agent: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletCreateResponse: + """ + To create a new Droplet, send a POST request to `/v2/droplets` setting the + required attributes. + + A Droplet will be created using the provided information. The response body will + contain a JSON object with a key called `droplet`. The value will be an object + containing the standard attributes for your new Droplet. The response code, 202 + Accepted, does not indicate the success or failure of the operation, just that + the request has been accepted for processing. The `actions` returned as part of + the response's `links` object can be used to check the status of the Droplet + create event. + + ### Create Multiple Droplets + + Creating multiple Droplets is very similar to creating a single Droplet. Instead + of sending `name` as a string, send `names` as an array of strings. A Droplet + will be created for each name you send using the associated information. Up to + ten Droplets may be created this way at a time. + + Rather than returning a single Droplet, the response body will contain a JSON + array with a key called `droplets`. This will be set to an array of JSON + objects, each of which will contain the standard Droplet attributes. The + response code, 202 Accepted, does not indicate the success or failure of any + operation, just that the request has been accepted for processing. The array of + `actions` returned as part of the response's `links` object can be used to check + the status of each individual Droplet create event. + + Args: + image: The image ID of a public or private image or the slug identifier for a public + image. This image will be the base image for your Droplet. Requires `image:read` + scope. + + names: An array of human human-readable strings you wish to use when displaying the + Droplet name. Each name, if set to a domain name managed in the DigitalOcean DNS + management system, will configure a PTR record for the Droplet. Each name set + during creation will also determine the hostname for the Droplet in its internal + configuration. + + size: The slug identifier for the size that you wish to select for this Droplet. + + backup_policy: An object specifying the backup policy for the Droplet. If omitted and `backups` + is `true`, the backup plan will default to daily. + + backups: A boolean indicating whether automated backups should be enabled for the + Droplet. + + ipv6: A boolean indicating whether to enable IPv6 on the Droplet. + + monitoring: A boolean indicating whether to install the DigitalOcean agent for monitoring. + + private_networking: This parameter has been deprecated. Use `vpc_uuid` instead to specify a VPC + network for the Droplet. If no `vpc_uuid` is provided, the Droplet will be + placed in your account's default VPC for the region. + + region: The slug identifier for the region that you wish to deploy the Droplet in. If + the specific datacenter is not not important, a slug prefix (e.g. `nyc`) can be + used to deploy the Droplet in any of the that region's locations (`nyc1`, + `nyc2`, or `nyc3`). If the region is omitted from the create request completely, + the Droplet may deploy in any region. + + ssh_keys: An array containing the IDs or fingerprints of the SSH keys that you wish to + embed in the Droplet's root account upon creation. You must add the keys to your + team before they can be embedded on a Droplet. Requires `ssh_key:read` scope. + + tags: A flat array of tag names as strings to apply to the Droplet after it is + created. Tag names can either be existing or new tags. Requires `tag:create` + scope. + + user_data: A string containing 'user data' which may be used to configure the Droplet on + first boot, often a 'cloud-config' file or Bash script. It must be plain text + and may not exceed 64 KiB in size. + + volumes: An array of IDs for block storage volumes that will be attached to the Droplet + once created. The volumes must not already be attached to an existing Droplet. + Requires `block_storage:read` scpoe. + + vpc_uuid: A string specifying the UUID of the VPC to which the Droplet will be assigned. + If excluded, the Droplet will be assigned to your account's default VPC for the + region. Requires `vpc:read` scope. + + with_droplet_agent: A boolean indicating whether to install the DigitalOcean agent used for + providing access to the Droplet web console in the control panel. By default, + the agent is installed on new Droplets but installation errors (i.e. OS not + supported) are ignored. To prevent it from being installed, set to `false`. To + make installation errors fatal, explicitly set it to `true`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["image", "name", "size"], ["image", "names", "size"]) + def create( + self, + *, + image: Union[str, int], + name: str | Omit = omit, + size: str, + backup_policy: DropletBackupPolicyParam | Omit = omit, + backups: bool | Omit = omit, + ipv6: bool | Omit = omit, + monitoring: bool | Omit = omit, + private_networking: bool | Omit = omit, + region: str | Omit = omit, + ssh_keys: SequenceNotStr[Union[str, int]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + user_data: str | Omit = omit, + volumes: SequenceNotStr[str] | Omit = omit, + vpc_uuid: str | Omit = omit, + with_droplet_agent: bool | Omit = omit, + names: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletCreateResponse: + return cast( + GPUDropletCreateResponse, + self._post( + "/v2/droplets" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/droplets", + body=maybe_transform( + { + "image": image, + "name": name, + "size": size, + "backup_policy": backup_policy, + "backups": backups, + "ipv6": ipv6, + "monitoring": monitoring, + "private_networking": private_networking, + "region": region, + "ssh_keys": ssh_keys, + "tags": tags, + "user_data": user_data, + "volumes": volumes, + "vpc_uuid": vpc_uuid, + "with_droplet_agent": with_droplet_agent, + "names": names, + }, + gpu_droplet_create_params.GPUDropletCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, GPUDropletCreateResponse + ), # Union types cannot be passed in as arguments in the type system + ), + ) + + def retrieve( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletRetrieveResponse: + """ + To show information about an individual Droplet, send a GET request to + `/v2/droplets/$DROPLET_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUDropletRetrieveResponse, + ) + + def list( + self, + *, + name: str | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + tag_name: str | Omit = omit, + type: Literal["droplets", "gpus"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListResponse: + """ + To list all Droplets in your account, send a GET request to `/v2/droplets`. + + The response body will be a JSON object with a key of `droplets`. This will be + set to an array containing objects each representing a Droplet. These will + contain the standard Droplet attributes. + + ### Filtering Results by Tag + + It's possible to request filtered results by including certain query parameters. + To only list Droplets assigned to a specific tag, include the `tag_name` query + parameter set to the name of the tag in your GET request. For example, + `/v2/droplets?tag_name=$TAG_NAME`. + + ### GPU Droplets + + By default, only non-GPU Droplets are returned. To list only GPU Droplets, set + the `type` query parameter to `gpus`. For example, `/v2/droplets?type=gpus`. + + Args: + name: Used to filter list response by Droplet name returning only exact matches. It is + case-insensitive and can not be combined with `tag_name`. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + tag_name: Used to filter Droplets by a specific tag. Can not be combined with `name` or + `type`. Requires `tag:read` scope. + + type: When `type` is set to `gpus`, only GPU Droplets will be returned. By default, + only non-GPU Droplets are returned. Can not be combined with `tag_name`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/droplets" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/droplets", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "name": name, + "page": page, + "per_page": per_page, + "tag_name": tag_name, + "type": type, + }, + gpu_droplet_list_params.GPUDropletListParams, + ), + ), + cast_to=GPUDropletListResponse, + ) + + def delete( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a Droplet, send a DELETE request to `/v2/droplets/$DROPLET_ID`. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/droplets/{droplet_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def delete_by_tag( + self, + *, + tag_name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete **all** Droplets assigned to a specific tag, include the `tag_name` + query parameter set to the name of the tag in your DELETE request. For example, + `/v2/droplets?tag_name=$TAG_NAME`. + + This endpoint requires `tag:read` scope. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + tag_name: Specifies Droplets to be deleted by tag. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + "/v2/droplets" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/droplets", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"tag_name": tag_name}, gpu_droplet_delete_by_tag_params.GPUDropletDeleteByTagParams + ), + ), + cast_to=NoneType, + ) + + def list_firewalls( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListFirewallsResponse: + """ + To retrieve a list of all firewalls available to a Droplet, send a GET request + to `/v2/droplets/$DROPLET_ID/firewalls` + + The response will be a JSON object that has a key called `firewalls`. This will + be set to an array of `firewall` objects, each of which contain the standard + `firewall` attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/firewalls" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/firewalls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + gpu_droplet_list_firewalls_params.GPUDropletListFirewallsParams, + ), + ), + cast_to=GPUDropletListFirewallsResponse, + ) + + def list_kernels( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListKernelsResponse: + """ + To retrieve a list of all kernels available to a Droplet, send a GET request to + `/v2/droplets/$DROPLET_ID/kernels` + + The response will be a JSON object that has a key called `kernels`. This will be + set to an array of `kernel` objects, each of which contain the standard `kernel` + attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/kernels" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/kernels", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + gpu_droplet_list_kernels_params.GPUDropletListKernelsParams, + ), + ), + cast_to=GPUDropletListKernelsResponse, + ) + + def list_neighbors( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListNeighborsResponse: + """To retrieve a list of any "neighbors" (i.e. + + Droplets that are co-located on the + same physical hardware) for a specific Droplet, send a GET request to + `/v2/droplets/$DROPLET_ID/neighbors`. + + The results will be returned as a JSON object with a key of `droplets`. This + will be set to an array containing objects representing any other Droplets that + share the same physical hardware. An empty array indicates that the Droplet is + not co-located any other Droplets associated with your account. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/neighbors" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/neighbors", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUDropletListNeighborsResponse, + ) + + def list_snapshots( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListSnapshotsResponse: + """ + To retrieve the snapshots that have been created from a Droplet, send a GET + request to `/v2/droplets/$DROPLET_ID/snapshots`. + + You will get back a JSON object that has a `snapshots` key. This will be set to + an array of snapshot objects, each of which contain the standard Droplet + snapshot attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/droplets/{droplet_id}/snapshots" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + gpu_droplet_list_snapshots_params.GPUDropletListSnapshotsParams, + ), + ), + cast_to=GPUDropletListSnapshotsResponse, + ) + + +class AsyncGPUDropletsResource(AsyncAPIResource): + @cached_property + def backups(self) -> AsyncBackupsResource: + return AsyncBackupsResource(self._client) + + @cached_property + def actions(self) -> AsyncActionsResource: + return AsyncActionsResource(self._client) + + @cached_property + def destroy_with_associated_resources(self) -> AsyncDestroyWithAssociatedResourcesResource: + return AsyncDestroyWithAssociatedResourcesResource(self._client) + + @cached_property + def autoscale(self) -> AsyncAutoscaleResource: + return AsyncAutoscaleResource(self._client) + + @cached_property + def firewalls(self) -> AsyncFirewallsResource: + return AsyncFirewallsResource(self._client) + + @cached_property + def floating_ips(self) -> AsyncFloatingIPsResource: + return AsyncFloatingIPsResource(self._client) + + @cached_property + def images(self) -> AsyncImagesResource: + return AsyncImagesResource(self._client) + + @cached_property + def load_balancers(self) -> AsyncLoadBalancersResource: + return AsyncLoadBalancersResource(self._client) + + @cached_property + def sizes(self) -> AsyncSizesResource: + return AsyncSizesResource(self._client) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResource: + return AsyncSnapshotsResource(self._client) + + @cached_property + def volumes(self) -> AsyncVolumesResource: + return AsyncVolumesResource(self._client) + + @cached_property + def account(self) -> AsyncAccountResource: + return AsyncAccountResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncGPUDropletsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncGPUDropletsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGPUDropletsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncGPUDropletsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + image: Union[str, int], + name: str, + size: str, + backup_policy: DropletBackupPolicyParam | Omit = omit, + backups: bool | Omit = omit, + ipv6: bool | Omit = omit, + monitoring: bool | Omit = omit, + private_networking: bool | Omit = omit, + region: str | Omit = omit, + ssh_keys: SequenceNotStr[Union[str, int]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + user_data: str | Omit = omit, + volumes: SequenceNotStr[str] | Omit = omit, + vpc_uuid: str | Omit = omit, + with_droplet_agent: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletCreateResponse: + """ + To create a new Droplet, send a POST request to `/v2/droplets` setting the + required attributes. + + A Droplet will be created using the provided information. The response body will + contain a JSON object with a key called `droplet`. The value will be an object + containing the standard attributes for your new Droplet. The response code, 202 + Accepted, does not indicate the success or failure of the operation, just that + the request has been accepted for processing. The `actions` returned as part of + the response's `links` object can be used to check the status of the Droplet + create event. + + ### Create Multiple Droplets + + Creating multiple Droplets is very similar to creating a single Droplet. Instead + of sending `name` as a string, send `names` as an array of strings. A Droplet + will be created for each name you send using the associated information. Up to + ten Droplets may be created this way at a time. + + Rather than returning a single Droplet, the response body will contain a JSON + array with a key called `droplets`. This will be set to an array of JSON + objects, each of which will contain the standard Droplet attributes. The + response code, 202 Accepted, does not indicate the success or failure of any + operation, just that the request has been accepted for processing. The array of + `actions` returned as part of the response's `links` object can be used to check + the status of each individual Droplet create event. + + Args: + image: The image ID of a public or private image or the slug identifier for a public + image. This image will be the base image for your Droplet. Requires `image:read` + scope. + + name: The human-readable string you wish to use when displaying the Droplet name. The + name, if set to a domain name managed in the DigitalOcean DNS management system, + will configure a PTR record for the Droplet. The name set during creation will + also determine the hostname for the Droplet in its internal configuration. + + size: The slug identifier for the size that you wish to select for this Droplet. + + backup_policy: An object specifying the backup policy for the Droplet. If omitted and `backups` + is `true`, the backup plan will default to daily. + + backups: A boolean indicating whether automated backups should be enabled for the + Droplet. + + ipv6: A boolean indicating whether to enable IPv6 on the Droplet. + + monitoring: A boolean indicating whether to install the DigitalOcean agent for monitoring. + + private_networking: This parameter has been deprecated. Use `vpc_uuid` instead to specify a VPC + network for the Droplet. If no `vpc_uuid` is provided, the Droplet will be + placed in your account's default VPC for the region. + + region: The slug identifier for the region that you wish to deploy the Droplet in. If + the specific datacenter is not not important, a slug prefix (e.g. `nyc`) can be + used to deploy the Droplet in any of the that region's locations (`nyc1`, + `nyc2`, or `nyc3`). If the region is omitted from the create request completely, + the Droplet may deploy in any region. + + ssh_keys: An array containing the IDs or fingerprints of the SSH keys that you wish to + embed in the Droplet's root account upon creation. You must add the keys to your + team before they can be embedded on a Droplet. Requires `ssh_key:read` scope. + + tags: A flat array of tag names as strings to apply to the Droplet after it is + created. Tag names can either be existing or new tags. Requires `tag:create` + scope. + + user_data: A string containing 'user data' which may be used to configure the Droplet on + first boot, often a 'cloud-config' file or Bash script. It must be plain text + and may not exceed 64 KiB in size. + + volumes: An array of IDs for block storage volumes that will be attached to the Droplet + once created. The volumes must not already be attached to an existing Droplet. + Requires `block_storage:read` scpoe. + + vpc_uuid: A string specifying the UUID of the VPC to which the Droplet will be assigned. + If excluded, the Droplet will be assigned to your account's default VPC for the + region. Requires `vpc:read` scope. + + with_droplet_agent: A boolean indicating whether to install the DigitalOcean agent used for + providing access to the Droplet web console in the control panel. By default, + the agent is installed on new Droplets but installation errors (i.e. OS not + supported) are ignored. To prevent it from being installed, set to `false`. To + make installation errors fatal, explicitly set it to `true`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + image: Union[str, int], + names: SequenceNotStr[str], + size: str, + backup_policy: DropletBackupPolicyParam | Omit = omit, + backups: bool | Omit = omit, + ipv6: bool | Omit = omit, + monitoring: bool | Omit = omit, + private_networking: bool | Omit = omit, + region: str | Omit = omit, + ssh_keys: SequenceNotStr[Union[str, int]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + user_data: str | Omit = omit, + volumes: SequenceNotStr[str] | Omit = omit, + vpc_uuid: str | Omit = omit, + with_droplet_agent: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletCreateResponse: + """ + To create a new Droplet, send a POST request to `/v2/droplets` setting the + required attributes. + + A Droplet will be created using the provided information. The response body will + contain a JSON object with a key called `droplet`. The value will be an object + containing the standard attributes for your new Droplet. The response code, 202 + Accepted, does not indicate the success or failure of the operation, just that + the request has been accepted for processing. The `actions` returned as part of + the response's `links` object can be used to check the status of the Droplet + create event. + + ### Create Multiple Droplets + + Creating multiple Droplets is very similar to creating a single Droplet. Instead + of sending `name` as a string, send `names` as an array of strings. A Droplet + will be created for each name you send using the associated information. Up to + ten Droplets may be created this way at a time. + + Rather than returning a single Droplet, the response body will contain a JSON + array with a key called `droplets`. This will be set to an array of JSON + objects, each of which will contain the standard Droplet attributes. The + response code, 202 Accepted, does not indicate the success or failure of any + operation, just that the request has been accepted for processing. The array of + `actions` returned as part of the response's `links` object can be used to check + the status of each individual Droplet create event. + + Args: + image: The image ID of a public or private image or the slug identifier for a public + image. This image will be the base image for your Droplet. Requires `image:read` + scope. + + names: An array of human human-readable strings you wish to use when displaying the + Droplet name. Each name, if set to a domain name managed in the DigitalOcean DNS + management system, will configure a PTR record for the Droplet. Each name set + during creation will also determine the hostname for the Droplet in its internal + configuration. + + size: The slug identifier for the size that you wish to select for this Droplet. + + backup_policy: An object specifying the backup policy for the Droplet. If omitted and `backups` + is `true`, the backup plan will default to daily. + + backups: A boolean indicating whether automated backups should be enabled for the + Droplet. + + ipv6: A boolean indicating whether to enable IPv6 on the Droplet. + + monitoring: A boolean indicating whether to install the DigitalOcean agent for monitoring. + + private_networking: This parameter has been deprecated. Use `vpc_uuid` instead to specify a VPC + network for the Droplet. If no `vpc_uuid` is provided, the Droplet will be + placed in your account's default VPC for the region. + + region: The slug identifier for the region that you wish to deploy the Droplet in. If + the specific datacenter is not not important, a slug prefix (e.g. `nyc`) can be + used to deploy the Droplet in any of the that region's locations (`nyc1`, + `nyc2`, or `nyc3`). If the region is omitted from the create request completely, + the Droplet may deploy in any region. + + ssh_keys: An array containing the IDs or fingerprints of the SSH keys that you wish to + embed in the Droplet's root account upon creation. You must add the keys to your + team before they can be embedded on a Droplet. Requires `ssh_key:read` scope. + + tags: A flat array of tag names as strings to apply to the Droplet after it is + created. Tag names can either be existing or new tags. Requires `tag:create` + scope. + + user_data: A string containing 'user data' which may be used to configure the Droplet on + first boot, often a 'cloud-config' file or Bash script. It must be plain text + and may not exceed 64 KiB in size. + + volumes: An array of IDs for block storage volumes that will be attached to the Droplet + once created. The volumes must not already be attached to an existing Droplet. + Requires `block_storage:read` scpoe. + + vpc_uuid: A string specifying the UUID of the VPC to which the Droplet will be assigned. + If excluded, the Droplet will be assigned to your account's default VPC for the + region. Requires `vpc:read` scope. + + with_droplet_agent: A boolean indicating whether to install the DigitalOcean agent used for + providing access to the Droplet web console in the control panel. By default, + the agent is installed on new Droplets but installation errors (i.e. OS not + supported) are ignored. To prevent it from being installed, set to `false`. To + make installation errors fatal, explicitly set it to `true`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["image", "name", "size"], ["image", "names", "size"]) + async def create( + self, + *, + image: Union[str, int], + name: str | Omit = omit, + size: str, + backup_policy: DropletBackupPolicyParam | Omit = omit, + backups: bool | Omit = omit, + ipv6: bool | Omit = omit, + monitoring: bool | Omit = omit, + private_networking: bool | Omit = omit, + region: str | Omit = omit, + ssh_keys: SequenceNotStr[Union[str, int]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + user_data: str | Omit = omit, + volumes: SequenceNotStr[str] | Omit = omit, + vpc_uuid: str | Omit = omit, + with_droplet_agent: bool | Omit = omit, + names: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletCreateResponse: + return cast( + GPUDropletCreateResponse, + await self._post( + "/v2/droplets" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/droplets", + body=await async_maybe_transform( + { + "image": image, + "name": name, + "size": size, + "backup_policy": backup_policy, + "backups": backups, + "ipv6": ipv6, + "monitoring": monitoring, + "private_networking": private_networking, + "region": region, + "ssh_keys": ssh_keys, + "tags": tags, + "user_data": user_data, + "volumes": volumes, + "vpc_uuid": vpc_uuid, + "with_droplet_agent": with_droplet_agent, + "names": names, + }, + gpu_droplet_create_params.GPUDropletCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, GPUDropletCreateResponse + ), # Union types cannot be passed in as arguments in the type system + ), + ) + + async def retrieve( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletRetrieveResponse: + """ + To show information about an individual Droplet, send a GET request to + `/v2/droplets/$DROPLET_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUDropletRetrieveResponse, + ) + + async def list( + self, + *, + name: str | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + tag_name: str | Omit = omit, + type: Literal["droplets", "gpus"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListResponse: + """ + To list all Droplets in your account, send a GET request to `/v2/droplets`. + + The response body will be a JSON object with a key of `droplets`. This will be + set to an array containing objects each representing a Droplet. These will + contain the standard Droplet attributes. + + ### Filtering Results by Tag + + It's possible to request filtered results by including certain query parameters. + To only list Droplets assigned to a specific tag, include the `tag_name` query + parameter set to the name of the tag in your GET request. For example, + `/v2/droplets?tag_name=$TAG_NAME`. + + ### GPU Droplets + + By default, only non-GPU Droplets are returned. To list only GPU Droplets, set + the `type` query parameter to `gpus`. For example, `/v2/droplets?type=gpus`. + + Args: + name: Used to filter list response by Droplet name returning only exact matches. It is + case-insensitive and can not be combined with `tag_name`. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + tag_name: Used to filter Droplets by a specific tag. Can not be combined with `name` or + `type`. Requires `tag:read` scope. + + type: When `type` is set to `gpus`, only GPU Droplets will be returned. By default, + only non-GPU Droplets are returned. Can not be combined with `tag_name`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/droplets" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/droplets", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "name": name, + "page": page, + "per_page": per_page, + "tag_name": tag_name, + "type": type, + }, + gpu_droplet_list_params.GPUDropletListParams, + ), + ), + cast_to=GPUDropletListResponse, + ) + + async def delete( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a Droplet, send a DELETE request to `/v2/droplets/$DROPLET_ID`. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/droplets/{droplet_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def delete_by_tag( + self, + *, + tag_name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete **all** Droplets assigned to a specific tag, include the `tag_name` + query parameter set to the name of the tag in your DELETE request. For example, + `/v2/droplets?tag_name=$TAG_NAME`. + + This endpoint requires `tag:read` scope. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + tag_name: Specifies Droplets to be deleted by tag. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + "/v2/droplets" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/droplets", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"tag_name": tag_name}, gpu_droplet_delete_by_tag_params.GPUDropletDeleteByTagParams + ), + ), + cast_to=NoneType, + ) + + async def list_firewalls( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListFirewallsResponse: + """ + To retrieve a list of all firewalls available to a Droplet, send a GET request + to `/v2/droplets/$DROPLET_ID/firewalls` + + The response will be a JSON object that has a key called `firewalls`. This will + be set to an array of `firewall` objects, each of which contain the standard + `firewall` attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/firewalls" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/firewalls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + gpu_droplet_list_firewalls_params.GPUDropletListFirewallsParams, + ), + ), + cast_to=GPUDropletListFirewallsResponse, + ) + + async def list_kernels( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListKernelsResponse: + """ + To retrieve a list of all kernels available to a Droplet, send a GET request to + `/v2/droplets/$DROPLET_ID/kernels` + + The response will be a JSON object that has a key called `kernels`. This will be + set to an array of `kernel` objects, each of which contain the standard `kernel` + attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/kernels" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/kernels", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + gpu_droplet_list_kernels_params.GPUDropletListKernelsParams, + ), + ), + cast_to=GPUDropletListKernelsResponse, + ) + + async def list_neighbors( + self, + droplet_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListNeighborsResponse: + """To retrieve a list of any "neighbors" (i.e. + + Droplets that are co-located on the + same physical hardware) for a specific Droplet, send a GET request to + `/v2/droplets/$DROPLET_ID/neighbors`. + + The results will be returned as a JSON object with a key of `droplets`. This + will be set to an array containing objects representing any other Droplets that + share the same physical hardware. An empty array indicates that the Droplet is + not co-located any other Droplets associated with your account. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/neighbors" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/neighbors", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUDropletListNeighborsResponse, + ) + + async def list_snapshots( + self, + droplet_id: int, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUDropletListSnapshotsResponse: + """ + To retrieve the snapshots that have been created from a Droplet, send a GET + request to `/v2/droplets/$DROPLET_ID/snapshots`. + + You will get back a JSON object that has a `snapshots` key. This will be set to + an array of snapshot objects, each of which contain the standard Droplet + snapshot attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/droplets/{droplet_id}/snapshots" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/droplets/{droplet_id}/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + gpu_droplet_list_snapshots_params.GPUDropletListSnapshotsParams, + ), + ), + cast_to=GPUDropletListSnapshotsResponse, + ) + + +class GPUDropletsResourceWithRawResponse: + def __init__(self, gpu_droplets: GPUDropletsResource) -> None: + self._gpu_droplets = gpu_droplets + + self.create = to_raw_response_wrapper( + gpu_droplets.create, + ) + self.retrieve = to_raw_response_wrapper( + gpu_droplets.retrieve, + ) + self.list = to_raw_response_wrapper( + gpu_droplets.list, + ) + self.delete = to_raw_response_wrapper( + gpu_droplets.delete, + ) + self.delete_by_tag = to_raw_response_wrapper( + gpu_droplets.delete_by_tag, + ) + self.list_firewalls = to_raw_response_wrapper( + gpu_droplets.list_firewalls, + ) + self.list_kernels = to_raw_response_wrapper( + gpu_droplets.list_kernels, + ) + self.list_neighbors = to_raw_response_wrapper( + gpu_droplets.list_neighbors, + ) + self.list_snapshots = to_raw_response_wrapper( + gpu_droplets.list_snapshots, + ) + + @cached_property + def backups(self) -> BackupsResourceWithRawResponse: + return BackupsResourceWithRawResponse(self._gpu_droplets.backups) + + @cached_property + def actions(self) -> ActionsResourceWithRawResponse: + return ActionsResourceWithRawResponse(self._gpu_droplets.actions) + + @cached_property + def destroy_with_associated_resources(self) -> DestroyWithAssociatedResourcesResourceWithRawResponse: + return DestroyWithAssociatedResourcesResourceWithRawResponse( + self._gpu_droplets.destroy_with_associated_resources + ) + + @cached_property + def autoscale(self) -> AutoscaleResourceWithRawResponse: + return AutoscaleResourceWithRawResponse(self._gpu_droplets.autoscale) + + @cached_property + def firewalls(self) -> FirewallsResourceWithRawResponse: + return FirewallsResourceWithRawResponse(self._gpu_droplets.firewalls) + + @cached_property + def floating_ips(self) -> FloatingIPsResourceWithRawResponse: + return FloatingIPsResourceWithRawResponse(self._gpu_droplets.floating_ips) + + @cached_property + def images(self) -> ImagesResourceWithRawResponse: + return ImagesResourceWithRawResponse(self._gpu_droplets.images) + + @cached_property + def load_balancers(self) -> LoadBalancersResourceWithRawResponse: + return LoadBalancersResourceWithRawResponse(self._gpu_droplets.load_balancers) + + @cached_property + def sizes(self) -> SizesResourceWithRawResponse: + return SizesResourceWithRawResponse(self._gpu_droplets.sizes) + + @cached_property + def snapshots(self) -> SnapshotsResourceWithRawResponse: + return SnapshotsResourceWithRawResponse(self._gpu_droplets.snapshots) + + @cached_property + def volumes(self) -> VolumesResourceWithRawResponse: + return VolumesResourceWithRawResponse(self._gpu_droplets.volumes) + + @cached_property + def account(self) -> AccountResourceWithRawResponse: + return AccountResourceWithRawResponse(self._gpu_droplets.account) + + +class AsyncGPUDropletsResourceWithRawResponse: + def __init__(self, gpu_droplets: AsyncGPUDropletsResource) -> None: + self._gpu_droplets = gpu_droplets + + self.create = async_to_raw_response_wrapper( + gpu_droplets.create, + ) + self.retrieve = async_to_raw_response_wrapper( + gpu_droplets.retrieve, + ) + self.list = async_to_raw_response_wrapper( + gpu_droplets.list, + ) + self.delete = async_to_raw_response_wrapper( + gpu_droplets.delete, + ) + self.delete_by_tag = async_to_raw_response_wrapper( + gpu_droplets.delete_by_tag, + ) + self.list_firewalls = async_to_raw_response_wrapper( + gpu_droplets.list_firewalls, + ) + self.list_kernels = async_to_raw_response_wrapper( + gpu_droplets.list_kernels, + ) + self.list_neighbors = async_to_raw_response_wrapper( + gpu_droplets.list_neighbors, + ) + self.list_snapshots = async_to_raw_response_wrapper( + gpu_droplets.list_snapshots, + ) + + @cached_property + def backups(self) -> AsyncBackupsResourceWithRawResponse: + return AsyncBackupsResourceWithRawResponse(self._gpu_droplets.backups) + + @cached_property + def actions(self) -> AsyncActionsResourceWithRawResponse: + return AsyncActionsResourceWithRawResponse(self._gpu_droplets.actions) + + @cached_property + def destroy_with_associated_resources(self) -> AsyncDestroyWithAssociatedResourcesResourceWithRawResponse: + return AsyncDestroyWithAssociatedResourcesResourceWithRawResponse( + self._gpu_droplets.destroy_with_associated_resources + ) + + @cached_property + def autoscale(self) -> AsyncAutoscaleResourceWithRawResponse: + return AsyncAutoscaleResourceWithRawResponse(self._gpu_droplets.autoscale) + + @cached_property + def firewalls(self) -> AsyncFirewallsResourceWithRawResponse: + return AsyncFirewallsResourceWithRawResponse(self._gpu_droplets.firewalls) + + @cached_property + def floating_ips(self) -> AsyncFloatingIPsResourceWithRawResponse: + return AsyncFloatingIPsResourceWithRawResponse(self._gpu_droplets.floating_ips) + + @cached_property + def images(self) -> AsyncImagesResourceWithRawResponse: + return AsyncImagesResourceWithRawResponse(self._gpu_droplets.images) + + @cached_property + def load_balancers(self) -> AsyncLoadBalancersResourceWithRawResponse: + return AsyncLoadBalancersResourceWithRawResponse(self._gpu_droplets.load_balancers) + + @cached_property + def sizes(self) -> AsyncSizesResourceWithRawResponse: + return AsyncSizesResourceWithRawResponse(self._gpu_droplets.sizes) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResourceWithRawResponse: + return AsyncSnapshotsResourceWithRawResponse(self._gpu_droplets.snapshots) + + @cached_property + def volumes(self) -> AsyncVolumesResourceWithRawResponse: + return AsyncVolumesResourceWithRawResponse(self._gpu_droplets.volumes) + + @cached_property + def account(self) -> AsyncAccountResourceWithRawResponse: + return AsyncAccountResourceWithRawResponse(self._gpu_droplets.account) + + +class GPUDropletsResourceWithStreamingResponse: + def __init__(self, gpu_droplets: GPUDropletsResource) -> None: + self._gpu_droplets = gpu_droplets + + self.create = to_streamed_response_wrapper( + gpu_droplets.create, + ) + self.retrieve = to_streamed_response_wrapper( + gpu_droplets.retrieve, + ) + self.list = to_streamed_response_wrapper( + gpu_droplets.list, + ) + self.delete = to_streamed_response_wrapper( + gpu_droplets.delete, + ) + self.delete_by_tag = to_streamed_response_wrapper( + gpu_droplets.delete_by_tag, + ) + self.list_firewalls = to_streamed_response_wrapper( + gpu_droplets.list_firewalls, + ) + self.list_kernels = to_streamed_response_wrapper( + gpu_droplets.list_kernels, + ) + self.list_neighbors = to_streamed_response_wrapper( + gpu_droplets.list_neighbors, + ) + self.list_snapshots = to_streamed_response_wrapper( + gpu_droplets.list_snapshots, + ) + + @cached_property + def backups(self) -> BackupsResourceWithStreamingResponse: + return BackupsResourceWithStreamingResponse(self._gpu_droplets.backups) + + @cached_property + def actions(self) -> ActionsResourceWithStreamingResponse: + return ActionsResourceWithStreamingResponse(self._gpu_droplets.actions) + + @cached_property + def destroy_with_associated_resources(self) -> DestroyWithAssociatedResourcesResourceWithStreamingResponse: + return DestroyWithAssociatedResourcesResourceWithStreamingResponse( + self._gpu_droplets.destroy_with_associated_resources + ) + + @cached_property + def autoscale(self) -> AutoscaleResourceWithStreamingResponse: + return AutoscaleResourceWithStreamingResponse(self._gpu_droplets.autoscale) + + @cached_property + def firewalls(self) -> FirewallsResourceWithStreamingResponse: + return FirewallsResourceWithStreamingResponse(self._gpu_droplets.firewalls) + + @cached_property + def floating_ips(self) -> FloatingIPsResourceWithStreamingResponse: + return FloatingIPsResourceWithStreamingResponse(self._gpu_droplets.floating_ips) + + @cached_property + def images(self) -> ImagesResourceWithStreamingResponse: + return ImagesResourceWithStreamingResponse(self._gpu_droplets.images) + + @cached_property + def load_balancers(self) -> LoadBalancersResourceWithStreamingResponse: + return LoadBalancersResourceWithStreamingResponse(self._gpu_droplets.load_balancers) + + @cached_property + def sizes(self) -> SizesResourceWithStreamingResponse: + return SizesResourceWithStreamingResponse(self._gpu_droplets.sizes) + + @cached_property + def snapshots(self) -> SnapshotsResourceWithStreamingResponse: + return SnapshotsResourceWithStreamingResponse(self._gpu_droplets.snapshots) + + @cached_property + def volumes(self) -> VolumesResourceWithStreamingResponse: + return VolumesResourceWithStreamingResponse(self._gpu_droplets.volumes) + + @cached_property + def account(self) -> AccountResourceWithStreamingResponse: + return AccountResourceWithStreamingResponse(self._gpu_droplets.account) + + +class AsyncGPUDropletsResourceWithStreamingResponse: + def __init__(self, gpu_droplets: AsyncGPUDropletsResource) -> None: + self._gpu_droplets = gpu_droplets + + self.create = async_to_streamed_response_wrapper( + gpu_droplets.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + gpu_droplets.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + gpu_droplets.list, + ) + self.delete = async_to_streamed_response_wrapper( + gpu_droplets.delete, + ) + self.delete_by_tag = async_to_streamed_response_wrapper( + gpu_droplets.delete_by_tag, + ) + self.list_firewalls = async_to_streamed_response_wrapper( + gpu_droplets.list_firewalls, + ) + self.list_kernels = async_to_streamed_response_wrapper( + gpu_droplets.list_kernels, + ) + self.list_neighbors = async_to_streamed_response_wrapper( + gpu_droplets.list_neighbors, + ) + self.list_snapshots = async_to_streamed_response_wrapper( + gpu_droplets.list_snapshots, + ) + + @cached_property + def backups(self) -> AsyncBackupsResourceWithStreamingResponse: + return AsyncBackupsResourceWithStreamingResponse(self._gpu_droplets.backups) + + @cached_property + def actions(self) -> AsyncActionsResourceWithStreamingResponse: + return AsyncActionsResourceWithStreamingResponse(self._gpu_droplets.actions) + + @cached_property + def destroy_with_associated_resources(self) -> AsyncDestroyWithAssociatedResourcesResourceWithStreamingResponse: + return AsyncDestroyWithAssociatedResourcesResourceWithStreamingResponse( + self._gpu_droplets.destroy_with_associated_resources + ) + + @cached_property + def autoscale(self) -> AsyncAutoscaleResourceWithStreamingResponse: + return AsyncAutoscaleResourceWithStreamingResponse(self._gpu_droplets.autoscale) + + @cached_property + def firewalls(self) -> AsyncFirewallsResourceWithStreamingResponse: + return AsyncFirewallsResourceWithStreamingResponse(self._gpu_droplets.firewalls) + + @cached_property + def floating_ips(self) -> AsyncFloatingIPsResourceWithStreamingResponse: + return AsyncFloatingIPsResourceWithStreamingResponse(self._gpu_droplets.floating_ips) + + @cached_property + def images(self) -> AsyncImagesResourceWithStreamingResponse: + return AsyncImagesResourceWithStreamingResponse(self._gpu_droplets.images) + + @cached_property + def load_balancers(self) -> AsyncLoadBalancersResourceWithStreamingResponse: + return AsyncLoadBalancersResourceWithStreamingResponse(self._gpu_droplets.load_balancers) + + @cached_property + def sizes(self) -> AsyncSizesResourceWithStreamingResponse: + return AsyncSizesResourceWithStreamingResponse(self._gpu_droplets.sizes) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResourceWithStreamingResponse: + return AsyncSnapshotsResourceWithStreamingResponse(self._gpu_droplets.snapshots) + + @cached_property + def volumes(self) -> AsyncVolumesResourceWithStreamingResponse: + return AsyncVolumesResourceWithStreamingResponse(self._gpu_droplets.volumes) + + @cached_property + def account(self) -> AsyncAccountResourceWithStreamingResponse: + return AsyncAccountResourceWithStreamingResponse(self._gpu_droplets.account) diff --git a/src/gradient/resources/gpu_droplets/images/__init__.py b/src/gradient/resources/gpu_droplets/images/__init__.py new file mode 100644 index 00000000..477fd657 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/images/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .images import ( + ImagesResource, + AsyncImagesResource, + ImagesResourceWithRawResponse, + AsyncImagesResourceWithRawResponse, + ImagesResourceWithStreamingResponse, + AsyncImagesResourceWithStreamingResponse, +) +from .actions import ( + ActionsResource, + AsyncActionsResource, + ActionsResourceWithRawResponse, + AsyncActionsResourceWithRawResponse, + ActionsResourceWithStreamingResponse, + AsyncActionsResourceWithStreamingResponse, +) + +__all__ = [ + "ActionsResource", + "AsyncActionsResource", + "ActionsResourceWithRawResponse", + "AsyncActionsResourceWithRawResponse", + "ActionsResourceWithStreamingResponse", + "AsyncActionsResourceWithStreamingResponse", + "ImagesResource", + "AsyncImagesResource", + "ImagesResourceWithRawResponse", + "AsyncImagesResourceWithRawResponse", + "ImagesResourceWithStreamingResponse", + "AsyncImagesResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/gpu_droplets/images/actions.py b/src/gradient/resources/gpu_droplets/images/actions.py new file mode 100644 index 00000000..d2d33f11 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/images/actions.py @@ -0,0 +1,560 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, overload + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.shared.action import Action +from ....types.gpu_droplets.images import action_create_params +from ....types.gpu_droplets.images.action_list_response import ActionListResponse + +__all__ = ["ActionsResource", "AsyncActionsResource"] + + +class ActionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ActionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ActionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ActionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ActionsResourceWithStreamingResponse(self) + + @overload + def create( + self, + image_id: int, + *, + type: Literal["convert", "transfer"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Action: + """ + The following actions are available on an Image. + + ## Convert an Image to a Snapshot + + To convert an image, for example, a backup to a snapshot, send a POST request to + `/v2/images/$IMAGE_ID/actions`. Set the `type` attribute to `convert`. + + ## Transfer an Image + + To transfer an image to another region, send a POST request to + `/v2/images/$IMAGE_ID/actions`. Set the `type` attribute to `transfer` and set + `region` attribute to the slug identifier of the region you wish to transfer to. + + Args: + type: The action to be taken on the image. Can be either `convert` or `transfer`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + image_id: int, + *, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ], + type: Literal["convert", "transfer"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Action: + """ + The following actions are available on an Image. + + ## Convert an Image to a Snapshot + + To convert an image, for example, a backup to a snapshot, send a POST request to + `/v2/images/$IMAGE_ID/actions`. Set the `type` attribute to `convert`. + + ## Transfer an Image + + To transfer an image to another region, send a POST request to + `/v2/images/$IMAGE_ID/actions`. Set the `type` attribute to `transfer` and set + `region` attribute to the slug identifier of the region you wish to transfer to. + + Args: + region: The slug identifier for the region where the resource will initially be + available. + + type: The action to be taken on the image. Can be either `convert` or `transfer`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["type"], ["region", "type"]) + def create( + self, + image_id: int, + *, + type: Literal["convert", "transfer"], + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Action: + return self._post( + f"/v2/images/{image_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}/actions", + body=maybe_transform( + { + "type": type, + "region": region, + }, + action_create_params.ActionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Action, + ) + + def retrieve( + self, + action_id: int, + *, + image_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Action: + """ + To retrieve the status of an image action, send a GET request to + `/v2/images/$IMAGE_ID/actions/$IMAGE_ACTION_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/images/{image_id}/actions/{action_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}/actions/{action_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Action, + ) + + def list( + self, + image_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionListResponse: + """ + To retrieve all actions that have been executed on an image, send a GET request + to `/v2/images/$IMAGE_ID/actions`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/images/{image_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}/actions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionListResponse, + ) + + +class AsyncActionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncActionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncActionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncActionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncActionsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + image_id: int, + *, + type: Literal["convert", "transfer"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Action: + """ + The following actions are available on an Image. + + ## Convert an Image to a Snapshot + + To convert an image, for example, a backup to a snapshot, send a POST request to + `/v2/images/$IMAGE_ID/actions`. Set the `type` attribute to `convert`. + + ## Transfer an Image + + To transfer an image to another region, send a POST request to + `/v2/images/$IMAGE_ID/actions`. Set the `type` attribute to `transfer` and set + `region` attribute to the slug identifier of the region you wish to transfer to. + + Args: + type: The action to be taken on the image. Can be either `convert` or `transfer`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + image_id: int, + *, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ], + type: Literal["convert", "transfer"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Action: + """ + The following actions are available on an Image. + + ## Convert an Image to a Snapshot + + To convert an image, for example, a backup to a snapshot, send a POST request to + `/v2/images/$IMAGE_ID/actions`. Set the `type` attribute to `convert`. + + ## Transfer an Image + + To transfer an image to another region, send a POST request to + `/v2/images/$IMAGE_ID/actions`. Set the `type` attribute to `transfer` and set + `region` attribute to the slug identifier of the region you wish to transfer to. + + Args: + region: The slug identifier for the region where the resource will initially be + available. + + type: The action to be taken on the image. Can be either `convert` or `transfer`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["type"], ["region", "type"]) + async def create( + self, + image_id: int, + *, + type: Literal["convert", "transfer"], + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Action: + return await self._post( + f"/v2/images/{image_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}/actions", + body=await async_maybe_transform( + { + "type": type, + "region": region, + }, + action_create_params.ActionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Action, + ) + + async def retrieve( + self, + action_id: int, + *, + image_id: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Action: + """ + To retrieve the status of an image action, send a GET request to + `/v2/images/$IMAGE_ID/actions/$IMAGE_ACTION_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/images/{image_id}/actions/{action_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}/actions/{action_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Action, + ) + + async def list( + self, + image_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionListResponse: + """ + To retrieve all actions that have been executed on an image, send a GET request + to `/v2/images/$IMAGE_ID/actions`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/images/{image_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}/actions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ActionListResponse, + ) + + +class ActionsResourceWithRawResponse: + def __init__(self, actions: ActionsResource) -> None: + self._actions = actions + + self.create = to_raw_response_wrapper( + actions.create, + ) + self.retrieve = to_raw_response_wrapper( + actions.retrieve, + ) + self.list = to_raw_response_wrapper( + actions.list, + ) + + +class AsyncActionsResourceWithRawResponse: + def __init__(self, actions: AsyncActionsResource) -> None: + self._actions = actions + + self.create = async_to_raw_response_wrapper( + actions.create, + ) + self.retrieve = async_to_raw_response_wrapper( + actions.retrieve, + ) + self.list = async_to_raw_response_wrapper( + actions.list, + ) + + +class ActionsResourceWithStreamingResponse: + def __init__(self, actions: ActionsResource) -> None: + self._actions = actions + + self.create = to_streamed_response_wrapper( + actions.create, + ) + self.retrieve = to_streamed_response_wrapper( + actions.retrieve, + ) + self.list = to_streamed_response_wrapper( + actions.list, + ) + + +class AsyncActionsResourceWithStreamingResponse: + def __init__(self, actions: AsyncActionsResource) -> None: + self._actions = actions + + self.create = async_to_streamed_response_wrapper( + actions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + actions.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + actions.list, + ) diff --git a/src/gradient/resources/gpu_droplets/images/images.py b/src/gradient/resources/gpu_droplets/images/images.py new file mode 100644 index 00000000..83e04d13 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/images/images.py @@ -0,0 +1,867 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal + +import httpx + +from .actions import ( + ActionsResource, + AsyncActionsResource, + ActionsResourceWithRawResponse, + AsyncActionsResourceWithRawResponse, + ActionsResourceWithStreamingResponse, + AsyncActionsResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets import image_list_params, image_create_params, image_update_params +from ....types.gpu_droplets.image_list_response import ImageListResponse +from ....types.gpu_droplets.image_create_response import ImageCreateResponse +from ....types.gpu_droplets.image_update_response import ImageUpdateResponse +from ....types.gpu_droplets.image_retrieve_response import ImageRetrieveResponse + +__all__ = ["ImagesResource", "AsyncImagesResource"] + + +class ImagesResource(SyncAPIResource): + @cached_property + def actions(self) -> ActionsResource: + return ActionsResource(self._client) + + @cached_property + def with_raw_response(self) -> ImagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ImagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ImagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ImagesResourceWithStreamingResponse(self) + + def create( + self, + *, + description: str | Omit = omit, + distribution: Literal[ + "Arch Linux", + "CentOS", + "CoreOS", + "Debian", + "Fedora", + "Fedora Atomic", + "FreeBSD", + "Gentoo", + "openSUSE", + "RancherOS", + "Rocky Linux", + "Ubuntu", + "Unknown", + ] + | Omit = omit, + name: str | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageCreateResponse: + """To create a new custom image, send a POST request to /v2/images. + + The body must + contain a url attribute pointing to a Linux virtual machine image to be imported + into DigitalOcean. The image must be in the raw, qcow2, vhdx, vdi, or vmdk + format. It may be compressed using gzip or bzip2 and must be smaller than 100 GB + after being decompressed. + + Args: + description: An optional free-form text field to describe an image. + + distribution: The name of a custom image's distribution. Currently, the valid values are + `Arch Linux`, `CentOS`, `CoreOS`, `Debian`, `Fedora`, `Fedora Atomic`, + `FreeBSD`, `Gentoo`, `openSUSE`, `RancherOS`, `Rocky Linux`, `Ubuntu`, and + `Unknown`. Any other value will be accepted but ignored, and `Unknown` will be + used in its place. + + name: The display name that has been given to an image. This is what is shown in the + control panel and is generally a descriptive title for the image in question. + + region: The slug identifier for the region where the resource will initially be + available. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + url: A URL from which the custom Linux virtual machine image may be retrieved. The + image it points to must be in the raw, qcow2, vhdx, vdi, or vmdk format. It may + be compressed using gzip or bzip2 and must be smaller than 100 GB after being + decompressed. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/images" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/images", + body=maybe_transform( + { + "description": description, + "distribution": distribution, + "name": name, + "region": region, + "tags": tags, + "url": url, + }, + image_create_params.ImageCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImageCreateResponse, + ) + + def retrieve( + self, + image_id: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageRetrieveResponse: + """ + To retrieve information about an image, send a `GET` request to + `/v2/images/$IDENTIFIER`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/images/{image_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImageRetrieveResponse, + ) + + def update( + self, + image_id: int, + *, + description: str | Omit = omit, + distribution: Literal[ + "Arch Linux", + "CentOS", + "CoreOS", + "Debian", + "Fedora", + "Fedora Atomic", + "FreeBSD", + "Gentoo", + "openSUSE", + "RancherOS", + "Rocky Linux", + "Ubuntu", + "Unknown", + ] + | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageUpdateResponse: + """To update an image, send a `PUT` request to `/v2/images/$IMAGE_ID`. + + Set the + `name` attribute to the new value you would like to use. For custom images, the + `description` and `distribution` attributes may also be updated. + + Args: + description: An optional free-form text field to describe an image. + + distribution: The name of a custom image's distribution. Currently, the valid values are + `Arch Linux`, `CentOS`, `CoreOS`, `Debian`, `Fedora`, `Fedora Atomic`, + `FreeBSD`, `Gentoo`, `openSUSE`, `RancherOS`, `Rocky Linux`, `Ubuntu`, and + `Unknown`. Any other value will be accepted but ignored, and `Unknown` will be + used in its place. + + name: The display name that has been given to an image. This is what is shown in the + control panel and is generally a descriptive title for the image in question. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._put( + f"/v2/images/{image_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}", + body=maybe_transform( + { + "description": description, + "distribution": distribution, + "name": name, + }, + image_update_params.ImageUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImageUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + private: bool | Omit = omit, + tag_name: str | Omit = omit, + type: Literal["application", "distribution"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageListResponse: + """ + To list all of the images available on your account, send a GET request to + /v2/images. + + ## Filtering Results + + --- + + It's possible to request filtered results by including certain query parameters. + + **Image Type** + + Either 1-Click Application or OS Distribution images can be filtered by using + the `type` query parameter. + + > Important: The `type` query parameter does not directly relate to the `type` + > attribute. + + To retrieve only **_distribution_** images, include the `type` query parameter + set to distribution, `/v2/images?type=distribution`. + + To retrieve only **_application_** images, include the `type` query parameter + set to application, `/v2/images?type=application`. + + **User Images** + + To retrieve only the private images of a user, include the `private` query + parameter set to true, `/v2/images?private=true`. + + **Tags** + + To list all images assigned to a specific tag, include the `tag_name` query + parameter set to the name of the tag in your GET request. For example, + `/v2/images?tag_name=$TAG_NAME`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + private: Used to filter only user images. + + tag_name: Used to filter images by a specific tag. + + type: Filters results based on image type which can be either `application` or + `distribution`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/images" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/images", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + "private": private, + "tag_name": tag_name, + "type": type, + }, + image_list_params.ImageListParams, + ), + ), + cast_to=ImageListResponse, + ) + + def delete( + self, + image_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a snapshot or custom image, send a `DELETE` request to + `/v2/images/$IMAGE_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/images/{image_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncImagesResource(AsyncAPIResource): + @cached_property + def actions(self) -> AsyncActionsResource: + return AsyncActionsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncImagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncImagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncImagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncImagesResourceWithStreamingResponse(self) + + async def create( + self, + *, + description: str | Omit = omit, + distribution: Literal[ + "Arch Linux", + "CentOS", + "CoreOS", + "Debian", + "Fedora", + "Fedora Atomic", + "FreeBSD", + "Gentoo", + "openSUSE", + "RancherOS", + "Rocky Linux", + "Ubuntu", + "Unknown", + ] + | Omit = omit, + name: str | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageCreateResponse: + """To create a new custom image, send a POST request to /v2/images. + + The body must + contain a url attribute pointing to a Linux virtual machine image to be imported + into DigitalOcean. The image must be in the raw, qcow2, vhdx, vdi, or vmdk + format. It may be compressed using gzip or bzip2 and must be smaller than 100 GB + after being decompressed. + + Args: + description: An optional free-form text field to describe an image. + + distribution: The name of a custom image's distribution. Currently, the valid values are + `Arch Linux`, `CentOS`, `CoreOS`, `Debian`, `Fedora`, `Fedora Atomic`, + `FreeBSD`, `Gentoo`, `openSUSE`, `RancherOS`, `Rocky Linux`, `Ubuntu`, and + `Unknown`. Any other value will be accepted but ignored, and `Unknown` will be + used in its place. + + name: The display name that has been given to an image. This is what is shown in the + control panel and is generally a descriptive title for the image in question. + + region: The slug identifier for the region where the resource will initially be + available. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + url: A URL from which the custom Linux virtual machine image may be retrieved. The + image it points to must be in the raw, qcow2, vhdx, vdi, or vmdk format. It may + be compressed using gzip or bzip2 and must be smaller than 100 GB after being + decompressed. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/images" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/images", + body=await async_maybe_transform( + { + "description": description, + "distribution": distribution, + "name": name, + "region": region, + "tags": tags, + "url": url, + }, + image_create_params.ImageCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImageCreateResponse, + ) + + async def retrieve( + self, + image_id: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageRetrieveResponse: + """ + To retrieve information about an image, send a `GET` request to + `/v2/images/$IDENTIFIER`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/images/{image_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImageRetrieveResponse, + ) + + async def update( + self, + image_id: int, + *, + description: str | Omit = omit, + distribution: Literal[ + "Arch Linux", + "CentOS", + "CoreOS", + "Debian", + "Fedora", + "Fedora Atomic", + "FreeBSD", + "Gentoo", + "openSUSE", + "RancherOS", + "Rocky Linux", + "Ubuntu", + "Unknown", + ] + | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageUpdateResponse: + """To update an image, send a `PUT` request to `/v2/images/$IMAGE_ID`. + + Set the + `name` attribute to the new value you would like to use. For custom images, the + `description` and `distribution` attributes may also be updated. + + Args: + description: An optional free-form text field to describe an image. + + distribution: The name of a custom image's distribution. Currently, the valid values are + `Arch Linux`, `CentOS`, `CoreOS`, `Debian`, `Fedora`, `Fedora Atomic`, + `FreeBSD`, `Gentoo`, `openSUSE`, `RancherOS`, `Rocky Linux`, `Ubuntu`, and + `Unknown`. Any other value will be accepted but ignored, and `Unknown` will be + used in its place. + + name: The display name that has been given to an image. This is what is shown in the + control panel and is generally a descriptive title for the image in question. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._put( + f"/v2/images/{image_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}", + body=await async_maybe_transform( + { + "description": description, + "distribution": distribution, + "name": name, + }, + image_update_params.ImageUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImageUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + private: bool | Omit = omit, + tag_name: str | Omit = omit, + type: Literal["application", "distribution"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageListResponse: + """ + To list all of the images available on your account, send a GET request to + /v2/images. + + ## Filtering Results + + --- + + It's possible to request filtered results by including certain query parameters. + + **Image Type** + + Either 1-Click Application or OS Distribution images can be filtered by using + the `type` query parameter. + + > Important: The `type` query parameter does not directly relate to the `type` + > attribute. + + To retrieve only **_distribution_** images, include the `type` query parameter + set to distribution, `/v2/images?type=distribution`. + + To retrieve only **_application_** images, include the `type` query parameter + set to application, `/v2/images?type=application`. + + **User Images** + + To retrieve only the private images of a user, include the `private` query + parameter set to true, `/v2/images?private=true`. + + **Tags** + + To list all images assigned to a specific tag, include the `tag_name` query + parameter set to the name of the tag in your GET request. For example, + `/v2/images?tag_name=$TAG_NAME`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + private: Used to filter only user images. + + tag_name: Used to filter images by a specific tag. + + type: Filters results based on image type which can be either `application` or + `distribution`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/images" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/images", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + "private": private, + "tag_name": tag_name, + "type": type, + }, + image_list_params.ImageListParams, + ), + ), + cast_to=ImageListResponse, + ) + + async def delete( + self, + image_id: int, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a snapshot or custom image, send a `DELETE` request to + `/v2/images/$IMAGE_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/images/{image_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/images/{image_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class ImagesResourceWithRawResponse: + def __init__(self, images: ImagesResource) -> None: + self._images = images + + self.create = to_raw_response_wrapper( + images.create, + ) + self.retrieve = to_raw_response_wrapper( + images.retrieve, + ) + self.update = to_raw_response_wrapper( + images.update, + ) + self.list = to_raw_response_wrapper( + images.list, + ) + self.delete = to_raw_response_wrapper( + images.delete, + ) + + @cached_property + def actions(self) -> ActionsResourceWithRawResponse: + return ActionsResourceWithRawResponse(self._images.actions) + + +class AsyncImagesResourceWithRawResponse: + def __init__(self, images: AsyncImagesResource) -> None: + self._images = images + + self.create = async_to_raw_response_wrapper( + images.create, + ) + self.retrieve = async_to_raw_response_wrapper( + images.retrieve, + ) + self.update = async_to_raw_response_wrapper( + images.update, + ) + self.list = async_to_raw_response_wrapper( + images.list, + ) + self.delete = async_to_raw_response_wrapper( + images.delete, + ) + + @cached_property + def actions(self) -> AsyncActionsResourceWithRawResponse: + return AsyncActionsResourceWithRawResponse(self._images.actions) + + +class ImagesResourceWithStreamingResponse: + def __init__(self, images: ImagesResource) -> None: + self._images = images + + self.create = to_streamed_response_wrapper( + images.create, + ) + self.retrieve = to_streamed_response_wrapper( + images.retrieve, + ) + self.update = to_streamed_response_wrapper( + images.update, + ) + self.list = to_streamed_response_wrapper( + images.list, + ) + self.delete = to_streamed_response_wrapper( + images.delete, + ) + + @cached_property + def actions(self) -> ActionsResourceWithStreamingResponse: + return ActionsResourceWithStreamingResponse(self._images.actions) + + +class AsyncImagesResourceWithStreamingResponse: + def __init__(self, images: AsyncImagesResource) -> None: + self._images = images + + self.create = async_to_streamed_response_wrapper( + images.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + images.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + images.update, + ) + self.list = async_to_streamed_response_wrapper( + images.list, + ) + self.delete = async_to_streamed_response_wrapper( + images.delete, + ) + + @cached_property + def actions(self) -> AsyncActionsResourceWithStreamingResponse: + return AsyncActionsResourceWithStreamingResponse(self._images.actions) diff --git a/src/gradient/resources/gpu_droplets/load_balancers/__init__.py b/src/gradient/resources/gpu_droplets/load_balancers/__init__.py new file mode 100644 index 00000000..2cede1c8 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/load_balancers/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .droplets import ( + DropletsResource, + AsyncDropletsResource, + DropletsResourceWithRawResponse, + AsyncDropletsResourceWithRawResponse, + DropletsResourceWithStreamingResponse, + AsyncDropletsResourceWithStreamingResponse, +) +from .load_balancers import ( + LoadBalancersResource, + AsyncLoadBalancersResource, + LoadBalancersResourceWithRawResponse, + AsyncLoadBalancersResourceWithRawResponse, + LoadBalancersResourceWithStreamingResponse, + AsyncLoadBalancersResourceWithStreamingResponse, +) +from .forwarding_rules import ( + ForwardingRulesResource, + AsyncForwardingRulesResource, + ForwardingRulesResourceWithRawResponse, + AsyncForwardingRulesResourceWithRawResponse, + ForwardingRulesResourceWithStreamingResponse, + AsyncForwardingRulesResourceWithStreamingResponse, +) + +__all__ = [ + "DropletsResource", + "AsyncDropletsResource", + "DropletsResourceWithRawResponse", + "AsyncDropletsResourceWithRawResponse", + "DropletsResourceWithStreamingResponse", + "AsyncDropletsResourceWithStreamingResponse", + "ForwardingRulesResource", + "AsyncForwardingRulesResource", + "ForwardingRulesResourceWithRawResponse", + "AsyncForwardingRulesResourceWithRawResponse", + "ForwardingRulesResourceWithStreamingResponse", + "AsyncForwardingRulesResourceWithStreamingResponse", + "LoadBalancersResource", + "AsyncLoadBalancersResource", + "LoadBalancersResourceWithRawResponse", + "AsyncLoadBalancersResourceWithRawResponse", + "LoadBalancersResourceWithStreamingResponse", + "AsyncLoadBalancersResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/gpu_droplets/load_balancers/droplets.py b/src/gradient/resources/gpu_droplets/load_balancers/droplets.py new file mode 100644 index 00000000..ddcdc63a --- /dev/null +++ b/src/gradient/resources/gpu_droplets/load_balancers/droplets.py @@ -0,0 +1,302 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ...._types import Body, Query, Headers, NoneType, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.load_balancers import droplet_add_params, droplet_remove_params + +__all__ = ["DropletsResource", "AsyncDropletsResource"] + + +class DropletsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DropletsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return DropletsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DropletsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return DropletsResourceWithStreamingResponse(self) + + def add( + self, + lb_id: str, + *, + droplet_ids: Iterable[int], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To assign a Droplet to a load balancer instance, send a POST request to + `/v2/load_balancers/$LOAD_BALANCER_ID/droplets`. In the body of the request, + there should be a `droplet_ids` attribute containing a list of Droplet IDs. + Individual Droplets can not be added to a load balancer configured with a + Droplet tag. Attempting to do so will result in a "422 Unprocessable Entity" + response from the API. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + droplet_ids: An array containing the IDs of the Droplets assigned to the load balancer. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/v2/load_balancers/{lb_id}/droplets" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/droplets", + body=maybe_transform({"droplet_ids": droplet_ids}, droplet_add_params.DropletAddParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def remove( + self, + lb_id: str, + *, + droplet_ids: Iterable[int], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove a Droplet from a load balancer instance, send a DELETE request to + `/v2/load_balancers/$LOAD_BALANCER_ID/droplets`. In the body of the request, + there should be a `droplet_ids` attribute containing a list of Droplet IDs. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + droplet_ids: An array containing the IDs of the Droplets assigned to the load balancer. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/load_balancers/{lb_id}/droplets" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/droplets", + body=maybe_transform({"droplet_ids": droplet_ids}, droplet_remove_params.DropletRemoveParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncDropletsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDropletsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncDropletsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDropletsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncDropletsResourceWithStreamingResponse(self) + + async def add( + self, + lb_id: str, + *, + droplet_ids: Iterable[int], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To assign a Droplet to a load balancer instance, send a POST request to + `/v2/load_balancers/$LOAD_BALANCER_ID/droplets`. In the body of the request, + there should be a `droplet_ids` attribute containing a list of Droplet IDs. + Individual Droplets can not be added to a load balancer configured with a + Droplet tag. Attempting to do so will result in a "422 Unprocessable Entity" + response from the API. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + droplet_ids: An array containing the IDs of the Droplets assigned to the load balancer. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/v2/load_balancers/{lb_id}/droplets" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/droplets", + body=await async_maybe_transform({"droplet_ids": droplet_ids}, droplet_add_params.DropletAddParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def remove( + self, + lb_id: str, + *, + droplet_ids: Iterable[int], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove a Droplet from a load balancer instance, send a DELETE request to + `/v2/load_balancers/$LOAD_BALANCER_ID/droplets`. In the body of the request, + there should be a `droplet_ids` attribute containing a list of Droplet IDs. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + droplet_ids: An array containing the IDs of the Droplets assigned to the load balancer. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/load_balancers/{lb_id}/droplets" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/droplets", + body=await async_maybe_transform({"droplet_ids": droplet_ids}, droplet_remove_params.DropletRemoveParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class DropletsResourceWithRawResponse: + def __init__(self, droplets: DropletsResource) -> None: + self._droplets = droplets + + self.add = to_raw_response_wrapper( + droplets.add, + ) + self.remove = to_raw_response_wrapper( + droplets.remove, + ) + + +class AsyncDropletsResourceWithRawResponse: + def __init__(self, droplets: AsyncDropletsResource) -> None: + self._droplets = droplets + + self.add = async_to_raw_response_wrapper( + droplets.add, + ) + self.remove = async_to_raw_response_wrapper( + droplets.remove, + ) + + +class DropletsResourceWithStreamingResponse: + def __init__(self, droplets: DropletsResource) -> None: + self._droplets = droplets + + self.add = to_streamed_response_wrapper( + droplets.add, + ) + self.remove = to_streamed_response_wrapper( + droplets.remove, + ) + + +class AsyncDropletsResourceWithStreamingResponse: + def __init__(self, droplets: AsyncDropletsResource) -> None: + self._droplets = droplets + + self.add = async_to_streamed_response_wrapper( + droplets.add, + ) + self.remove = async_to_streamed_response_wrapper( + droplets.remove, + ) diff --git a/src/gradient/resources/gpu_droplets/load_balancers/forwarding_rules.py b/src/gradient/resources/gpu_droplets/load_balancers/forwarding_rules.py new file mode 100644 index 00000000..8f9092e0 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/load_balancers/forwarding_rules.py @@ -0,0 +1,301 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ...._types import Body, Query, Headers, NoneType, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.load_balancers import forwarding_rule_add_params, forwarding_rule_remove_params +from ....types.gpu_droplets.forwarding_rule_param import ForwardingRuleParam + +__all__ = ["ForwardingRulesResource", "AsyncForwardingRulesResource"] + + +class ForwardingRulesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ForwardingRulesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ForwardingRulesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ForwardingRulesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ForwardingRulesResourceWithStreamingResponse(self) + + def add( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To add an additional forwarding rule to a load balancer instance, send a POST + request to `/v2/load_balancers/$LOAD_BALANCER_ID/forwarding_rules`. In the body + of the request, there should be a `forwarding_rules` attribute containing an + array of rules to be added. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/v2/load_balancers/{lb_id}/forwarding_rules" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/forwarding_rules", + body=maybe_transform( + {"forwarding_rules": forwarding_rules}, forwarding_rule_add_params.ForwardingRuleAddParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def remove( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove forwarding rules from a load balancer instance, send a DELETE request + to `/v2/load_balancers/$LOAD_BALANCER_ID/forwarding_rules`. In the body of the + request, there should be a `forwarding_rules` attribute containing an array of + rules to be removed. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/load_balancers/{lb_id}/forwarding_rules" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/forwarding_rules", + body=maybe_transform( + {"forwarding_rules": forwarding_rules}, forwarding_rule_remove_params.ForwardingRuleRemoveParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncForwardingRulesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncForwardingRulesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncForwardingRulesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncForwardingRulesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncForwardingRulesResourceWithStreamingResponse(self) + + async def add( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To add an additional forwarding rule to a load balancer instance, send a POST + request to `/v2/load_balancers/$LOAD_BALANCER_ID/forwarding_rules`. In the body + of the request, there should be a `forwarding_rules` attribute containing an + array of rules to be added. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/v2/load_balancers/{lb_id}/forwarding_rules" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/forwarding_rules", + body=await async_maybe_transform( + {"forwarding_rules": forwarding_rules}, forwarding_rule_add_params.ForwardingRuleAddParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def remove( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To remove forwarding rules from a load balancer instance, send a DELETE request + to `/v2/load_balancers/$LOAD_BALANCER_ID/forwarding_rules`. In the body of the + request, there should be a `forwarding_rules` attribute containing an array of + rules to be removed. + + No response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/load_balancers/{lb_id}/forwarding_rules" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/forwarding_rules", + body=await async_maybe_transform( + {"forwarding_rules": forwarding_rules}, forwarding_rule_remove_params.ForwardingRuleRemoveParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class ForwardingRulesResourceWithRawResponse: + def __init__(self, forwarding_rules: ForwardingRulesResource) -> None: + self._forwarding_rules = forwarding_rules + + self.add = to_raw_response_wrapper( + forwarding_rules.add, + ) + self.remove = to_raw_response_wrapper( + forwarding_rules.remove, + ) + + +class AsyncForwardingRulesResourceWithRawResponse: + def __init__(self, forwarding_rules: AsyncForwardingRulesResource) -> None: + self._forwarding_rules = forwarding_rules + + self.add = async_to_raw_response_wrapper( + forwarding_rules.add, + ) + self.remove = async_to_raw_response_wrapper( + forwarding_rules.remove, + ) + + +class ForwardingRulesResourceWithStreamingResponse: + def __init__(self, forwarding_rules: ForwardingRulesResource) -> None: + self._forwarding_rules = forwarding_rules + + self.add = to_streamed_response_wrapper( + forwarding_rules.add, + ) + self.remove = to_streamed_response_wrapper( + forwarding_rules.remove, + ) + + +class AsyncForwardingRulesResourceWithStreamingResponse: + def __init__(self, forwarding_rules: AsyncForwardingRulesResource) -> None: + self._forwarding_rules = forwarding_rules + + self.add = async_to_streamed_response_wrapper( + forwarding_rules.add, + ) + self.remove = async_to_streamed_response_wrapper( + forwarding_rules.remove, + ) diff --git a/src/gradient/resources/gpu_droplets/load_balancers/load_balancers.py b/src/gradient/resources/gpu_droplets/load_balancers/load_balancers.py new file mode 100644 index 00000000..2a1e52d9 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/load_balancers/load_balancers.py @@ -0,0 +1,2205 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, overload + +import httpx + +from .droplets import ( + DropletsResource, + AsyncDropletsResource, + DropletsResourceWithRawResponse, + AsyncDropletsResourceWithRawResponse, + DropletsResourceWithStreamingResponse, + AsyncDropletsResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from .forwarding_rules import ( + ForwardingRulesResource, + AsyncForwardingRulesResource, + ForwardingRulesResourceWithRawResponse, + AsyncForwardingRulesResourceWithRawResponse, + ForwardingRulesResourceWithStreamingResponse, + AsyncForwardingRulesResourceWithStreamingResponse, +) +from ....types.gpu_droplets import ( + load_balancer_list_params, + load_balancer_create_params, + load_balancer_update_params, +) +from ....types.gpu_droplets.domains_param import DomainsParam +from ....types.gpu_droplets.lb_firewall_param import LbFirewallParam +from ....types.gpu_droplets.glb_settings_param import GlbSettingsParam +from ....types.gpu_droplets.health_check_param import HealthCheckParam +from ....types.gpu_droplets.forwarding_rule_param import ForwardingRuleParam +from ....types.gpu_droplets.sticky_sessions_param import StickySessionsParam +from ....types.gpu_droplets.load_balancer_list_response import LoadBalancerListResponse +from ....types.gpu_droplets.load_balancer_create_response import LoadBalancerCreateResponse +from ....types.gpu_droplets.load_balancer_update_response import LoadBalancerUpdateResponse +from ....types.gpu_droplets.load_balancer_retrieve_response import LoadBalancerRetrieveResponse + +__all__ = ["LoadBalancersResource", "AsyncLoadBalancersResource"] + + +class LoadBalancersResource(SyncAPIResource): + @cached_property + def droplets(self) -> DropletsResource: + return DropletsResource(self._client) + + @cached_property + def forwarding_rules(self) -> ForwardingRulesResource: + return ForwardingRulesResource(self._client) + + @cached_property + def with_raw_response(self) -> LoadBalancersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return LoadBalancersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LoadBalancersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return LoadBalancersResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + droplet_ids: Iterable[int] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerCreateResponse: + """ + To create a new load balancer instance, send a POST request to + `/v2/load_balancers`. + + You can specify the Droplets that will sit behind the load balancer using one of + two methods: + + - Set `droplet_ids` to a list of specific Droplet IDs. + - Set `tag` to the name of a tag. All Droplets with this tag applied will be + assigned to the load balancer. Additional Droplets will be automatically + assigned as they are tagged. + + These methods are mutually exclusive. + + Args: + forwarding_rules: An array of objects specifying the forwarding rules for a load balancer. + + algorithm: This field has been deprecated. You can no longer specify an algorithm for load + balancers. + + disable_lets_encrypt_dns_records: A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + + domains: An array of objects specifying the domain configurations for a Global load + balancer. + + droplet_ids: An array containing the IDs of the Droplets assigned to the load balancer. + + enable_backend_keepalive: A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + + enable_proxy_protocol: A boolean value indicating whether PROXY Protocol is in use. + + firewall: An object specifying allow and deny rules to control traffic to the load + balancer. + + glb_settings: An object specifying forwarding configurations for a Global load balancer. + + health_check: An object specifying health check settings for the load balancer. + + http_idle_timeout_seconds: An integer value which configures the idle timeout for HTTP requests to the + target droplets. + + name: A human-readable name for a load balancer instance. + + network: A string indicating whether the load balancer should be external or internal. + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + + network_stack: A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + + project_id: The ID of the project that the load balancer is associated with. If no ID is + provided at creation, the load balancer associates with the user's default + project. If an invalid project ID is provided, the load balancer will not be + created. + + redirect_http_to_https: A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + + region: The slug identifier for the region where the resource will initially be + available. + + size: This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + + size_unit: How many nodes the load balancer contains. Each additional node increases the + load balancer's ability to manage more connections. Load balancers can be scaled + up or down, and you can change the number of nodes after creation up to once per + hour. This field is currently not available in the AMS2, NYC2, or SFO1 regions. + Use the `size` field to scale load balancers that reside in these regions. + + sticky_sessions: An object specifying sticky sessions settings for the load balancer. + + target_load_balancer_ids: An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + + tls_cipher_policy: A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + + type: A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + + vpc_uuid: A string specifying the UUID of the VPC to which the load balancer is assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + tag: str | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerCreateResponse: + """ + To create a new load balancer instance, send a POST request to + `/v2/load_balancers`. + + You can specify the Droplets that will sit behind the load balancer using one of + two methods: + + - Set `droplet_ids` to a list of specific Droplet IDs. + - Set `tag` to the name of a tag. All Droplets with this tag applied will be + assigned to the load balancer. Additional Droplets will be automatically + assigned as they are tagged. + + These methods are mutually exclusive. + + Args: + forwarding_rules: An array of objects specifying the forwarding rules for a load balancer. + + algorithm: This field has been deprecated. You can no longer specify an algorithm for load + balancers. + + disable_lets_encrypt_dns_records: A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + + domains: An array of objects specifying the domain configurations for a Global load + balancer. + + enable_backend_keepalive: A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + + enable_proxy_protocol: A boolean value indicating whether PROXY Protocol is in use. + + firewall: An object specifying allow and deny rules to control traffic to the load + balancer. + + glb_settings: An object specifying forwarding configurations for a Global load balancer. + + health_check: An object specifying health check settings for the load balancer. + + http_idle_timeout_seconds: An integer value which configures the idle timeout for HTTP requests to the + target droplets. + + name: A human-readable name for a load balancer instance. + + network: A string indicating whether the load balancer should be external or internal. + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + + network_stack: A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + + project_id: The ID of the project that the load balancer is associated with. If no ID is + provided at creation, the load balancer associates with the user's default + project. If an invalid project ID is provided, the load balancer will not be + created. + + redirect_http_to_https: A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + + region: The slug identifier for the region where the resource will initially be + available. + + size: This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + + size_unit: How many nodes the load balancer contains. Each additional node increases the + load balancer's ability to manage more connections. Load balancers can be scaled + up or down, and you can change the number of nodes after creation up to once per + hour. This field is currently not available in the AMS2, NYC2, or SFO1 regions. + Use the `size` field to scale load balancers that reside in these regions. + + sticky_sessions: An object specifying sticky sessions settings for the load balancer. + + tag: The name of a Droplet tag corresponding to Droplets assigned to the load + balancer. + + target_load_balancer_ids: An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + + tls_cipher_policy: A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + + type: A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + + vpc_uuid: A string specifying the UUID of the VPC to which the load balancer is assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["forwarding_rules"]) + def create( + self, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + droplet_ids: Iterable[int] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + tag: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerCreateResponse: + return self._post( + "/v2/load_balancers" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/load_balancers", + body=maybe_transform( + { + "forwarding_rules": forwarding_rules, + "algorithm": algorithm, + "disable_lets_encrypt_dns_records": disable_lets_encrypt_dns_records, + "domains": domains, + "droplet_ids": droplet_ids, + "enable_backend_keepalive": enable_backend_keepalive, + "enable_proxy_protocol": enable_proxy_protocol, + "firewall": firewall, + "glb_settings": glb_settings, + "health_check": health_check, + "http_idle_timeout_seconds": http_idle_timeout_seconds, + "name": name, + "network": network, + "network_stack": network_stack, + "project_id": project_id, + "redirect_http_to_https": redirect_http_to_https, + "region": region, + "size": size, + "size_unit": size_unit, + "sticky_sessions": sticky_sessions, + "target_load_balancer_ids": target_load_balancer_ids, + "tls_cipher_policy": tls_cipher_policy, + "type": type, + "vpc_uuid": vpc_uuid, + "tag": tag, + }, + load_balancer_create_params.LoadBalancerCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoadBalancerCreateResponse, + ) + + def retrieve( + self, + lb_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerRetrieveResponse: + """ + To show information about a load balancer instance, send a GET request to + `/v2/load_balancers/$LOAD_BALANCER_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + return self._get( + f"/v2/load_balancers/{lb_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoadBalancerRetrieveResponse, + ) + + @overload + def update( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + droplet_ids: Iterable[int] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerUpdateResponse: + """ + To update a load balancer's settings, send a PUT request to + `/v2/load_balancers/$LOAD_BALANCER_ID`. The request should contain a full + representation of the load balancer including existing attributes. It may + contain _one of_ the `droplets_ids` or `tag` attributes as they are mutually + exclusive. **Note that any attribute that is not provided will be reset to its + default value.** + + Args: + forwarding_rules: An array of objects specifying the forwarding rules for a load balancer. + + algorithm: This field has been deprecated. You can no longer specify an algorithm for load + balancers. + + disable_lets_encrypt_dns_records: A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + + domains: An array of objects specifying the domain configurations for a Global load + balancer. + + droplet_ids: An array containing the IDs of the Droplets assigned to the load balancer. + + enable_backend_keepalive: A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + + enable_proxy_protocol: A boolean value indicating whether PROXY Protocol is in use. + + firewall: An object specifying allow and deny rules to control traffic to the load + balancer. + + glb_settings: An object specifying forwarding configurations for a Global load balancer. + + health_check: An object specifying health check settings for the load balancer. + + http_idle_timeout_seconds: An integer value which configures the idle timeout for HTTP requests to the + target droplets. + + name: A human-readable name for a load balancer instance. + + network: A string indicating whether the load balancer should be external or internal. + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + + network_stack: A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + + project_id: The ID of the project that the load balancer is associated with. If no ID is + provided at creation, the load balancer associates with the user's default + project. If an invalid project ID is provided, the load balancer will not be + created. + + redirect_http_to_https: A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + + region: The slug identifier for the region where the resource will initially be + available. + + size: This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + + size_unit: How many nodes the load balancer contains. Each additional node increases the + load balancer's ability to manage more connections. Load balancers can be scaled + up or down, and you can change the number of nodes after creation up to once per + hour. This field is currently not available in the AMS2, NYC2, or SFO1 regions. + Use the `size` field to scale load balancers that reside in these regions. + + sticky_sessions: An object specifying sticky sessions settings for the load balancer. + + target_load_balancer_ids: An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + + tls_cipher_policy: A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + + type: A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + + vpc_uuid: A string specifying the UUID of the VPC to which the load balancer is assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def update( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + tag: str | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerUpdateResponse: + """ + To update a load balancer's settings, send a PUT request to + `/v2/load_balancers/$LOAD_BALANCER_ID`. The request should contain a full + representation of the load balancer including existing attributes. It may + contain _one of_ the `droplets_ids` or `tag` attributes as they are mutually + exclusive. **Note that any attribute that is not provided will be reset to its + default value.** + + Args: + forwarding_rules: An array of objects specifying the forwarding rules for a load balancer. + + algorithm: This field has been deprecated. You can no longer specify an algorithm for load + balancers. + + disable_lets_encrypt_dns_records: A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + + domains: An array of objects specifying the domain configurations for a Global load + balancer. + + enable_backend_keepalive: A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + + enable_proxy_protocol: A boolean value indicating whether PROXY Protocol is in use. + + firewall: An object specifying allow and deny rules to control traffic to the load + balancer. + + glb_settings: An object specifying forwarding configurations for a Global load balancer. + + health_check: An object specifying health check settings for the load balancer. + + http_idle_timeout_seconds: An integer value which configures the idle timeout for HTTP requests to the + target droplets. + + name: A human-readable name for a load balancer instance. + + network: A string indicating whether the load balancer should be external or internal. + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + + network_stack: A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + + project_id: The ID of the project that the load balancer is associated with. If no ID is + provided at creation, the load balancer associates with the user's default + project. If an invalid project ID is provided, the load balancer will not be + created. + + redirect_http_to_https: A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + + region: The slug identifier for the region where the resource will initially be + available. + + size: This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + + size_unit: How many nodes the load balancer contains. Each additional node increases the + load balancer's ability to manage more connections. Load balancers can be scaled + up or down, and you can change the number of nodes after creation up to once per + hour. This field is currently not available in the AMS2, NYC2, or SFO1 regions. + Use the `size` field to scale load balancers that reside in these regions. + + sticky_sessions: An object specifying sticky sessions settings for the load balancer. + + tag: The name of a Droplet tag corresponding to Droplets assigned to the load + balancer. + + target_load_balancer_ids: An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + + tls_cipher_policy: A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + + type: A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + + vpc_uuid: A string specifying the UUID of the VPC to which the load balancer is assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["forwarding_rules"]) + def update( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + droplet_ids: Iterable[int] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + tag: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerUpdateResponse: + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + return self._put( + f"/v2/load_balancers/{lb_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}", + body=maybe_transform( + { + "forwarding_rules": forwarding_rules, + "algorithm": algorithm, + "disable_lets_encrypt_dns_records": disable_lets_encrypt_dns_records, + "domains": domains, + "droplet_ids": droplet_ids, + "enable_backend_keepalive": enable_backend_keepalive, + "enable_proxy_protocol": enable_proxy_protocol, + "firewall": firewall, + "glb_settings": glb_settings, + "health_check": health_check, + "http_idle_timeout_seconds": http_idle_timeout_seconds, + "name": name, + "network": network, + "network_stack": network_stack, + "project_id": project_id, + "redirect_http_to_https": redirect_http_to_https, + "region": region, + "size": size, + "size_unit": size_unit, + "sticky_sessions": sticky_sessions, + "target_load_balancer_ids": target_load_balancer_ids, + "tls_cipher_policy": tls_cipher_policy, + "type": type, + "vpc_uuid": vpc_uuid, + "tag": tag, + }, + load_balancer_update_params.LoadBalancerUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoadBalancerUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerListResponse: + """ + To list all of the load balancer instances on your account, send a GET request + to `/v2/load_balancers`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/load_balancers" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/load_balancers", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + load_balancer_list_params.LoadBalancerListParams, + ), + ), + cast_to=LoadBalancerListResponse, + ) + + def delete( + self, + lb_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a load balancer instance, disassociating any Droplets assigned to it + and removing it from your account, send a DELETE request to + `/v2/load_balancers/$LOAD_BALANCER_ID`. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/load_balancers/{lb_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def delete_cache( + self, + lb_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a Global load balancer CDN cache, send a DELETE request to + `/v2/load_balancers/$LOAD_BALANCER_ID/cache`. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/load_balancers/{lb_id}/cache" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/cache", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncLoadBalancersResource(AsyncAPIResource): + @cached_property + def droplets(self) -> AsyncDropletsResource: + return AsyncDropletsResource(self._client) + + @cached_property + def forwarding_rules(self) -> AsyncForwardingRulesResource: + return AsyncForwardingRulesResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncLoadBalancersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncLoadBalancersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLoadBalancersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncLoadBalancersResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + droplet_ids: Iterable[int] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerCreateResponse: + """ + To create a new load balancer instance, send a POST request to + `/v2/load_balancers`. + + You can specify the Droplets that will sit behind the load balancer using one of + two methods: + + - Set `droplet_ids` to a list of specific Droplet IDs. + - Set `tag` to the name of a tag. All Droplets with this tag applied will be + assigned to the load balancer. Additional Droplets will be automatically + assigned as they are tagged. + + These methods are mutually exclusive. + + Args: + forwarding_rules: An array of objects specifying the forwarding rules for a load balancer. + + algorithm: This field has been deprecated. You can no longer specify an algorithm for load + balancers. + + disable_lets_encrypt_dns_records: A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + + domains: An array of objects specifying the domain configurations for a Global load + balancer. + + droplet_ids: An array containing the IDs of the Droplets assigned to the load balancer. + + enable_backend_keepalive: A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + + enable_proxy_protocol: A boolean value indicating whether PROXY Protocol is in use. + + firewall: An object specifying allow and deny rules to control traffic to the load + balancer. + + glb_settings: An object specifying forwarding configurations for a Global load balancer. + + health_check: An object specifying health check settings for the load balancer. + + http_idle_timeout_seconds: An integer value which configures the idle timeout for HTTP requests to the + target droplets. + + name: A human-readable name for a load balancer instance. + + network: A string indicating whether the load balancer should be external or internal. + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + + network_stack: A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + + project_id: The ID of the project that the load balancer is associated with. If no ID is + provided at creation, the load balancer associates with the user's default + project. If an invalid project ID is provided, the load balancer will not be + created. + + redirect_http_to_https: A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + + region: The slug identifier for the region where the resource will initially be + available. + + size: This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + + size_unit: How many nodes the load balancer contains. Each additional node increases the + load balancer's ability to manage more connections. Load balancers can be scaled + up or down, and you can change the number of nodes after creation up to once per + hour. This field is currently not available in the AMS2, NYC2, or SFO1 regions. + Use the `size` field to scale load balancers that reside in these regions. + + sticky_sessions: An object specifying sticky sessions settings for the load balancer. + + target_load_balancer_ids: An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + + tls_cipher_policy: A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + + type: A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + + vpc_uuid: A string specifying the UUID of the VPC to which the load balancer is assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + tag: str | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerCreateResponse: + """ + To create a new load balancer instance, send a POST request to + `/v2/load_balancers`. + + You can specify the Droplets that will sit behind the load balancer using one of + two methods: + + - Set `droplet_ids` to a list of specific Droplet IDs. + - Set `tag` to the name of a tag. All Droplets with this tag applied will be + assigned to the load balancer. Additional Droplets will be automatically + assigned as they are tagged. + + These methods are mutually exclusive. + + Args: + forwarding_rules: An array of objects specifying the forwarding rules for a load balancer. + + algorithm: This field has been deprecated. You can no longer specify an algorithm for load + balancers. + + disable_lets_encrypt_dns_records: A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + + domains: An array of objects specifying the domain configurations for a Global load + balancer. + + enable_backend_keepalive: A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + + enable_proxy_protocol: A boolean value indicating whether PROXY Protocol is in use. + + firewall: An object specifying allow and deny rules to control traffic to the load + balancer. + + glb_settings: An object specifying forwarding configurations for a Global load balancer. + + health_check: An object specifying health check settings for the load balancer. + + http_idle_timeout_seconds: An integer value which configures the idle timeout for HTTP requests to the + target droplets. + + name: A human-readable name for a load balancer instance. + + network: A string indicating whether the load balancer should be external or internal. + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + + network_stack: A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + + project_id: The ID of the project that the load balancer is associated with. If no ID is + provided at creation, the load balancer associates with the user's default + project. If an invalid project ID is provided, the load balancer will not be + created. + + redirect_http_to_https: A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + + region: The slug identifier for the region where the resource will initially be + available. + + size: This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + + size_unit: How many nodes the load balancer contains. Each additional node increases the + load balancer's ability to manage more connections. Load balancers can be scaled + up or down, and you can change the number of nodes after creation up to once per + hour. This field is currently not available in the AMS2, NYC2, or SFO1 regions. + Use the `size` field to scale load balancers that reside in these regions. + + sticky_sessions: An object specifying sticky sessions settings for the load balancer. + + tag: The name of a Droplet tag corresponding to Droplets assigned to the load + balancer. + + target_load_balancer_ids: An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + + tls_cipher_policy: A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + + type: A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + + vpc_uuid: A string specifying the UUID of the VPC to which the load balancer is assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["forwarding_rules"]) + async def create( + self, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + droplet_ids: Iterable[int] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + tag: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerCreateResponse: + return await self._post( + "/v2/load_balancers" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/load_balancers", + body=await async_maybe_transform( + { + "forwarding_rules": forwarding_rules, + "algorithm": algorithm, + "disable_lets_encrypt_dns_records": disable_lets_encrypt_dns_records, + "domains": domains, + "droplet_ids": droplet_ids, + "enable_backend_keepalive": enable_backend_keepalive, + "enable_proxy_protocol": enable_proxy_protocol, + "firewall": firewall, + "glb_settings": glb_settings, + "health_check": health_check, + "http_idle_timeout_seconds": http_idle_timeout_seconds, + "name": name, + "network": network, + "network_stack": network_stack, + "project_id": project_id, + "redirect_http_to_https": redirect_http_to_https, + "region": region, + "size": size, + "size_unit": size_unit, + "sticky_sessions": sticky_sessions, + "target_load_balancer_ids": target_load_balancer_ids, + "tls_cipher_policy": tls_cipher_policy, + "type": type, + "vpc_uuid": vpc_uuid, + "tag": tag, + }, + load_balancer_create_params.LoadBalancerCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoadBalancerCreateResponse, + ) + + async def retrieve( + self, + lb_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerRetrieveResponse: + """ + To show information about a load balancer instance, send a GET request to + `/v2/load_balancers/$LOAD_BALANCER_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + return await self._get( + f"/v2/load_balancers/{lb_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoadBalancerRetrieveResponse, + ) + + @overload + async def update( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + droplet_ids: Iterable[int] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerUpdateResponse: + """ + To update a load balancer's settings, send a PUT request to + `/v2/load_balancers/$LOAD_BALANCER_ID`. The request should contain a full + representation of the load balancer including existing attributes. It may + contain _one of_ the `droplets_ids` or `tag` attributes as they are mutually + exclusive. **Note that any attribute that is not provided will be reset to its + default value.** + + Args: + forwarding_rules: An array of objects specifying the forwarding rules for a load balancer. + + algorithm: This field has been deprecated. You can no longer specify an algorithm for load + balancers. + + disable_lets_encrypt_dns_records: A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + + domains: An array of objects specifying the domain configurations for a Global load + balancer. + + droplet_ids: An array containing the IDs of the Droplets assigned to the load balancer. + + enable_backend_keepalive: A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + + enable_proxy_protocol: A boolean value indicating whether PROXY Protocol is in use. + + firewall: An object specifying allow and deny rules to control traffic to the load + balancer. + + glb_settings: An object specifying forwarding configurations for a Global load balancer. + + health_check: An object specifying health check settings for the load balancer. + + http_idle_timeout_seconds: An integer value which configures the idle timeout for HTTP requests to the + target droplets. + + name: A human-readable name for a load balancer instance. + + network: A string indicating whether the load balancer should be external or internal. + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + + network_stack: A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + + project_id: The ID of the project that the load balancer is associated with. If no ID is + provided at creation, the load balancer associates with the user's default + project. If an invalid project ID is provided, the load balancer will not be + created. + + redirect_http_to_https: A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + + region: The slug identifier for the region where the resource will initially be + available. + + size: This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + + size_unit: How many nodes the load balancer contains. Each additional node increases the + load balancer's ability to manage more connections. Load balancers can be scaled + up or down, and you can change the number of nodes after creation up to once per + hour. This field is currently not available in the AMS2, NYC2, or SFO1 regions. + Use the `size` field to scale load balancers that reside in these regions. + + sticky_sessions: An object specifying sticky sessions settings for the load balancer. + + target_load_balancer_ids: An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + + tls_cipher_policy: A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + + type: A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + + vpc_uuid: A string specifying the UUID of the VPC to which the load balancer is assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def update( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + tag: str | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerUpdateResponse: + """ + To update a load balancer's settings, send a PUT request to + `/v2/load_balancers/$LOAD_BALANCER_ID`. The request should contain a full + representation of the load balancer including existing attributes. It may + contain _one of_ the `droplets_ids` or `tag` attributes as they are mutually + exclusive. **Note that any attribute that is not provided will be reset to its + default value.** + + Args: + forwarding_rules: An array of objects specifying the forwarding rules for a load balancer. + + algorithm: This field has been deprecated. You can no longer specify an algorithm for load + balancers. + + disable_lets_encrypt_dns_records: A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + + domains: An array of objects specifying the domain configurations for a Global load + balancer. + + enable_backend_keepalive: A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + + enable_proxy_protocol: A boolean value indicating whether PROXY Protocol is in use. + + firewall: An object specifying allow and deny rules to control traffic to the load + balancer. + + glb_settings: An object specifying forwarding configurations for a Global load balancer. + + health_check: An object specifying health check settings for the load balancer. + + http_idle_timeout_seconds: An integer value which configures the idle timeout for HTTP requests to the + target droplets. + + name: A human-readable name for a load balancer instance. + + network: A string indicating whether the load balancer should be external or internal. + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + + network_stack: A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + + project_id: The ID of the project that the load balancer is associated with. If no ID is + provided at creation, the load balancer associates with the user's default + project. If an invalid project ID is provided, the load balancer will not be + created. + + redirect_http_to_https: A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + + region: The slug identifier for the region where the resource will initially be + available. + + size: This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + + size_unit: How many nodes the load balancer contains. Each additional node increases the + load balancer's ability to manage more connections. Load balancers can be scaled + up or down, and you can change the number of nodes after creation up to once per + hour. This field is currently not available in the AMS2, NYC2, or SFO1 regions. + Use the `size` field to scale load balancers that reside in these regions. + + sticky_sessions: An object specifying sticky sessions settings for the load balancer. + + tag: The name of a Droplet tag corresponding to Droplets assigned to the load + balancer. + + target_load_balancer_ids: An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + + tls_cipher_policy: A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + + type: A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + + vpc_uuid: A string specifying the UUID of the VPC to which the load balancer is assigned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["forwarding_rules"]) + async def update( + self, + lb_id: str, + *, + forwarding_rules: Iterable[ForwardingRuleParam], + algorithm: Literal["round_robin", "least_connections"] | Omit = omit, + disable_lets_encrypt_dns_records: bool | Omit = omit, + domains: Iterable[DomainsParam] | Omit = omit, + droplet_ids: Iterable[int] | Omit = omit, + enable_backend_keepalive: bool | Omit = omit, + enable_proxy_protocol: bool | Omit = omit, + firewall: LbFirewallParam | Omit = omit, + glb_settings: GlbSettingsParam | Omit = omit, + health_check: HealthCheckParam | Omit = omit, + http_idle_timeout_seconds: int | Omit = omit, + name: str | Omit = omit, + network: Literal["EXTERNAL", "INTERNAL"] | Omit = omit, + network_stack: Literal["IPV4", "DUALSTACK"] | Omit = omit, + project_id: str | Omit = omit, + redirect_http_to_https: bool | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + size: Literal["lb-small", "lb-medium", "lb-large"] | Omit = omit, + size_unit: int | Omit = omit, + sticky_sessions: StickySessionsParam | Omit = omit, + target_load_balancer_ids: SequenceNotStr[str] | Omit = omit, + tls_cipher_policy: Literal["DEFAULT", "STRONG"] | Omit = omit, + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] | Omit = omit, + vpc_uuid: str | Omit = omit, + tag: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerUpdateResponse: + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + return await self._put( + f"/v2/load_balancers/{lb_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}", + body=await async_maybe_transform( + { + "forwarding_rules": forwarding_rules, + "algorithm": algorithm, + "disable_lets_encrypt_dns_records": disable_lets_encrypt_dns_records, + "domains": domains, + "droplet_ids": droplet_ids, + "enable_backend_keepalive": enable_backend_keepalive, + "enable_proxy_protocol": enable_proxy_protocol, + "firewall": firewall, + "glb_settings": glb_settings, + "health_check": health_check, + "http_idle_timeout_seconds": http_idle_timeout_seconds, + "name": name, + "network": network, + "network_stack": network_stack, + "project_id": project_id, + "redirect_http_to_https": redirect_http_to_https, + "region": region, + "size": size, + "size_unit": size_unit, + "sticky_sessions": sticky_sessions, + "target_load_balancer_ids": target_load_balancer_ids, + "tls_cipher_policy": tls_cipher_policy, + "type": type, + "vpc_uuid": vpc_uuid, + "tag": tag, + }, + load_balancer_update_params.LoadBalancerUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoadBalancerUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoadBalancerListResponse: + """ + To list all of the load balancer instances on your account, send a GET request + to `/v2/load_balancers`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/load_balancers" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/load_balancers", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + load_balancer_list_params.LoadBalancerListParams, + ), + ), + cast_to=LoadBalancerListResponse, + ) + + async def delete( + self, + lb_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a load balancer instance, disassociating any Droplets assigned to it + and removing it from your account, send a DELETE request to + `/v2/load_balancers/$LOAD_BALANCER_ID`. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/load_balancers/{lb_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def delete_cache( + self, + lb_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a Global load balancer CDN cache, send a DELETE request to + `/v2/load_balancers/$LOAD_BALANCER_ID/cache`. + + A successful request will receive a 204 status code with no body in response. + This indicates that the request was processed successfully. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not lb_id: + raise ValueError(f"Expected a non-empty value for `lb_id` but received {lb_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/load_balancers/{lb_id}/cache" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/load_balancers/{lb_id}/cache", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class LoadBalancersResourceWithRawResponse: + def __init__(self, load_balancers: LoadBalancersResource) -> None: + self._load_balancers = load_balancers + + self.create = to_raw_response_wrapper( + load_balancers.create, + ) + self.retrieve = to_raw_response_wrapper( + load_balancers.retrieve, + ) + self.update = to_raw_response_wrapper( + load_balancers.update, + ) + self.list = to_raw_response_wrapper( + load_balancers.list, + ) + self.delete = to_raw_response_wrapper( + load_balancers.delete, + ) + self.delete_cache = to_raw_response_wrapper( + load_balancers.delete_cache, + ) + + @cached_property + def droplets(self) -> DropletsResourceWithRawResponse: + return DropletsResourceWithRawResponse(self._load_balancers.droplets) + + @cached_property + def forwarding_rules(self) -> ForwardingRulesResourceWithRawResponse: + return ForwardingRulesResourceWithRawResponse(self._load_balancers.forwarding_rules) + + +class AsyncLoadBalancersResourceWithRawResponse: + def __init__(self, load_balancers: AsyncLoadBalancersResource) -> None: + self._load_balancers = load_balancers + + self.create = async_to_raw_response_wrapper( + load_balancers.create, + ) + self.retrieve = async_to_raw_response_wrapper( + load_balancers.retrieve, + ) + self.update = async_to_raw_response_wrapper( + load_balancers.update, + ) + self.list = async_to_raw_response_wrapper( + load_balancers.list, + ) + self.delete = async_to_raw_response_wrapper( + load_balancers.delete, + ) + self.delete_cache = async_to_raw_response_wrapper( + load_balancers.delete_cache, + ) + + @cached_property + def droplets(self) -> AsyncDropletsResourceWithRawResponse: + return AsyncDropletsResourceWithRawResponse(self._load_balancers.droplets) + + @cached_property + def forwarding_rules(self) -> AsyncForwardingRulesResourceWithRawResponse: + return AsyncForwardingRulesResourceWithRawResponse(self._load_balancers.forwarding_rules) + + +class LoadBalancersResourceWithStreamingResponse: + def __init__(self, load_balancers: LoadBalancersResource) -> None: + self._load_balancers = load_balancers + + self.create = to_streamed_response_wrapper( + load_balancers.create, + ) + self.retrieve = to_streamed_response_wrapper( + load_balancers.retrieve, + ) + self.update = to_streamed_response_wrapper( + load_balancers.update, + ) + self.list = to_streamed_response_wrapper( + load_balancers.list, + ) + self.delete = to_streamed_response_wrapper( + load_balancers.delete, + ) + self.delete_cache = to_streamed_response_wrapper( + load_balancers.delete_cache, + ) + + @cached_property + def droplets(self) -> DropletsResourceWithStreamingResponse: + return DropletsResourceWithStreamingResponse(self._load_balancers.droplets) + + @cached_property + def forwarding_rules(self) -> ForwardingRulesResourceWithStreamingResponse: + return ForwardingRulesResourceWithStreamingResponse(self._load_balancers.forwarding_rules) + + +class AsyncLoadBalancersResourceWithStreamingResponse: + def __init__(self, load_balancers: AsyncLoadBalancersResource) -> None: + self._load_balancers = load_balancers + + self.create = async_to_streamed_response_wrapper( + load_balancers.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + load_balancers.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + load_balancers.update, + ) + self.list = async_to_streamed_response_wrapper( + load_balancers.list, + ) + self.delete = async_to_streamed_response_wrapper( + load_balancers.delete, + ) + self.delete_cache = async_to_streamed_response_wrapper( + load_balancers.delete_cache, + ) + + @cached_property + def droplets(self) -> AsyncDropletsResourceWithStreamingResponse: + return AsyncDropletsResourceWithStreamingResponse(self._load_balancers.droplets) + + @cached_property + def forwarding_rules(self) -> AsyncForwardingRulesResourceWithStreamingResponse: + return AsyncForwardingRulesResourceWithStreamingResponse(self._load_balancers.forwarding_rules) diff --git a/src/gradient/resources/gpu_droplets/sizes.py b/src/gradient/resources/gpu_droplets/sizes.py new file mode 100644 index 00000000..9893903f --- /dev/null +++ b/src/gradient/resources/gpu_droplets/sizes.py @@ -0,0 +1,199 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.gpu_droplets import size_list_params +from ...types.gpu_droplets.size_list_response import SizeListResponse + +__all__ = ["SizesResource", "AsyncSizesResource"] + + +class SizesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SizesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return SizesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SizesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return SizesResourceWithStreamingResponse(self) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SizeListResponse: + """To list all of available Droplet sizes, send a GET request to `/v2/sizes`. + + The + response will be a JSON object with a key called `sizes`. The value of this will + be an array of `size` objects each of which contain the standard size + attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/sizes" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/sizes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + size_list_params.SizeListParams, + ), + ), + cast_to=SizeListResponse, + ) + + +class AsyncSizesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSizesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncSizesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSizesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncSizesResourceWithStreamingResponse(self) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SizeListResponse: + """To list all of available Droplet sizes, send a GET request to `/v2/sizes`. + + The + response will be a JSON object with a key called `sizes`. The value of this will + be an array of `size` objects each of which contain the standard size + attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/sizes" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/sizes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + size_list_params.SizeListParams, + ), + ), + cast_to=SizeListResponse, + ) + + +class SizesResourceWithRawResponse: + def __init__(self, sizes: SizesResource) -> None: + self._sizes = sizes + + self.list = to_raw_response_wrapper( + sizes.list, + ) + + +class AsyncSizesResourceWithRawResponse: + def __init__(self, sizes: AsyncSizesResource) -> None: + self._sizes = sizes + + self.list = async_to_raw_response_wrapper( + sizes.list, + ) + + +class SizesResourceWithStreamingResponse: + def __init__(self, sizes: SizesResource) -> None: + self._sizes = sizes + + self.list = to_streamed_response_wrapper( + sizes.list, + ) + + +class AsyncSizesResourceWithStreamingResponse: + def __init__(self, sizes: AsyncSizesResource) -> None: + self._sizes = sizes + + self.list = async_to_streamed_response_wrapper( + sizes.list, + ) diff --git a/src/gradient/resources/gpu_droplets/snapshots.py b/src/gradient/resources/gpu_droplets/snapshots.py new file mode 100644 index 00000000..78bd01ac --- /dev/null +++ b/src/gradient/resources/gpu_droplets/snapshots.py @@ -0,0 +1,425 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.gpu_droplets import snapshot_list_params +from ...types.gpu_droplets.snapshot_list_response import SnapshotListResponse +from ...types.gpu_droplets.snapshot_retrieve_response import SnapshotRetrieveResponse + +__all__ = ["SnapshotsResource", "AsyncSnapshotsResource"] + + +class SnapshotsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SnapshotsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return SnapshotsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SnapshotsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return SnapshotsResourceWithStreamingResponse(self) + + def retrieve( + self, + snapshot_id: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotRetrieveResponse: + """ + To retrieve information about a snapshot, send a GET request to + `/v2/snapshots/$SNAPSHOT_ID`. + + The response will be a JSON object with a key called `snapshot`. The value of + this will be an snapshot object containing the standard snapshot attributes. + + Args: + snapshot_id: The ID of a Droplet snapshot. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/v2/snapshots/{snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/snapshots/{snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotRetrieveResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + resource_type: Literal["droplet", "volume"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotListResponse: + """ + To list all of the snapshots available on your account, send a GET request to + `/v2/snapshots`. + + The response will be a JSON object with a key called `snapshots`. This will be + set to an array of `snapshot` objects, each of which will contain the standard + snapshot attributes. + + ### Filtering Results by Resource Type + + It's possible to request filtered results by including certain query parameters. + + #### List Droplet Snapshots + + To retrieve only snapshots based on Droplets, include the `resource_type` query + parameter set to `droplet`. For example, `/v2/snapshots?resource_type=droplet`. + + #### List Volume Snapshots + + To retrieve only snapshots based on volumes, include the `resource_type` query + parameter set to `volume`. For example, `/v2/snapshots?resource_type=volume`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + resource_type: Used to filter snapshots by a resource type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/snapshots" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + "resource_type": resource_type, + }, + snapshot_list_params.SnapshotListParams, + ), + ), + cast_to=SnapshotListResponse, + ) + + def delete( + self, + snapshot_id: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Both Droplet and volume snapshots are managed through the `/v2/snapshots/` + endpoint. To delete a snapshot, send a DELETE request to + `/v2/snapshots/$SNAPSHOT_ID`. + + A status of 204 will be given. This indicates that the request was processed + successfully, but that no response body is needed. + + Args: + snapshot_id: The ID of a Droplet snapshot. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/snapshots/{snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/snapshots/{snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncSnapshotsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSnapshotsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncSnapshotsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSnapshotsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncSnapshotsResourceWithStreamingResponse(self) + + async def retrieve( + self, + snapshot_id: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotRetrieveResponse: + """ + To retrieve information about a snapshot, send a GET request to + `/v2/snapshots/$SNAPSHOT_ID`. + + The response will be a JSON object with a key called `snapshot`. The value of + this will be an snapshot object containing the standard snapshot attributes. + + Args: + snapshot_id: The ID of a Droplet snapshot. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/v2/snapshots/{snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/snapshots/{snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotRetrieveResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + resource_type: Literal["droplet", "volume"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotListResponse: + """ + To list all of the snapshots available on your account, send a GET request to + `/v2/snapshots`. + + The response will be a JSON object with a key called `snapshots`. This will be + set to an array of `snapshot` objects, each of which will contain the standard + snapshot attributes. + + ### Filtering Results by Resource Type + + It's possible to request filtered results by including certain query parameters. + + #### List Droplet Snapshots + + To retrieve only snapshots based on Droplets, include the `resource_type` query + parameter set to `droplet`. For example, `/v2/snapshots?resource_type=droplet`. + + #### List Volume Snapshots + + To retrieve only snapshots based on volumes, include the `resource_type` query + parameter set to `volume`. For example, `/v2/snapshots?resource_type=volume`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + resource_type: Used to filter snapshots by a resource type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/snapshots" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + "resource_type": resource_type, + }, + snapshot_list_params.SnapshotListParams, + ), + ), + cast_to=SnapshotListResponse, + ) + + async def delete( + self, + snapshot_id: Union[int, str], + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Both Droplet and volume snapshots are managed through the `/v2/snapshots/` + endpoint. To delete a snapshot, send a DELETE request to + `/v2/snapshots/$SNAPSHOT_ID`. + + A status of 204 will be given. This indicates that the request was processed + successfully, but that no response body is needed. + + Args: + snapshot_id: The ID of a Droplet snapshot. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/snapshots/{snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/snapshots/{snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class SnapshotsResourceWithRawResponse: + def __init__(self, snapshots: SnapshotsResource) -> None: + self._snapshots = snapshots + + self.retrieve = to_raw_response_wrapper( + snapshots.retrieve, + ) + self.list = to_raw_response_wrapper( + snapshots.list, + ) + self.delete = to_raw_response_wrapper( + snapshots.delete, + ) + + +class AsyncSnapshotsResourceWithRawResponse: + def __init__(self, snapshots: AsyncSnapshotsResource) -> None: + self._snapshots = snapshots + + self.retrieve = async_to_raw_response_wrapper( + snapshots.retrieve, + ) + self.list = async_to_raw_response_wrapper( + snapshots.list, + ) + self.delete = async_to_raw_response_wrapper( + snapshots.delete, + ) + + +class SnapshotsResourceWithStreamingResponse: + def __init__(self, snapshots: SnapshotsResource) -> None: + self._snapshots = snapshots + + self.retrieve = to_streamed_response_wrapper( + snapshots.retrieve, + ) + self.list = to_streamed_response_wrapper( + snapshots.list, + ) + self.delete = to_streamed_response_wrapper( + snapshots.delete, + ) + + +class AsyncSnapshotsResourceWithStreamingResponse: + def __init__(self, snapshots: AsyncSnapshotsResource) -> None: + self._snapshots = snapshots + + self.retrieve = async_to_streamed_response_wrapper( + snapshots.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + snapshots.list, + ) + self.delete = async_to_streamed_response_wrapper( + snapshots.delete, + ) diff --git a/src/gradient/resources/gpu_droplets/volumes/__init__.py b/src/gradient/resources/gpu_droplets/volumes/__init__.py new file mode 100644 index 00000000..167db0b3 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/volumes/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .actions import ( + ActionsResource, + AsyncActionsResource, + ActionsResourceWithRawResponse, + AsyncActionsResourceWithRawResponse, + ActionsResourceWithStreamingResponse, + AsyncActionsResourceWithStreamingResponse, +) +from .volumes import ( + VolumesResource, + AsyncVolumesResource, + VolumesResourceWithRawResponse, + AsyncVolumesResourceWithRawResponse, + VolumesResourceWithStreamingResponse, + AsyncVolumesResourceWithStreamingResponse, +) +from .snapshots import ( + SnapshotsResource, + AsyncSnapshotsResource, + SnapshotsResourceWithRawResponse, + AsyncSnapshotsResourceWithRawResponse, + SnapshotsResourceWithStreamingResponse, + AsyncSnapshotsResourceWithStreamingResponse, +) + +__all__ = [ + "ActionsResource", + "AsyncActionsResource", + "ActionsResourceWithRawResponse", + "AsyncActionsResourceWithRawResponse", + "ActionsResourceWithStreamingResponse", + "AsyncActionsResourceWithStreamingResponse", + "SnapshotsResource", + "AsyncSnapshotsResource", + "SnapshotsResourceWithRawResponse", + "AsyncSnapshotsResourceWithRawResponse", + "SnapshotsResourceWithStreamingResponse", + "AsyncSnapshotsResourceWithStreamingResponse", + "VolumesResource", + "AsyncVolumesResource", + "VolumesResourceWithRawResponse", + "AsyncVolumesResourceWithRawResponse", + "VolumesResourceWithStreamingResponse", + "AsyncVolumesResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/gpu_droplets/volumes/actions.py b/src/gradient/resources/gpu_droplets/volumes/actions.py new file mode 100644 index 00000000..1c0c66a0 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/volumes/actions.py @@ -0,0 +1,1554 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, overload + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.volumes import ( + action_list_params, + action_retrieve_params, + action_initiate_by_id_params, + action_initiate_by_name_params, +) +from ....types.gpu_droplets.volumes.action_list_response import ActionListResponse +from ....types.gpu_droplets.volumes.action_retrieve_response import ActionRetrieveResponse +from ....types.gpu_droplets.volumes.action_initiate_by_id_response import ActionInitiateByIDResponse +from ....types.gpu_droplets.volumes.action_initiate_by_name_response import ActionInitiateByNameResponse + +__all__ = ["ActionsResource", "AsyncActionsResource"] + + +class ActionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ActionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ActionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ActionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ActionsResourceWithStreamingResponse(self) + + def retrieve( + self, + action_id: int, + *, + volume_id: str, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionRetrieveResponse: + """ + To retrieve the status of a volume action, send a GET request to + `/v2/volumes/$VOLUME_ID/actions/$ACTION_ID`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return self._get( + f"/v2/volumes/{volume_id}/actions/{action_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/actions/{action_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_retrieve_params.ActionRetrieveParams, + ), + ), + cast_to=ActionRetrieveResponse, + ) + + def list( + self, + volume_id: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionListResponse: + """ + To retrieve all actions that have been executed on a volume, send a GET request + to `/v2/volumes/$VOLUME_ID/actions`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return self._get( + f"/v2/volumes/{volume_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/actions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_list_params.ActionListParams, + ), + ), + cast_to=ActionListResponse, + ) + + @overload + def initiate_by_id( + self, + volume_id: str, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByIDResponse: + """ + To initiate an action on a block storage volume by Id, send a POST request to + `~/v2/volumes/$VOLUME_ID/actions`. The body should contain the appropriate + attributes for the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + ## Resize a Volume + + | Attribute | Details | + | -------------- | ------------------------------------------------------------------- | + | type | This must be `resize` | + | size_gigabytes | The new size of the block storage volume in GiB (1024^3) | + | region | Set to the slug representing the region where the volume is located | + + Volumes may only be resized upwards. The maximum size for a volume is 16TiB. + + Args: + droplet_id: The unique identifier for the Droplet the volume will be attached or detached + from. + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate_by_id( + self, + volume_id: str, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByIDResponse: + """ + To initiate an action on a block storage volume by Id, send a POST request to + `~/v2/volumes/$VOLUME_ID/actions`. The body should contain the appropriate + attributes for the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + ## Resize a Volume + + | Attribute | Details | + | -------------- | ------------------------------------------------------------------- | + | type | This must be `resize` | + | size_gigabytes | The new size of the block storage volume in GiB (1024^3) | + | region | Set to the slug representing the region where the volume is located | + + Volumes may only be resized upwards. The maximum size for a volume is 16TiB. + + Args: + droplet_id: The unique identifier for the Droplet the volume will be attached or detached + from. + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate_by_id( + self, + volume_id: str, + *, + size_gigabytes: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByIDResponse: + """ + To initiate an action on a block storage volume by Id, send a POST request to + `~/v2/volumes/$VOLUME_ID/actions`. The body should contain the appropriate + attributes for the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + ## Resize a Volume + + | Attribute | Details | + | -------------- | ------------------------------------------------------------------- | + | type | This must be `resize` | + | size_gigabytes | The new size of the block storage volume in GiB (1024^3) | + | region | Set to the slug representing the region where the volume is located | + + Volumes may only be resized upwards. The maximum size for a volume is 16TiB. + + Args: + size_gigabytes: The new size of the block storage volume in GiB (1024^3). + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["droplet_id", "type"], ["size_gigabytes", "type"]) + def initiate_by_id( + self, + volume_id: str, + *, + droplet_id: int | Omit = omit, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + size_gigabytes: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByIDResponse: + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return self._post( + f"/v2/volumes/{volume_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/actions", + body=maybe_transform( + { + "droplet_id": droplet_id, + "type": type, + "region": region, + "tags": tags, + "size_gigabytes": size_gigabytes, + }, + action_initiate_by_id_params.ActionInitiateByIDParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_initiate_by_id_params.ActionInitiateByIDParams, + ), + ), + cast_to=ActionInitiateByIDResponse, + ) + + @overload + def initiate_by_name( + self, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByNameResponse: + """ + To initiate an action on a block storage volume by Name, send a POST request to + `~/v2/volumes/actions`. The body should contain the appropriate attributes for + the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ----------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | volume_name | The name of the block storage volume | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ----------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | volume_name | The name of the block storage volume | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Args: + droplet_id: The unique identifier for the Droplet the volume will be attached or detached + from. + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate_by_name( + self, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByNameResponse: + """ + To initiate an action on a block storage volume by Name, send a POST request to + `~/v2/volumes/actions`. The body should contain the appropriate attributes for + the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ----------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | volume_name | The name of the block storage volume | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ----------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | volume_name | The name of the block storage volume | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Args: + droplet_id: The unique identifier for the Droplet the volume will be attached or detached + from. + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["droplet_id", "type"]) + def initiate_by_name( + self, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByNameResponse: + return self._post( + "/v2/volumes/actions" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/volumes/actions", + body=maybe_transform( + { + "droplet_id": droplet_id, + "type": type, + "region": region, + "tags": tags, + }, + action_initiate_by_name_params.ActionInitiateByNameParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_initiate_by_name_params.ActionInitiateByNameParams, + ), + ), + cast_to=ActionInitiateByNameResponse, + ) + + +class AsyncActionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncActionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncActionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncActionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncActionsResourceWithStreamingResponse(self) + + async def retrieve( + self, + action_id: int, + *, + volume_id: str, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionRetrieveResponse: + """ + To retrieve the status of a volume action, send a GET request to + `/v2/volumes/$VOLUME_ID/actions/$ACTION_ID`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return await self._get( + f"/v2/volumes/{volume_id}/actions/{action_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/actions/{action_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_retrieve_params.ActionRetrieveParams, + ), + ), + cast_to=ActionRetrieveResponse, + ) + + async def list( + self, + volume_id: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionListResponse: + """ + To retrieve all actions that have been executed on a volume, send a GET request + to `/v2/volumes/$VOLUME_ID/actions`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return await self._get( + f"/v2/volumes/{volume_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/actions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_list_params.ActionListParams, + ), + ), + cast_to=ActionListResponse, + ) + + @overload + async def initiate_by_id( + self, + volume_id: str, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByIDResponse: + """ + To initiate an action on a block storage volume by Id, send a POST request to + `~/v2/volumes/$VOLUME_ID/actions`. The body should contain the appropriate + attributes for the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + ## Resize a Volume + + | Attribute | Details | + | -------------- | ------------------------------------------------------------------- | + | type | This must be `resize` | + | size_gigabytes | The new size of the block storage volume in GiB (1024^3) | + | region | Set to the slug representing the region where the volume is located | + + Volumes may only be resized upwards. The maximum size for a volume is 16TiB. + + Args: + droplet_id: The unique identifier for the Droplet the volume will be attached or detached + from. + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate_by_id( + self, + volume_id: str, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByIDResponse: + """ + To initiate an action on a block storage volume by Id, send a POST request to + `~/v2/volumes/$VOLUME_ID/actions`. The body should contain the appropriate + attributes for the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + ## Resize a Volume + + | Attribute | Details | + | -------------- | ------------------------------------------------------------------- | + | type | This must be `resize` | + | size_gigabytes | The new size of the block storage volume in GiB (1024^3) | + | region | Set to the slug representing the region where the volume is located | + + Volumes may only be resized upwards. The maximum size for a volume is 16TiB. + + Args: + droplet_id: The unique identifier for the Droplet the volume will be attached or detached + from. + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate_by_id( + self, + volume_id: str, + *, + size_gigabytes: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByIDResponse: + """ + To initiate an action on a block storage volume by Id, send a POST request to + `~/v2/volumes/$VOLUME_ID/actions`. The body should contain the appropriate + attributes for the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ---------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + ## Resize a Volume + + | Attribute | Details | + | -------------- | ------------------------------------------------------------------- | + | type | This must be `resize` | + | size_gigabytes | The new size of the block storage volume in GiB (1024^3) | + | region | Set to the slug representing the region where the volume is located | + + Volumes may only be resized upwards. The maximum size for a volume is 16TiB. + + Args: + size_gigabytes: The new size of the block storage volume in GiB (1024^3). + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["droplet_id", "type"], ["size_gigabytes", "type"]) + async def initiate_by_id( + self, + volume_id: str, + *, + droplet_id: int | Omit = omit, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + size_gigabytes: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByIDResponse: + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return await self._post( + f"/v2/volumes/{volume_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/actions", + body=await async_maybe_transform( + { + "droplet_id": droplet_id, + "type": type, + "region": region, + "tags": tags, + "size_gigabytes": size_gigabytes, + }, + action_initiate_by_id_params.ActionInitiateByIDParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_initiate_by_id_params.ActionInitiateByIDParams, + ), + ), + cast_to=ActionInitiateByIDResponse, + ) + + @overload + async def initiate_by_name( + self, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByNameResponse: + """ + To initiate an action on a block storage volume by Name, send a POST request to + `~/v2/volumes/actions`. The body should contain the appropriate attributes for + the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ----------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | volume_name | The name of the block storage volume | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ----------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | volume_name | The name of the block storage volume | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Args: + droplet_id: The unique identifier for the Droplet the volume will be attached or detached + from. + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate_by_name( + self, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByNameResponse: + """ + To initiate an action on a block storage volume by Name, send a POST request to + `~/v2/volumes/actions`. The body should contain the appropriate attributes for + the respective action. + + ## Attach a Block Storage Volume to a Droplet + + | Attribute | Details | + | ----------- | ------------------------------------------------------------------- | + | type | This must be `attach` | + | volume_name | The name of the block storage volume | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Each volume may only be attached to a single Droplet. However, up to fifteen + volumes may be attached to a Droplet at a time. Pre-formatted volumes will be + automatically mounted to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS + Droplets created on or after April 26, 2018 when attached. On older Droplets, + [additional configuration](https://docs.digitalocean.com/products/volumes/how-to/mount/) + is required. + + ## Remove a Block Storage Volume from a Droplet + + | Attribute | Details | + | ----------- | ------------------------------------------------------------------- | + | type | This must be `detach` | + | volume_name | The name of the block storage volume | + | droplet_id | Set to the Droplet's ID | + | region | Set to the slug representing the region where the volume is located | + + Args: + droplet_id: The unique identifier for the Droplet the volume will be attached or detached + from. + + type: The volume action to initiate. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource will initially be + available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["droplet_id", "type"]) + async def initiate_by_name( + self, + *, + droplet_id: int, + type: Literal["attach", "detach", "resize"], + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ActionInitiateByNameResponse: + return await self._post( + "/v2/volumes/actions" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/volumes/actions", + body=await async_maybe_transform( + { + "droplet_id": droplet_id, + "type": type, + "region": region, + "tags": tags, + }, + action_initiate_by_name_params.ActionInitiateByNameParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + action_initiate_by_name_params.ActionInitiateByNameParams, + ), + ), + cast_to=ActionInitiateByNameResponse, + ) + + +class ActionsResourceWithRawResponse: + def __init__(self, actions: ActionsResource) -> None: + self._actions = actions + + self.retrieve = to_raw_response_wrapper( + actions.retrieve, + ) + self.list = to_raw_response_wrapper( + actions.list, + ) + self.initiate_by_id = to_raw_response_wrapper( + actions.initiate_by_id, + ) + self.initiate_by_name = to_raw_response_wrapper( + actions.initiate_by_name, + ) + + +class AsyncActionsResourceWithRawResponse: + def __init__(self, actions: AsyncActionsResource) -> None: + self._actions = actions + + self.retrieve = async_to_raw_response_wrapper( + actions.retrieve, + ) + self.list = async_to_raw_response_wrapper( + actions.list, + ) + self.initiate_by_id = async_to_raw_response_wrapper( + actions.initiate_by_id, + ) + self.initiate_by_name = async_to_raw_response_wrapper( + actions.initiate_by_name, + ) + + +class ActionsResourceWithStreamingResponse: + def __init__(self, actions: ActionsResource) -> None: + self._actions = actions + + self.retrieve = to_streamed_response_wrapper( + actions.retrieve, + ) + self.list = to_streamed_response_wrapper( + actions.list, + ) + self.initiate_by_id = to_streamed_response_wrapper( + actions.initiate_by_id, + ) + self.initiate_by_name = to_streamed_response_wrapper( + actions.initiate_by_name, + ) + + +class AsyncActionsResourceWithStreamingResponse: + def __init__(self, actions: AsyncActionsResource) -> None: + self._actions = actions + + self.retrieve = async_to_streamed_response_wrapper( + actions.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + actions.list, + ) + self.initiate_by_id = async_to_streamed_response_wrapper( + actions.initiate_by_id, + ) + self.initiate_by_name = async_to_streamed_response_wrapper( + actions.initiate_by_name, + ) diff --git a/src/gradient/resources/gpu_droplets/volumes/snapshots.py b/src/gradient/resources/gpu_droplets/volumes/snapshots.py new file mode 100644 index 00000000..694de074 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/volumes/snapshots.py @@ -0,0 +1,499 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets.volumes import snapshot_list_params, snapshot_create_params +from ....types.gpu_droplets.volumes.snapshot_list_response import SnapshotListResponse +from ....types.gpu_droplets.volumes.snapshot_create_response import SnapshotCreateResponse +from ....types.gpu_droplets.volumes.snapshot_retrieve_response import SnapshotRetrieveResponse + +__all__ = ["SnapshotsResource", "AsyncSnapshotsResource"] + + +class SnapshotsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SnapshotsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return SnapshotsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SnapshotsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return SnapshotsResourceWithStreamingResponse(self) + + def create( + self, + volume_id: str, + *, + name: str, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotCreateResponse: + """ + To create a snapshot from a volume, sent a POST request to + `/v2/volumes/$VOLUME_ID/snapshots`. + + Args: + name: A human-readable name for the volume snapshot. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return self._post( + f"/v2/volumes/{volume_id}/snapshots" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/snapshots", + body=maybe_transform( + { + "name": name, + "tags": tags, + }, + snapshot_create_params.SnapshotCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotCreateResponse, + ) + + def retrieve( + self, + snapshot_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotRetrieveResponse: + """ + To retrieve the details of a snapshot that has been created from a volume, send + a GET request to `/v2/volumes/snapshots/$VOLUME_SNAPSHOT_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not snapshot_id: + raise ValueError(f"Expected a non-empty value for `snapshot_id` but received {snapshot_id!r}") + return self._get( + f"/v2/volumes/snapshots/{snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/snapshots/{snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotRetrieveResponse, + ) + + def list( + self, + volume_id: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotListResponse: + """ + To retrieve the snapshots that have been created from a volume, send a GET + request to `/v2/volumes/$VOLUME_ID/snapshots`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return self._get( + f"/v2/volumes/{volume_id}/snapshots" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + snapshot_list_params.SnapshotListParams, + ), + ), + cast_to=SnapshotListResponse, + ) + + def delete( + self, + snapshot_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a volume snapshot, send a DELETE request to + `/v2/volumes/snapshots/$VOLUME_SNAPSHOT_ID`. + + A status of 204 will be given. This indicates that the request was processed + successfully, but that no response body is needed. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not snapshot_id: + raise ValueError(f"Expected a non-empty value for `snapshot_id` but received {snapshot_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/volumes/snapshots/{snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/snapshots/{snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncSnapshotsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSnapshotsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncSnapshotsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSnapshotsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncSnapshotsResourceWithStreamingResponse(self) + + async def create( + self, + volume_id: str, + *, + name: str, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotCreateResponse: + """ + To create a snapshot from a volume, sent a POST request to + `/v2/volumes/$VOLUME_ID/snapshots`. + + Args: + name: A human-readable name for the volume snapshot. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return await self._post( + f"/v2/volumes/{volume_id}/snapshots" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/snapshots", + body=await async_maybe_transform( + { + "name": name, + "tags": tags, + }, + snapshot_create_params.SnapshotCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotCreateResponse, + ) + + async def retrieve( + self, + snapshot_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotRetrieveResponse: + """ + To retrieve the details of a snapshot that has been created from a volume, send + a GET request to `/v2/volumes/snapshots/$VOLUME_SNAPSHOT_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not snapshot_id: + raise ValueError(f"Expected a non-empty value for `snapshot_id` but received {snapshot_id!r}") + return await self._get( + f"/v2/volumes/snapshots/{snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/snapshots/{snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotRetrieveResponse, + ) + + async def list( + self, + volume_id: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotListResponse: + """ + To retrieve the snapshots that have been created from a volume, send a GET + request to `/v2/volumes/$VOLUME_ID/snapshots`. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return await self._get( + f"/v2/volumes/{volume_id}/snapshots" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + snapshot_list_params.SnapshotListParams, + ), + ), + cast_to=SnapshotListResponse, + ) + + async def delete( + self, + snapshot_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a volume snapshot, send a DELETE request to + `/v2/volumes/snapshots/$VOLUME_SNAPSHOT_ID`. + + A status of 204 will be given. This indicates that the request was processed + successfully, but that no response body is needed. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not snapshot_id: + raise ValueError(f"Expected a non-empty value for `snapshot_id` but received {snapshot_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/volumes/snapshots/{snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/snapshots/{snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class SnapshotsResourceWithRawResponse: + def __init__(self, snapshots: SnapshotsResource) -> None: + self._snapshots = snapshots + + self.create = to_raw_response_wrapper( + snapshots.create, + ) + self.retrieve = to_raw_response_wrapper( + snapshots.retrieve, + ) + self.list = to_raw_response_wrapper( + snapshots.list, + ) + self.delete = to_raw_response_wrapper( + snapshots.delete, + ) + + +class AsyncSnapshotsResourceWithRawResponse: + def __init__(self, snapshots: AsyncSnapshotsResource) -> None: + self._snapshots = snapshots + + self.create = async_to_raw_response_wrapper( + snapshots.create, + ) + self.retrieve = async_to_raw_response_wrapper( + snapshots.retrieve, + ) + self.list = async_to_raw_response_wrapper( + snapshots.list, + ) + self.delete = async_to_raw_response_wrapper( + snapshots.delete, + ) + + +class SnapshotsResourceWithStreamingResponse: + def __init__(self, snapshots: SnapshotsResource) -> None: + self._snapshots = snapshots + + self.create = to_streamed_response_wrapper( + snapshots.create, + ) + self.retrieve = to_streamed_response_wrapper( + snapshots.retrieve, + ) + self.list = to_streamed_response_wrapper( + snapshots.list, + ) + self.delete = to_streamed_response_wrapper( + snapshots.delete, + ) + + +class AsyncSnapshotsResourceWithStreamingResponse: + def __init__(self, snapshots: AsyncSnapshotsResource) -> None: + self._snapshots = snapshots + + self.create = async_to_streamed_response_wrapper( + snapshots.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + snapshots.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + snapshots.list, + ) + self.delete = async_to_streamed_response_wrapper( + snapshots.delete, + ) diff --git a/src/gradient/resources/gpu_droplets/volumes/volumes.py b/src/gradient/resources/gpu_droplets/volumes/volumes.py new file mode 100644 index 00000000..fb86c288 --- /dev/null +++ b/src/gradient/resources/gpu_droplets/volumes/volumes.py @@ -0,0 +1,1144 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, overload + +import httpx + +from .actions import ( + ActionsResource, + AsyncActionsResource, + ActionsResourceWithRawResponse, + AsyncActionsResourceWithRawResponse, + ActionsResourceWithStreamingResponse, + AsyncActionsResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from .snapshots import ( + SnapshotsResource, + AsyncSnapshotsResource, + SnapshotsResourceWithRawResponse, + AsyncSnapshotsResourceWithRawResponse, + SnapshotsResourceWithStreamingResponse, + AsyncSnapshotsResourceWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.gpu_droplets import volume_list_params, volume_create_params, volume_delete_by_name_params +from ....types.gpu_droplets.volume_list_response import VolumeListResponse +from ....types.gpu_droplets.volume_create_response import VolumeCreateResponse +from ....types.gpu_droplets.volume_retrieve_response import VolumeRetrieveResponse + +__all__ = ["VolumesResource", "AsyncVolumesResource"] + + +class VolumesResource(SyncAPIResource): + @cached_property + def actions(self) -> ActionsResource: + return ActionsResource(self._client) + + @cached_property + def snapshots(self) -> SnapshotsResource: + return SnapshotsResource(self._client) + + @cached_property + def with_raw_response(self) -> VolumesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return VolumesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VolumesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return VolumesResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + name: str, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ], + size_gigabytes: int, + description: str | Omit = omit, + filesystem_label: str | Omit = omit, + filesystem_type: str | Omit = omit, + snapshot_id: str | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeCreateResponse: + """To create a new volume, send a POST request to `/v2/volumes`. + + Optionally, a + `filesystem_type` attribute may be provided in order to automatically format the + volume's filesystem. Pre-formatted volumes are automatically mounted when + attached to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS Droplets created + on or after April 26, 2018. Attaching pre-formatted volumes to Droplets without + support for auto-mounting is not recommended. + + Args: + name: A human-readable name for the block storage volume. Must be lowercase and be + composed only of numbers, letters and "-", up to a limit of 64 characters. The + name must begin with a letter. + + region: The slug identifier for the region where the resource will initially be + available. + + size_gigabytes: The size of the block storage volume in GiB (1024^3). This field does not apply + when creating a volume from a snapshot. + + description: An optional free-form text field to describe a block storage volume. + + filesystem_label: The label applied to the filesystem. Labels for ext4 type filesystems may + contain 16 characters while labels for xfs type filesystems are limited to 12 + characters. May only be used in conjunction with filesystem_type. + + filesystem_type: The name of the filesystem type to be used on the volume. When provided, the + volume will automatically be formatted to the specified filesystem type. + Currently, the available options are `ext4` and `xfs`. Pre-formatted volumes are + automatically mounted when attached to Ubuntu, Debian, Fedora, Fedora Atomic, + and CentOS Droplets created on or after April 26, 2018. Attaching pre-formatted + volumes to other Droplets is not recommended. + + snapshot_id: The unique identifier for the volume snapshot from which to create the volume. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + name: str, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ], + size_gigabytes: int, + description: str | Omit = omit, + filesystem_label: str | Omit = omit, + filesystem_type: str | Omit = omit, + snapshot_id: str | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeCreateResponse: + """To create a new volume, send a POST request to `/v2/volumes`. + + Optionally, a + `filesystem_type` attribute may be provided in order to automatically format the + volume's filesystem. Pre-formatted volumes are automatically mounted when + attached to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS Droplets created + on or after April 26, 2018. Attaching pre-formatted volumes to Droplets without + support for auto-mounting is not recommended. + + Args: + name: A human-readable name for the block storage volume. Must be lowercase and be + composed only of numbers, letters and "-", up to a limit of 64 characters. The + name must begin with a letter. + + region: The slug identifier for the region where the resource will initially be + available. + + size_gigabytes: The size of the block storage volume in GiB (1024^3). This field does not apply + when creating a volume from a snapshot. + + description: An optional free-form text field to describe a block storage volume. + + filesystem_label: The label applied to the filesystem. Labels for ext4 type filesystems may + contain 16 characters while labels for xfs type filesystems are limited to 12 + characters. May only be used in conjunction with filesystem_type. + + filesystem_type: The name of the filesystem type to be used on the volume. When provided, the + volume will automatically be formatted to the specified filesystem type. + Currently, the available options are `ext4` and `xfs`. Pre-formatted volumes are + automatically mounted when attached to Ubuntu, Debian, Fedora, Fedora Atomic, + and CentOS Droplets created on or after April 26, 2018. Attaching pre-formatted + volumes to other Droplets is not recommended. + + snapshot_id: The unique identifier for the volume snapshot from which to create the volume. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["name", "region", "size_gigabytes"]) + def create( + self, + *, + name: str, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ], + size_gigabytes: int, + description: str | Omit = omit, + filesystem_label: str | Omit = omit, + filesystem_type: str | Omit = omit, + snapshot_id: str | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeCreateResponse: + return self._post( + "/v2/volumes" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/volumes", + body=maybe_transform( + { + "name": name, + "region": region, + "size_gigabytes": size_gigabytes, + "description": description, + "filesystem_label": filesystem_label, + "filesystem_type": filesystem_type, + "snapshot_id": snapshot_id, + "tags": tags, + }, + volume_create_params.VolumeCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VolumeCreateResponse, + ) + + def retrieve( + self, + volume_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeRetrieveResponse: + """ + To show information about a block storage volume, send a GET request to + `/v2/volumes/$VOLUME_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return self._get( + f"/v2/volumes/{volume_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VolumeRetrieveResponse, + ) + + def list( + self, + *, + name: str | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeListResponse: + """ + To list all of the block storage volumes available on your account, send a GET + request to `/v2/volumes`. + + ## Filtering Results + + ### By Region + + The `region` may be provided as query parameter in order to restrict results to + volumes available in a specific region. For example: `/v2/volumes?region=nyc1` + + ### By Name + + It is also possible to list volumes on your account that match a specified name. + To do so, send a GET request with the volume's name as a query parameter to + `/v2/volumes?name=$VOLUME_NAME`. **Note:** You can only create one volume per + region with the same name. + + ### By Name and Region + + It is also possible to retrieve information about a block storage volume by + name. To do so, send a GET request with the volume's name and the region slug + for the region it is located in as query parameters to + `/v2/volumes?name=$VOLUME_NAME®ion=nyc1`. + + Args: + name: The block storage volume's name. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource is available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/volumes" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/volumes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "name": name, + "page": page, + "per_page": per_page, + "region": region, + }, + volume_list_params.VolumeListParams, + ), + ), + cast_to=VolumeListResponse, + ) + + def delete( + self, + volume_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a block storage volume, destroying all data and removing it from your + account, send a DELETE request to `/v2/volumes/$VOLUME_ID`. No response body + will be sent back, but the response code will indicate success. Specifically, + the response code will be a 204, which means that the action was successful with + no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/volumes/{volume_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def delete_by_name( + self, + *, + name: str | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Block storage volumes may also be deleted by name by sending a DELETE request + with the volume's **name** and the **region slug** for the region it is located + in as query parameters to `/v2/volumes?name=$VOLUME_NAME®ion=nyc1`. No + response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + name: The block storage volume's name. + + region: The slug identifier for the region where the resource is available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + "/v2/volumes" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/volumes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "name": name, + "region": region, + }, + volume_delete_by_name_params.VolumeDeleteByNameParams, + ), + ), + cast_to=NoneType, + ) + + +class AsyncVolumesResource(AsyncAPIResource): + @cached_property + def actions(self) -> AsyncActionsResource: + return AsyncActionsResource(self._client) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResource: + return AsyncSnapshotsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncVolumesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncVolumesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVolumesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncVolumesResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + name: str, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ], + size_gigabytes: int, + description: str | Omit = omit, + filesystem_label: str | Omit = omit, + filesystem_type: str | Omit = omit, + snapshot_id: str | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeCreateResponse: + """To create a new volume, send a POST request to `/v2/volumes`. + + Optionally, a + `filesystem_type` attribute may be provided in order to automatically format the + volume's filesystem. Pre-formatted volumes are automatically mounted when + attached to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS Droplets created + on or after April 26, 2018. Attaching pre-formatted volumes to Droplets without + support for auto-mounting is not recommended. + + Args: + name: A human-readable name for the block storage volume. Must be lowercase and be + composed only of numbers, letters and "-", up to a limit of 64 characters. The + name must begin with a letter. + + region: The slug identifier for the region where the resource will initially be + available. + + size_gigabytes: The size of the block storage volume in GiB (1024^3). This field does not apply + when creating a volume from a snapshot. + + description: An optional free-form text field to describe a block storage volume. + + filesystem_label: The label applied to the filesystem. Labels for ext4 type filesystems may + contain 16 characters while labels for xfs type filesystems are limited to 12 + characters. May only be used in conjunction with filesystem_type. + + filesystem_type: The name of the filesystem type to be used on the volume. When provided, the + volume will automatically be formatted to the specified filesystem type. + Currently, the available options are `ext4` and `xfs`. Pre-formatted volumes are + automatically mounted when attached to Ubuntu, Debian, Fedora, Fedora Atomic, + and CentOS Droplets created on or after April 26, 2018. Attaching pre-formatted + volumes to other Droplets is not recommended. + + snapshot_id: The unique identifier for the volume snapshot from which to create the volume. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + name: str, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ], + size_gigabytes: int, + description: str | Omit = omit, + filesystem_label: str | Omit = omit, + filesystem_type: str | Omit = omit, + snapshot_id: str | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeCreateResponse: + """To create a new volume, send a POST request to `/v2/volumes`. + + Optionally, a + `filesystem_type` attribute may be provided in order to automatically format the + volume's filesystem. Pre-formatted volumes are automatically mounted when + attached to Ubuntu, Debian, Fedora, Fedora Atomic, and CentOS Droplets created + on or after April 26, 2018. Attaching pre-formatted volumes to Droplets without + support for auto-mounting is not recommended. + + Args: + name: A human-readable name for the block storage volume. Must be lowercase and be + composed only of numbers, letters and "-", up to a limit of 64 characters. The + name must begin with a letter. + + region: The slug identifier for the region where the resource will initially be + available. + + size_gigabytes: The size of the block storage volume in GiB (1024^3). This field does not apply + when creating a volume from a snapshot. + + description: An optional free-form text field to describe a block storage volume. + + filesystem_label: The label applied to the filesystem. Labels for ext4 type filesystems may + contain 16 characters while labels for xfs type filesystems are limited to 12 + characters. May only be used in conjunction with filesystem_type. + + filesystem_type: The name of the filesystem type to be used on the volume. When provided, the + volume will automatically be formatted to the specified filesystem type. + Currently, the available options are `ext4` and `xfs`. Pre-formatted volumes are + automatically mounted when attached to Ubuntu, Debian, Fedora, Fedora Atomic, + and CentOS Droplets created on or after April 26, 2018. Attaching pre-formatted + volumes to other Droplets is not recommended. + + snapshot_id: The unique identifier for the volume snapshot from which to create the volume. + + tags: A flat array of tag names as strings to be applied to the resource. Tag names + may be for either existing or new tags. + + Requires `tag:create` scope. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["name", "region", "size_gigabytes"]) + async def create( + self, + *, + name: str, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ], + size_gigabytes: int, + description: str | Omit = omit, + filesystem_label: str | Omit = omit, + filesystem_type: str | Omit = omit, + snapshot_id: str | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeCreateResponse: + return await self._post( + "/v2/volumes" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/volumes", + body=await async_maybe_transform( + { + "name": name, + "region": region, + "size_gigabytes": size_gigabytes, + "description": description, + "filesystem_label": filesystem_label, + "filesystem_type": filesystem_type, + "snapshot_id": snapshot_id, + "tags": tags, + }, + volume_create_params.VolumeCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VolumeCreateResponse, + ) + + async def retrieve( + self, + volume_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeRetrieveResponse: + """ + To show information about a block storage volume, send a GET request to + `/v2/volumes/$VOLUME_ID`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + return await self._get( + f"/v2/volumes/{volume_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VolumeRetrieveResponse, + ) + + async def list( + self, + *, + name: str | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VolumeListResponse: + """ + To list all of the block storage volumes available on your account, send a GET + request to `/v2/volumes`. + + ## Filtering Results + + ### By Region + + The `region` may be provided as query parameter in order to restrict results to + volumes available in a specific region. For example: `/v2/volumes?region=nyc1` + + ### By Name + + It is also possible to list volumes on your account that match a specified name. + To do so, send a GET request with the volume's name as a query parameter to + `/v2/volumes?name=$VOLUME_NAME`. **Note:** You can only create one volume per + region with the same name. + + ### By Name and Region + + It is also possible to retrieve information about a block storage volume by + name. To do so, send a GET request with the volume's name and the region slug + for the region it is located in as query parameters to + `/v2/volumes?name=$VOLUME_NAME®ion=nyc1`. + + Args: + name: The block storage volume's name. + + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + region: The slug identifier for the region where the resource is available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/volumes" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/volumes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "name": name, + "page": page, + "per_page": per_page, + "region": region, + }, + volume_list_params.VolumeListParams, + ), + ), + cast_to=VolumeListResponse, + ) + + async def delete( + self, + volume_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete a block storage volume, destroying all data and removing it from your + account, send a DELETE request to `/v2/volumes/$VOLUME_ID`. No response body + will be sent back, but the response code will indicate success. Specifically, + the response code will be a 204, which means that the action was successful with + no returned body data. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not volume_id: + raise ValueError(f"Expected a non-empty value for `volume_id` but received {volume_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/volumes/{volume_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/volumes/{volume_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def delete_by_name( + self, + *, + name: str | Omit = omit, + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Block storage volumes may also be deleted by name by sending a DELETE request + with the volume's **name** and the **region slug** for the region it is located + in as query parameters to `/v2/volumes?name=$VOLUME_NAME®ion=nyc1`. No + response body will be sent back, but the response code will indicate success. + Specifically, the response code will be a 204, which means that the action was + successful with no returned body data. + + Args: + name: The block storage volume's name. + + region: The slug identifier for the region where the resource is available. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + "/v2/volumes" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/volumes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "name": name, + "region": region, + }, + volume_delete_by_name_params.VolumeDeleteByNameParams, + ), + ), + cast_to=NoneType, + ) + + +class VolumesResourceWithRawResponse: + def __init__(self, volumes: VolumesResource) -> None: + self._volumes = volumes + + self.create = to_raw_response_wrapper( + volumes.create, + ) + self.retrieve = to_raw_response_wrapper( + volumes.retrieve, + ) + self.list = to_raw_response_wrapper( + volumes.list, + ) + self.delete = to_raw_response_wrapper( + volumes.delete, + ) + self.delete_by_name = to_raw_response_wrapper( + volumes.delete_by_name, + ) + + @cached_property + def actions(self) -> ActionsResourceWithRawResponse: + return ActionsResourceWithRawResponse(self._volumes.actions) + + @cached_property + def snapshots(self) -> SnapshotsResourceWithRawResponse: + return SnapshotsResourceWithRawResponse(self._volumes.snapshots) + + +class AsyncVolumesResourceWithRawResponse: + def __init__(self, volumes: AsyncVolumesResource) -> None: + self._volumes = volumes + + self.create = async_to_raw_response_wrapper( + volumes.create, + ) + self.retrieve = async_to_raw_response_wrapper( + volumes.retrieve, + ) + self.list = async_to_raw_response_wrapper( + volumes.list, + ) + self.delete = async_to_raw_response_wrapper( + volumes.delete, + ) + self.delete_by_name = async_to_raw_response_wrapper( + volumes.delete_by_name, + ) + + @cached_property + def actions(self) -> AsyncActionsResourceWithRawResponse: + return AsyncActionsResourceWithRawResponse(self._volumes.actions) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResourceWithRawResponse: + return AsyncSnapshotsResourceWithRawResponse(self._volumes.snapshots) + + +class VolumesResourceWithStreamingResponse: + def __init__(self, volumes: VolumesResource) -> None: + self._volumes = volumes + + self.create = to_streamed_response_wrapper( + volumes.create, + ) + self.retrieve = to_streamed_response_wrapper( + volumes.retrieve, + ) + self.list = to_streamed_response_wrapper( + volumes.list, + ) + self.delete = to_streamed_response_wrapper( + volumes.delete, + ) + self.delete_by_name = to_streamed_response_wrapper( + volumes.delete_by_name, + ) + + @cached_property + def actions(self) -> ActionsResourceWithStreamingResponse: + return ActionsResourceWithStreamingResponse(self._volumes.actions) + + @cached_property + def snapshots(self) -> SnapshotsResourceWithStreamingResponse: + return SnapshotsResourceWithStreamingResponse(self._volumes.snapshots) + + +class AsyncVolumesResourceWithStreamingResponse: + def __init__(self, volumes: AsyncVolumesResource) -> None: + self._volumes = volumes + + self.create = async_to_streamed_response_wrapper( + volumes.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + volumes.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + volumes.list, + ) + self.delete = async_to_streamed_response_wrapper( + volumes.delete, + ) + self.delete_by_name = async_to_streamed_response_wrapper( + volumes.delete_by_name, + ) + + @cached_property + def actions(self) -> AsyncActionsResourceWithStreamingResponse: + return AsyncActionsResourceWithStreamingResponse(self._volumes.actions) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResourceWithStreamingResponse: + return AsyncSnapshotsResourceWithStreamingResponse(self._volumes.snapshots) diff --git a/src/gradient/resources/images.py b/src/gradient/resources/images.py new file mode 100644 index 00000000..1cf13502 --- /dev/null +++ b/src/gradient/resources/images.py @@ -0,0 +1,686 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, overload + +import httpx + +from ..types import image_generate_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import required_args, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._streaming import Stream, AsyncStream +from .._base_client import make_request_options +from ..types.image_generate_response import ImageGenerateResponse +from ..types.shared.image_gen_stream_event import ImageGenStreamEvent + +__all__ = ["ImagesResource", "AsyncImagesResource"] + + +class ImagesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ImagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ImagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ImagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ImagesResourceWithStreamingResponse(self) + + @overload + def generate( + self, + *, + prompt: str, + background: Optional[str] | Omit = omit, + model: str | Omit = omit, + moderation: Optional[str] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[str] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[str] | Omit = omit, + size: Optional[str] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + user: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageGenerateResponse: + """ + Creates a high-quality image from a text prompt using GPT-IMAGE-1, the latest + image generation model with automatic prompt optimization and enhanced visual + capabilities. + + Args: + prompt: A text description of the desired image(s). GPT-IMAGE-1 supports up to 32,000 + characters and provides automatic prompt optimization for best results. + + background: + The background setting for the image generation. GPT-IMAGE-1 supports: + transparent, opaque, auto. + + model: The model to use for image generation. GPT-IMAGE-1 is the latest model offering + the best quality with automatic optimization and enhanced capabilities. + + moderation: The moderation setting for the image generation. GPT-IMAGE-1 supports: low, + auto. + + n: The number of images to generate. GPT-IMAGE-1 only supports n=1. + + output_compression: The output compression for the image generation. GPT-IMAGE-1 supports: 0-100. + + output_format: The output format for the image generation. GPT-IMAGE-1 supports: png, webp, + jpeg. + + partial_images: The number of partial image chunks to return during streaming generation. This + parameter is optional with a default of 0. When stream=true, this must be + greater than 0 to receive progressive updates of the image as it's being + generated. Higher values provide more frequent updates but may increase response + overhead. + + quality: The quality of the image that will be generated. GPT-IMAGE-1 supports: auto + (automatically select best quality), high, medium, low. + + size: The size of the generated images. GPT-IMAGE-1 supports: auto (automatically + select best size), 1536x1024 (landscape), 1024x1536 (portrait). + + stream: If set to true, partial image data will be streamed as the image is being + generated. When streaming, the response will be sent as server-sent events with + partial image chunks. When stream is true, partial_images must be greater + than 0. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def generate( + self, + *, + prompt: str, + stream: Literal[True], + background: Optional[str] | Omit = omit, + model: str | Omit = omit, + moderation: Optional[str] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[str] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[str] | Omit = omit, + size: Optional[str] | Omit = omit, + user: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[ImageGenStreamEvent]: + """ + Creates a high-quality image from a text prompt using GPT-IMAGE-1, the latest + image generation model with automatic prompt optimization and enhanced visual + capabilities. + + Args: + prompt: A text description of the desired image(s). GPT-IMAGE-1 supports up to 32,000 + characters and provides automatic prompt optimization for best results. + + stream: If set to true, partial image data will be streamed as the image is being + generated. When streaming, the response will be sent as server-sent events with + partial image chunks. When stream is true, partial_images must be greater + than 0. + + background: + The background setting for the image generation. GPT-IMAGE-1 supports: + transparent, opaque, auto. + + model: The model to use for image generation. GPT-IMAGE-1 is the latest model offering + the best quality with automatic optimization and enhanced capabilities. + + moderation: The moderation setting for the image generation. GPT-IMAGE-1 supports: low, + auto. + + n: The number of images to generate. GPT-IMAGE-1 only supports n=1. + + output_compression: The output compression for the image generation. GPT-IMAGE-1 supports: 0-100. + + output_format: The output format for the image generation. GPT-IMAGE-1 supports: png, webp, + jpeg. + + partial_images: The number of partial image chunks to return during streaming generation. This + parameter is optional with a default of 0. When stream=true, this must be + greater than 0 to receive progressive updates of the image as it's being + generated. Higher values provide more frequent updates but may increase response + overhead. + + quality: The quality of the image that will be generated. GPT-IMAGE-1 supports: auto + (automatically select best quality), high, medium, low. + + size: The size of the generated images. GPT-IMAGE-1 supports: auto (automatically + select best size), 1536x1024 (landscape), 1024x1536 (portrait). + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def generate( + self, + *, + prompt: str, + stream: bool, + background: Optional[str] | Omit = omit, + model: str | Omit = omit, + moderation: Optional[str] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[str] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[str] | Omit = omit, + size: Optional[str] | Omit = omit, + user: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageGenerateResponse | Stream[ImageGenStreamEvent]: + """ + Creates a high-quality image from a text prompt using GPT-IMAGE-1, the latest + image generation model with automatic prompt optimization and enhanced visual + capabilities. + + Args: + prompt: A text description of the desired image(s). GPT-IMAGE-1 supports up to 32,000 + characters and provides automatic prompt optimization for best results. + + stream: If set to true, partial image data will be streamed as the image is being + generated. When streaming, the response will be sent as server-sent events with + partial image chunks. When stream is true, partial_images must be greater + than 0. + + background: + The background setting for the image generation. GPT-IMAGE-1 supports: + transparent, opaque, auto. + + model: The model to use for image generation. GPT-IMAGE-1 is the latest model offering + the best quality with automatic optimization and enhanced capabilities. + + moderation: The moderation setting for the image generation. GPT-IMAGE-1 supports: low, + auto. + + n: The number of images to generate. GPT-IMAGE-1 only supports n=1. + + output_compression: The output compression for the image generation. GPT-IMAGE-1 supports: 0-100. + + output_format: The output format for the image generation. GPT-IMAGE-1 supports: png, webp, + jpeg. + + partial_images: The number of partial image chunks to return during streaming generation. This + parameter is optional with a default of 0. When stream=true, this must be + greater than 0 to receive progressive updates of the image as it's being + generated. Higher values provide more frequent updates but may increase response + overhead. + + quality: The quality of the image that will be generated. GPT-IMAGE-1 supports: auto + (automatically select best quality), high, medium, low. + + size: The size of the generated images. GPT-IMAGE-1 supports: auto (automatically + select best size), 1536x1024 (landscape), 1024x1536 (portrait). + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["prompt"], ["prompt", "stream"]) + def generate( + self, + *, + prompt: str, + background: Optional[str] | Omit = omit, + model: str | Omit = omit, + moderation: Optional[str] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[str] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[str] | Omit = omit, + size: Optional[str] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + user: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageGenerateResponse | Stream[ImageGenStreamEvent]: + return self._post( + "/images/generations" + if self._client._base_url_overridden + else f"{self._client.inference_endpoint}/v1/images/generations", + body=maybe_transform( + { + "prompt": prompt, + "background": background, + "model": model, + "moderation": moderation, + "n": n, + "output_compression": output_compression, + "output_format": output_format, + "partial_images": partial_images, + "quality": quality, + "size": size, + "stream": stream, + "user": user, + }, + image_generate_params.ImageGenerateParamsStreaming + if stream + else image_generate_params.ImageGenerateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImageGenerateResponse, + stream=stream or False, + stream_cls=Stream[ImageGenStreamEvent], + ) + + +class AsyncImagesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncImagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncImagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncImagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncImagesResourceWithStreamingResponse(self) + + @overload + async def generate( + self, + *, + prompt: str, + background: Optional[str] | Omit = omit, + model: str | Omit = omit, + moderation: Optional[str] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[str] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[str] | Omit = omit, + size: Optional[str] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + user: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageGenerateResponse: + """ + Creates a high-quality image from a text prompt using GPT-IMAGE-1, the latest + image generation model with automatic prompt optimization and enhanced visual + capabilities. + + Args: + prompt: A text description of the desired image(s). GPT-IMAGE-1 supports up to 32,000 + characters and provides automatic prompt optimization for best results. + + background: + The background setting for the image generation. GPT-IMAGE-1 supports: + transparent, opaque, auto. + + model: The model to use for image generation. GPT-IMAGE-1 is the latest model offering + the best quality with automatic optimization and enhanced capabilities. + + moderation: The moderation setting for the image generation. GPT-IMAGE-1 supports: low, + auto. + + n: The number of images to generate. GPT-IMAGE-1 only supports n=1. + + output_compression: The output compression for the image generation. GPT-IMAGE-1 supports: 0-100. + + output_format: The output format for the image generation. GPT-IMAGE-1 supports: png, webp, + jpeg. + + partial_images: The number of partial image chunks to return during streaming generation. This + parameter is optional with a default of 0. When stream=true, this must be + greater than 0 to receive progressive updates of the image as it's being + generated. Higher values provide more frequent updates but may increase response + overhead. + + quality: The quality of the image that will be generated. GPT-IMAGE-1 supports: auto + (automatically select best quality), high, medium, low. + + size: The size of the generated images. GPT-IMAGE-1 supports: auto (automatically + select best size), 1536x1024 (landscape), 1024x1536 (portrait). + + stream: If set to true, partial image data will be streamed as the image is being + generated. When streaming, the response will be sent as server-sent events with + partial image chunks. When stream is true, partial_images must be greater + than 0. + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def generate( + self, + *, + prompt: str, + stream: Literal[True], + background: Optional[str] | Omit = omit, + model: str | Omit = omit, + moderation: Optional[str] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[str] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[str] | Omit = omit, + size: Optional[str] | Omit = omit, + user: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ImageGenStreamEvent]: + """ + Creates a high-quality image from a text prompt using GPT-IMAGE-1, the latest + image generation model with automatic prompt optimization and enhanced visual + capabilities. + + Args: + prompt: A text description of the desired image(s). GPT-IMAGE-1 supports up to 32,000 + characters and provides automatic prompt optimization for best results. + + stream: If set to true, partial image data will be streamed as the image is being + generated. When streaming, the response will be sent as server-sent events with + partial image chunks. When stream is true, partial_images must be greater + than 0. + + background: + The background setting for the image generation. GPT-IMAGE-1 supports: + transparent, opaque, auto. + + model: The model to use for image generation. GPT-IMAGE-1 is the latest model offering + the best quality with automatic optimization and enhanced capabilities. + + moderation: The moderation setting for the image generation. GPT-IMAGE-1 supports: low, + auto. + + n: The number of images to generate. GPT-IMAGE-1 only supports n=1. + + output_compression: The output compression for the image generation. GPT-IMAGE-1 supports: 0-100. + + output_format: The output format for the image generation. GPT-IMAGE-1 supports: png, webp, + jpeg. + + partial_images: The number of partial image chunks to return during streaming generation. This + parameter is optional with a default of 0. When stream=true, this must be + greater than 0 to receive progressive updates of the image as it's being + generated. Higher values provide more frequent updates but may increase response + overhead. + + quality: The quality of the image that will be generated. GPT-IMAGE-1 supports: auto + (automatically select best quality), high, medium, low. + + size: The size of the generated images. GPT-IMAGE-1 supports: auto (automatically + select best size), 1536x1024 (landscape), 1024x1536 (portrait). + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def generate( + self, + *, + prompt: str, + stream: bool, + background: Optional[str] | Omit = omit, + model: str | Omit = omit, + moderation: Optional[str] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[str] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[str] | Omit = omit, + size: Optional[str] | Omit = omit, + user: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageGenerateResponse | AsyncStream[ImageGenStreamEvent]: + """ + Creates a high-quality image from a text prompt using GPT-IMAGE-1, the latest + image generation model with automatic prompt optimization and enhanced visual + capabilities. + + Args: + prompt: A text description of the desired image(s). GPT-IMAGE-1 supports up to 32,000 + characters and provides automatic prompt optimization for best results. + + stream: If set to true, partial image data will be streamed as the image is being + generated. When streaming, the response will be sent as server-sent events with + partial image chunks. When stream is true, partial_images must be greater + than 0. + + background: + The background setting for the image generation. GPT-IMAGE-1 supports: + transparent, opaque, auto. + + model: The model to use for image generation. GPT-IMAGE-1 is the latest model offering + the best quality with automatic optimization and enhanced capabilities. + + moderation: The moderation setting for the image generation. GPT-IMAGE-1 supports: low, + auto. + + n: The number of images to generate. GPT-IMAGE-1 only supports n=1. + + output_compression: The output compression for the image generation. GPT-IMAGE-1 supports: 0-100. + + output_format: The output format for the image generation. GPT-IMAGE-1 supports: png, webp, + jpeg. + + partial_images: The number of partial image chunks to return during streaming generation. This + parameter is optional with a default of 0. When stream=true, this must be + greater than 0 to receive progressive updates of the image as it's being + generated. Higher values provide more frequent updates but may increase response + overhead. + + quality: The quality of the image that will be generated. GPT-IMAGE-1 supports: auto + (automatically select best quality), high, medium, low. + + size: The size of the generated images. GPT-IMAGE-1 supports: auto (automatically + select best size), 1536x1024 (landscape), 1024x1536 (portrait). + + user: A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["prompt"], ["prompt", "stream"]) + async def generate( + self, + *, + prompt: str, + background: Optional[str] | Omit = omit, + model: str | Omit = omit, + moderation: Optional[str] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[str] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[str] | Omit = omit, + size: Optional[str] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + user: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageGenerateResponse | AsyncStream[ImageGenStreamEvent]: + return await self._post( + "/images/generations" + if self._client._base_url_overridden + else f"{self._client.inference_endpoint}/v1/images/generations", + body=await async_maybe_transform( + { + "prompt": prompt, + "background": background, + "model": model, + "moderation": moderation, + "n": n, + "output_compression": output_compression, + "output_format": output_format, + "partial_images": partial_images, + "quality": quality, + "size": size, + "stream": stream, + "user": user, + }, + image_generate_params.ImageGenerateParamsStreaming + if stream + else image_generate_params.ImageGenerateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImageGenerateResponse, + stream=stream or False, + stream_cls=AsyncStream[ImageGenStreamEvent], + ) + + +class ImagesResourceWithRawResponse: + def __init__(self, images: ImagesResource) -> None: + self._images = images + + self.generate = to_raw_response_wrapper( + images.generate, + ) + + +class AsyncImagesResourceWithRawResponse: + def __init__(self, images: AsyncImagesResource) -> None: + self._images = images + + self.generate = async_to_raw_response_wrapper( + images.generate, + ) + + +class ImagesResourceWithStreamingResponse: + def __init__(self, images: ImagesResource) -> None: + self._images = images + + self.generate = to_streamed_response_wrapper( + images.generate, + ) + + +class AsyncImagesResourceWithStreamingResponse: + def __init__(self, images: AsyncImagesResource) -> None: + self._images = images + + self.generate = async_to_streamed_response_wrapper( + images.generate, + ) diff --git a/src/gradient/resources/inference/__init__.py b/src/gradient/resources/inference/__init__.py new file mode 100644 index 00000000..21798ab2 --- /dev/null +++ b/src/gradient/resources/inference/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .api_keys import ( + APIKeysResource, + AsyncAPIKeysResource, + APIKeysResourceWithRawResponse, + AsyncAPIKeysResourceWithRawResponse, + APIKeysResourceWithStreamingResponse, + AsyncAPIKeysResourceWithStreamingResponse, +) +from .inference import ( + InferenceResource, + AsyncInferenceResource, + InferenceResourceWithRawResponse, + AsyncInferenceResourceWithRawResponse, + InferenceResourceWithStreamingResponse, + AsyncInferenceResourceWithStreamingResponse, +) + +__all__ = [ + "APIKeysResource", + "AsyncAPIKeysResource", + "APIKeysResourceWithRawResponse", + "AsyncAPIKeysResourceWithRawResponse", + "APIKeysResourceWithStreamingResponse", + "AsyncAPIKeysResourceWithStreamingResponse", + "InferenceResource", + "AsyncInferenceResource", + "InferenceResourceWithRawResponse", + "AsyncInferenceResourceWithRawResponse", + "InferenceResourceWithStreamingResponse", + "AsyncInferenceResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/inference/api_keys.py b/src/gradient/resources/inference/api_keys.py new file mode 100644 index 00000000..8dfa54e1 --- /dev/null +++ b/src/gradient/resources/inference/api_keys.py @@ -0,0 +1,561 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.inference import api_key_list_params, api_key_create_params, api_key_update_params +from ...types.inference.api_key_list_response import APIKeyListResponse +from ...types.inference.api_key_create_response import APIKeyCreateResponse +from ...types.inference.api_key_delete_response import APIKeyDeleteResponse +from ...types.inference.api_key_update_response import APIKeyUpdateResponse +from ...types.inference.api_key_update_regenerate_response import APIKeyUpdateRegenerateResponse + +__all__ = ["APIKeysResource", "AsyncAPIKeysResource"] + + +class APIKeysResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> APIKeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return APIKeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> APIKeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return APIKeysResourceWithStreamingResponse(self) + + def create( + self, + *, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyCreateResponse: + """ + To create a model API key, send a POST request to `/v2/gen-ai/models/api_keys`. + + Args: + name: A human friendly name to identify the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/models/api_keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/models/api_keys", + body=maybe_transform({"name": name}, api_key_create_params.APIKeyCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyCreateResponse, + ) + + def update( + self, + path_api_key_uuid: str, + *, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyUpdateResponse: + """ + To update a model API key, send a PUT request to + `/v2/gen-ai/models/api_keys/{api_key_uuid}`. + + Args: + body_api_key_uuid: API key ID + + name: Name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return self._put( + f"/v2/gen-ai/models/api_keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/models/api_keys/{path_api_key_uuid}", + body=maybe_transform( + { + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + api_key_update_params.APIKeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyListResponse: + """ + To list all model API keys, send a GET request to `/v2/gen-ai/models/api_keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/models/api_keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/models/api_keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + api_key_list_params.APIKeyListParams, + ), + ), + cast_to=APIKeyListResponse, + ) + + def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyDeleteResponse: + """ + To delete an API key for a model, send a DELETE request to + `/v2/gen-ai/models/api_keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._delete( + f"/v2/gen-ai/models/api_keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/models/api_keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyDeleteResponse, + ) + + def update_regenerate( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyUpdateRegenerateResponse: + """ + To regenerate a model API key, send a PUT request to + `/v2/gen-ai/models/api_keys/{api_key_uuid}/regenerate`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._put( + f"/v2/gen-ai/models/api_keys/{api_key_uuid}/regenerate" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/models/api_keys/{api_key_uuid}/regenerate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyUpdateRegenerateResponse, + ) + + +class AsyncAPIKeysResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAPIKeysResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncAPIKeysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAPIKeysResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncAPIKeysResourceWithStreamingResponse(self) + + async def create( + self, + *, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyCreateResponse: + """ + To create a model API key, send a POST request to `/v2/gen-ai/models/api_keys`. + + Args: + name: A human friendly name to identify the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/models/api_keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/models/api_keys", + body=await async_maybe_transform({"name": name}, api_key_create_params.APIKeyCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyCreateResponse, + ) + + async def update( + self, + path_api_key_uuid: str, + *, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyUpdateResponse: + """ + To update a model API key, send a PUT request to + `/v2/gen-ai/models/api_keys/{api_key_uuid}`. + + Args: + body_api_key_uuid: API key ID + + name: Name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return await self._put( + f"/v2/gen-ai/models/api_keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/models/api_keys/{path_api_key_uuid}", + body=await async_maybe_transform( + { + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + api_key_update_params.APIKeyUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyListResponse: + """ + To list all model API keys, send a GET request to `/v2/gen-ai/models/api_keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/models/api_keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/models/api_keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + api_key_list_params.APIKeyListParams, + ), + ), + cast_to=APIKeyListResponse, + ) + + async def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyDeleteResponse: + """ + To delete an API key for a model, send a DELETE request to + `/v2/gen-ai/models/api_keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._delete( + f"/v2/gen-ai/models/api_keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/models/api_keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyDeleteResponse, + ) + + async def update_regenerate( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyUpdateRegenerateResponse: + """ + To regenerate a model API key, send a PUT request to + `/v2/gen-ai/models/api_keys/{api_key_uuid}/regenerate`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._put( + f"/v2/gen-ai/models/api_keys/{api_key_uuid}/regenerate" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/models/api_keys/{api_key_uuid}/regenerate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=APIKeyUpdateRegenerateResponse, + ) + + +class APIKeysResourceWithRawResponse: + def __init__(self, api_keys: APIKeysResource) -> None: + self._api_keys = api_keys + + self.create = to_raw_response_wrapper( + api_keys.create, + ) + self.update = to_raw_response_wrapper( + api_keys.update, + ) + self.list = to_raw_response_wrapper( + api_keys.list, + ) + self.delete = to_raw_response_wrapper( + api_keys.delete, + ) + self.update_regenerate = to_raw_response_wrapper( + api_keys.update_regenerate, + ) + + +class AsyncAPIKeysResourceWithRawResponse: + def __init__(self, api_keys: AsyncAPIKeysResource) -> None: + self._api_keys = api_keys + + self.create = async_to_raw_response_wrapper( + api_keys.create, + ) + self.update = async_to_raw_response_wrapper( + api_keys.update, + ) + self.list = async_to_raw_response_wrapper( + api_keys.list, + ) + self.delete = async_to_raw_response_wrapper( + api_keys.delete, + ) + self.update_regenerate = async_to_raw_response_wrapper( + api_keys.update_regenerate, + ) + + +class APIKeysResourceWithStreamingResponse: + def __init__(self, api_keys: APIKeysResource) -> None: + self._api_keys = api_keys + + self.create = to_streamed_response_wrapper( + api_keys.create, + ) + self.update = to_streamed_response_wrapper( + api_keys.update, + ) + self.list = to_streamed_response_wrapper( + api_keys.list, + ) + self.delete = to_streamed_response_wrapper( + api_keys.delete, + ) + self.update_regenerate = to_streamed_response_wrapper( + api_keys.update_regenerate, + ) + + +class AsyncAPIKeysResourceWithStreamingResponse: + def __init__(self, api_keys: AsyncAPIKeysResource) -> None: + self._api_keys = api_keys + + self.create = async_to_streamed_response_wrapper( + api_keys.create, + ) + self.update = async_to_streamed_response_wrapper( + api_keys.update, + ) + self.list = async_to_streamed_response_wrapper( + api_keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + api_keys.delete, + ) + self.update_regenerate = async_to_streamed_response_wrapper( + api_keys.update_regenerate, + ) diff --git a/src/gradient/resources/inference/inference.py b/src/gradient/resources/inference/inference.py new file mode 100644 index 00000000..d22543b3 --- /dev/null +++ b/src/gradient/resources/inference/inference.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .api_keys import ( + APIKeysResource, + AsyncAPIKeysResource, + APIKeysResourceWithRawResponse, + AsyncAPIKeysResourceWithRawResponse, + APIKeysResourceWithStreamingResponse, + AsyncAPIKeysResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["InferenceResource", "AsyncInferenceResource"] + + +class InferenceResource(SyncAPIResource): + @cached_property + def api_keys(self) -> APIKeysResource: + return APIKeysResource(self._client) + + @cached_property + def with_raw_response(self) -> InferenceResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return InferenceResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InferenceResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return InferenceResourceWithStreamingResponse(self) + + +class AsyncInferenceResource(AsyncAPIResource): + @cached_property + def api_keys(self) -> AsyncAPIKeysResource: + return AsyncAPIKeysResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncInferenceResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncInferenceResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInferenceResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncInferenceResourceWithStreamingResponse(self) + + +class InferenceResourceWithRawResponse: + def __init__(self, inference: InferenceResource) -> None: + self._inference = inference + + @cached_property + def api_keys(self) -> APIKeysResourceWithRawResponse: + return APIKeysResourceWithRawResponse(self._inference.api_keys) + + +class AsyncInferenceResourceWithRawResponse: + def __init__(self, inference: AsyncInferenceResource) -> None: + self._inference = inference + + @cached_property + def api_keys(self) -> AsyncAPIKeysResourceWithRawResponse: + return AsyncAPIKeysResourceWithRawResponse(self._inference.api_keys) + + +class InferenceResourceWithStreamingResponse: + def __init__(self, inference: InferenceResource) -> None: + self._inference = inference + + @cached_property + def api_keys(self) -> APIKeysResourceWithStreamingResponse: + return APIKeysResourceWithStreamingResponse(self._inference.api_keys) + + +class AsyncInferenceResourceWithStreamingResponse: + def __init__(self, inference: AsyncInferenceResource) -> None: + self._inference = inference + + @cached_property + def api_keys(self) -> AsyncAPIKeysResourceWithStreamingResponse: + return AsyncAPIKeysResourceWithStreamingResponse(self._inference.api_keys) diff --git a/src/gradient/resources/knowledge_bases/__init__.py b/src/gradient/resources/knowledge_bases/__init__.py new file mode 100644 index 00000000..80d04328 --- /dev/null +++ b/src/gradient/resources/knowledge_bases/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .data_sources import ( + DataSourcesResource, + AsyncDataSourcesResource, + DataSourcesResourceWithRawResponse, + AsyncDataSourcesResourceWithRawResponse, + DataSourcesResourceWithStreamingResponse, + AsyncDataSourcesResourceWithStreamingResponse, +) +from .indexing_jobs import ( + IndexingJobsResource, + AsyncIndexingJobsResource, + IndexingJobsResourceWithRawResponse, + AsyncIndexingJobsResourceWithRawResponse, + IndexingJobsResourceWithStreamingResponse, + AsyncIndexingJobsResourceWithStreamingResponse, +) +from .knowledge_bases import ( + KnowledgeBasesResource, + AsyncKnowledgeBasesResource, + KnowledgeBasesResourceWithRawResponse, + AsyncKnowledgeBasesResourceWithRawResponse, + KnowledgeBasesResourceWithStreamingResponse, + AsyncKnowledgeBasesResourceWithStreamingResponse, +) + +__all__ = [ + "DataSourcesResource", + "AsyncDataSourcesResource", + "DataSourcesResourceWithRawResponse", + "AsyncDataSourcesResourceWithRawResponse", + "DataSourcesResourceWithStreamingResponse", + "AsyncDataSourcesResourceWithStreamingResponse", + "IndexingJobsResource", + "AsyncIndexingJobsResource", + "IndexingJobsResourceWithRawResponse", + "AsyncIndexingJobsResourceWithRawResponse", + "IndexingJobsResourceWithStreamingResponse", + "AsyncIndexingJobsResourceWithStreamingResponse", + "KnowledgeBasesResource", + "AsyncKnowledgeBasesResource", + "KnowledgeBasesResourceWithRawResponse", + "AsyncKnowledgeBasesResourceWithRawResponse", + "KnowledgeBasesResourceWithStreamingResponse", + "AsyncKnowledgeBasesResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/knowledge_bases/data_sources.py b/src/gradient/resources/knowledge_bases/data_sources.py new file mode 100644 index 00000000..a00d93f5 --- /dev/null +++ b/src/gradient/resources/knowledge_bases/data_sources.py @@ -0,0 +1,533 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.knowledge_bases import ( + data_source_list_params, + data_source_create_params, + data_source_create_presigned_urls_params, +) +from ...types.knowledge_bases.aws_data_source_param import AwsDataSourceParam +from ...types.knowledge_bases.data_source_list_response import DataSourceListResponse +from ...types.knowledge_bases.data_source_create_response import DataSourceCreateResponse +from ...types.knowledge_bases.data_source_delete_response import DataSourceDeleteResponse +from ...types.knowledge_bases.api_spaces_data_source_param import APISpacesDataSourceParam +from ...types.knowledge_bases.api_web_crawler_data_source_param import APIWebCrawlerDataSourceParam +from ...types.knowledge_bases.data_source_create_presigned_urls_response import DataSourceCreatePresignedURLsResponse + +__all__ = ["DataSourcesResource", "AsyncDataSourcesResource"] + + +class DataSourcesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DataSourcesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return DataSourcesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DataSourcesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return DataSourcesResourceWithStreamingResponse(self) + + def create( + self, + path_knowledge_base_uuid: str, + *, + aws_data_source: AwsDataSourceParam | Omit = omit, + body_knowledge_base_uuid: str | Omit = omit, + spaces_data_source: APISpacesDataSourceParam | Omit = omit, + web_crawler_data_source: APIWebCrawlerDataSourceParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DataSourceCreateResponse: + """ + To add a data source to a knowledge base, send a POST request to + `/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources`. + + Args: + aws_data_source: AWS S3 Data Source + + body_knowledge_base_uuid: Knowledge base id + + spaces_data_source: Spaces Bucket Data Source + + web_crawler_data_source: WebCrawlerDataSource + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `path_knowledge_base_uuid` but received {path_knowledge_base_uuid!r}" + ) + return self._post( + f"/v2/gen-ai/knowledge_bases/{path_knowledge_base_uuid}/data_sources" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{path_knowledge_base_uuid}/data_sources", + body=maybe_transform( + { + "aws_data_source": aws_data_source, + "body_knowledge_base_uuid": body_knowledge_base_uuid, + "spaces_data_source": spaces_data_source, + "web_crawler_data_source": web_crawler_data_source, + }, + data_source_create_params.DataSourceCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DataSourceCreateResponse, + ) + + def list( + self, + knowledge_base_uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DataSourceListResponse: + """ + To list all data sources for a knowledge base, send a GET request to + `/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return self._get( + f"/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + data_source_list_params.DataSourceListParams, + ), + ), + cast_to=DataSourceListResponse, + ) + + def delete( + self, + data_source_uuid: str, + *, + knowledge_base_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DataSourceDeleteResponse: + """ + To delete a data source from a knowledge base, send a DELETE request to + `/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources/{data_source_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + if not data_source_uuid: + raise ValueError(f"Expected a non-empty value for `data_source_uuid` but received {data_source_uuid!r}") + return self._delete( + f"/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources/{data_source_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources/{data_source_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DataSourceDeleteResponse, + ) + + def create_presigned_urls( + self, + *, + files: Iterable[data_source_create_presigned_urls_params.File] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DataSourceCreatePresignedURLsResponse: + """ + To create presigned URLs for knowledge base data source file upload, send a POST + request to `/v2/gen-ai/knowledge_bases/data_sources/file_upload_presigned_urls`. + + Args: + files: A list of files to generate presigned URLs for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/knowledge_bases/data_sources/file_upload_presigned_urls" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/knowledge_bases/data_sources/file_upload_presigned_urls", + body=maybe_transform( + {"files": files}, data_source_create_presigned_urls_params.DataSourceCreatePresignedURLsParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DataSourceCreatePresignedURLsResponse, + ) + + +class AsyncDataSourcesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDataSourcesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncDataSourcesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDataSourcesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncDataSourcesResourceWithStreamingResponse(self) + + async def create( + self, + path_knowledge_base_uuid: str, + *, + aws_data_source: AwsDataSourceParam | Omit = omit, + body_knowledge_base_uuid: str | Omit = omit, + spaces_data_source: APISpacesDataSourceParam | Omit = omit, + web_crawler_data_source: APIWebCrawlerDataSourceParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DataSourceCreateResponse: + """ + To add a data source to a knowledge base, send a POST request to + `/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources`. + + Args: + aws_data_source: AWS S3 Data Source + + body_knowledge_base_uuid: Knowledge base id + + spaces_data_source: Spaces Bucket Data Source + + web_crawler_data_source: WebCrawlerDataSource + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `path_knowledge_base_uuid` but received {path_knowledge_base_uuid!r}" + ) + return await self._post( + f"/v2/gen-ai/knowledge_bases/{path_knowledge_base_uuid}/data_sources" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{path_knowledge_base_uuid}/data_sources", + body=await async_maybe_transform( + { + "aws_data_source": aws_data_source, + "body_knowledge_base_uuid": body_knowledge_base_uuid, + "spaces_data_source": spaces_data_source, + "web_crawler_data_source": web_crawler_data_source, + }, + data_source_create_params.DataSourceCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DataSourceCreateResponse, + ) + + async def list( + self, + knowledge_base_uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DataSourceListResponse: + """ + To list all data sources for a knowledge base, send a GET request to + `/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return await self._get( + f"/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + data_source_list_params.DataSourceListParams, + ), + ), + cast_to=DataSourceListResponse, + ) + + async def delete( + self, + data_source_uuid: str, + *, + knowledge_base_uuid: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DataSourceDeleteResponse: + """ + To delete a data source from a knowledge base, send a DELETE request to + `/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources/{data_source_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + if not data_source_uuid: + raise ValueError(f"Expected a non-empty value for `data_source_uuid` but received {data_source_uuid!r}") + return await self._delete( + f"/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources/{data_source_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/data_sources/{data_source_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DataSourceDeleteResponse, + ) + + async def create_presigned_urls( + self, + *, + files: Iterable[data_source_create_presigned_urls_params.File] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DataSourceCreatePresignedURLsResponse: + """ + To create presigned URLs for knowledge base data source file upload, send a POST + request to `/v2/gen-ai/knowledge_bases/data_sources/file_upload_presigned_urls`. + + Args: + files: A list of files to generate presigned URLs for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/knowledge_bases/data_sources/file_upload_presigned_urls" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/knowledge_bases/data_sources/file_upload_presigned_urls", + body=await async_maybe_transform( + {"files": files}, data_source_create_presigned_urls_params.DataSourceCreatePresignedURLsParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DataSourceCreatePresignedURLsResponse, + ) + + +class DataSourcesResourceWithRawResponse: + def __init__(self, data_sources: DataSourcesResource) -> None: + self._data_sources = data_sources + + self.create = to_raw_response_wrapper( + data_sources.create, + ) + self.list = to_raw_response_wrapper( + data_sources.list, + ) + self.delete = to_raw_response_wrapper( + data_sources.delete, + ) + self.create_presigned_urls = to_raw_response_wrapper( + data_sources.create_presigned_urls, + ) + + +class AsyncDataSourcesResourceWithRawResponse: + def __init__(self, data_sources: AsyncDataSourcesResource) -> None: + self._data_sources = data_sources + + self.create = async_to_raw_response_wrapper( + data_sources.create, + ) + self.list = async_to_raw_response_wrapper( + data_sources.list, + ) + self.delete = async_to_raw_response_wrapper( + data_sources.delete, + ) + self.create_presigned_urls = async_to_raw_response_wrapper( + data_sources.create_presigned_urls, + ) + + +class DataSourcesResourceWithStreamingResponse: + def __init__(self, data_sources: DataSourcesResource) -> None: + self._data_sources = data_sources + + self.create = to_streamed_response_wrapper( + data_sources.create, + ) + self.list = to_streamed_response_wrapper( + data_sources.list, + ) + self.delete = to_streamed_response_wrapper( + data_sources.delete, + ) + self.create_presigned_urls = to_streamed_response_wrapper( + data_sources.create_presigned_urls, + ) + + +class AsyncDataSourcesResourceWithStreamingResponse: + def __init__(self, data_sources: AsyncDataSourcesResource) -> None: + self._data_sources = data_sources + + self.create = async_to_streamed_response_wrapper( + data_sources.create, + ) + self.list = async_to_streamed_response_wrapper( + data_sources.list, + ) + self.delete = async_to_streamed_response_wrapper( + data_sources.delete, + ) + self.create_presigned_urls = async_to_streamed_response_wrapper( + data_sources.create_presigned_urls, + ) diff --git a/src/gradient/resources/knowledge_bases/indexing_jobs.py b/src/gradient/resources/knowledge_bases/indexing_jobs.py new file mode 100644 index 00000000..4c222bcf --- /dev/null +++ b/src/gradient/resources/knowledge_bases/indexing_jobs.py @@ -0,0 +1,660 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.knowledge_bases import ( + indexing_job_list_params, + indexing_job_create_params, + indexing_job_update_cancel_params, +) +from ...types.knowledge_bases.indexing_job_list_response import IndexingJobListResponse +from ...types.knowledge_bases.indexing_job_create_response import IndexingJobCreateResponse +from ...types.knowledge_bases.indexing_job_retrieve_response import IndexingJobRetrieveResponse +from ...types.knowledge_bases.indexing_job_update_cancel_response import IndexingJobUpdateCancelResponse +from ...types.knowledge_bases.indexing_job_retrieve_signed_url_response import IndexingJobRetrieveSignedURLResponse +from ...types.knowledge_bases.indexing_job_retrieve_data_sources_response import IndexingJobRetrieveDataSourcesResponse + +__all__ = ["IndexingJobsResource", "AsyncIndexingJobsResource"] + + +class IndexingJobsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> IndexingJobsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return IndexingJobsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> IndexingJobsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return IndexingJobsResourceWithStreamingResponse(self) + + def create( + self, + *, + data_source_uuids: SequenceNotStr[str] | Omit = omit, + knowledge_base_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobCreateResponse: + """ + To start an indexing job for a knowledge base, send a POST request to + `/v2/gen-ai/indexing_jobs`. + + Args: + data_source_uuids: List of data source ids to index, if none are provided, all data sources will be + indexed + + knowledge_base_uuid: Knowledge base id + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/indexing_jobs" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/indexing_jobs", + body=maybe_transform( + { + "data_source_uuids": data_source_uuids, + "knowledge_base_uuid": knowledge_base_uuid, + }, + indexing_job_create_params.IndexingJobCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobCreateResponse, + ) + + def retrieve( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobRetrieveResponse: + """ + To get status of an indexing Job for a knowledge base, send a GET request to + `/v2/gen-ai/indexing_jobs/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/indexing_jobs/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/indexing_jobs/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobRetrieveResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobListResponse: + """ + To list all indexing jobs for a knowledge base, send a GET request to + `/v2/gen-ai/indexing_jobs`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/indexing_jobs" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/indexing_jobs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + indexing_job_list_params.IndexingJobListParams, + ), + ), + cast_to=IndexingJobListResponse, + ) + + def retrieve_data_sources( + self, + indexing_job_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobRetrieveDataSourcesResponse: + """ + To list all datasources for an indexing job, send a GET request to + `/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/data_sources`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not indexing_job_uuid: + raise ValueError(f"Expected a non-empty value for `indexing_job_uuid` but received {indexing_job_uuid!r}") + return self._get( + f"/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/data_sources" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/data_sources", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobRetrieveDataSourcesResponse, + ) + + def retrieve_signed_url( + self, + indexing_job_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobRetrieveSignedURLResponse: + """ + To get a signed URL for indexing job details, send a GET request to + `/v2/gen-ai/indexing_jobs/{uuid}/details_signed_url`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not indexing_job_uuid: + raise ValueError(f"Expected a non-empty value for `indexing_job_uuid` but received {indexing_job_uuid!r}") + return self._get( + f"/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/details_signed_url" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/details_signed_url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobRetrieveSignedURLResponse, + ) + + def update_cancel( + self, + path_uuid: str, + *, + body_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobUpdateCancelResponse: + """ + To cancel an indexing job for a knowledge base, send a PUT request to + `/v2/gen-ai/indexing_jobs/{uuid}/cancel`. + + Args: + body_uuid: A unique identifier for an indexing job. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return self._put( + f"/v2/gen-ai/indexing_jobs/{path_uuid}/cancel" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/indexing_jobs/{path_uuid}/cancel", + body=maybe_transform( + {"body_uuid": body_uuid}, indexing_job_update_cancel_params.IndexingJobUpdateCancelParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobUpdateCancelResponse, + ) + + +class AsyncIndexingJobsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncIndexingJobsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncIndexingJobsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncIndexingJobsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncIndexingJobsResourceWithStreamingResponse(self) + + async def create( + self, + *, + data_source_uuids: SequenceNotStr[str] | Omit = omit, + knowledge_base_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobCreateResponse: + """ + To start an indexing job for a knowledge base, send a POST request to + `/v2/gen-ai/indexing_jobs`. + + Args: + data_source_uuids: List of data source ids to index, if none are provided, all data sources will be + indexed + + knowledge_base_uuid: Knowledge base id + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/indexing_jobs" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/indexing_jobs", + body=await async_maybe_transform( + { + "data_source_uuids": data_source_uuids, + "knowledge_base_uuid": knowledge_base_uuid, + }, + indexing_job_create_params.IndexingJobCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobCreateResponse, + ) + + async def retrieve( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobRetrieveResponse: + """ + To get status of an indexing Job for a knowledge base, send a GET request to + `/v2/gen-ai/indexing_jobs/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/indexing_jobs/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/indexing_jobs/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobRetrieveResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobListResponse: + """ + To list all indexing jobs for a knowledge base, send a GET request to + `/v2/gen-ai/indexing_jobs`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/indexing_jobs" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/indexing_jobs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + indexing_job_list_params.IndexingJobListParams, + ), + ), + cast_to=IndexingJobListResponse, + ) + + async def retrieve_data_sources( + self, + indexing_job_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobRetrieveDataSourcesResponse: + """ + To list all datasources for an indexing job, send a GET request to + `/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/data_sources`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not indexing_job_uuid: + raise ValueError(f"Expected a non-empty value for `indexing_job_uuid` but received {indexing_job_uuid!r}") + return await self._get( + f"/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/data_sources" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/data_sources", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobRetrieveDataSourcesResponse, + ) + + async def retrieve_signed_url( + self, + indexing_job_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobRetrieveSignedURLResponse: + """ + To get a signed URL for indexing job details, send a GET request to + `/v2/gen-ai/indexing_jobs/{uuid}/details_signed_url`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not indexing_job_uuid: + raise ValueError(f"Expected a non-empty value for `indexing_job_uuid` but received {indexing_job_uuid!r}") + return await self._get( + f"/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/details_signed_url" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/indexing_jobs/{indexing_job_uuid}/details_signed_url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobRetrieveSignedURLResponse, + ) + + async def update_cancel( + self, + path_uuid: str, + *, + body_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> IndexingJobUpdateCancelResponse: + """ + To cancel an indexing job for a knowledge base, send a PUT request to + `/v2/gen-ai/indexing_jobs/{uuid}/cancel`. + + Args: + body_uuid: A unique identifier for an indexing job. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return await self._put( + f"/v2/gen-ai/indexing_jobs/{path_uuid}/cancel" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/indexing_jobs/{path_uuid}/cancel", + body=await async_maybe_transform( + {"body_uuid": body_uuid}, indexing_job_update_cancel_params.IndexingJobUpdateCancelParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=IndexingJobUpdateCancelResponse, + ) + + +class IndexingJobsResourceWithRawResponse: + def __init__(self, indexing_jobs: IndexingJobsResource) -> None: + self._indexing_jobs = indexing_jobs + + self.create = to_raw_response_wrapper( + indexing_jobs.create, + ) + self.retrieve = to_raw_response_wrapper( + indexing_jobs.retrieve, + ) + self.list = to_raw_response_wrapper( + indexing_jobs.list, + ) + self.retrieve_data_sources = to_raw_response_wrapper( + indexing_jobs.retrieve_data_sources, + ) + self.retrieve_signed_url = to_raw_response_wrapper( + indexing_jobs.retrieve_signed_url, + ) + self.update_cancel = to_raw_response_wrapper( + indexing_jobs.update_cancel, + ) + + +class AsyncIndexingJobsResourceWithRawResponse: + def __init__(self, indexing_jobs: AsyncIndexingJobsResource) -> None: + self._indexing_jobs = indexing_jobs + + self.create = async_to_raw_response_wrapper( + indexing_jobs.create, + ) + self.retrieve = async_to_raw_response_wrapper( + indexing_jobs.retrieve, + ) + self.list = async_to_raw_response_wrapper( + indexing_jobs.list, + ) + self.retrieve_data_sources = async_to_raw_response_wrapper( + indexing_jobs.retrieve_data_sources, + ) + self.retrieve_signed_url = async_to_raw_response_wrapper( + indexing_jobs.retrieve_signed_url, + ) + self.update_cancel = async_to_raw_response_wrapper( + indexing_jobs.update_cancel, + ) + + +class IndexingJobsResourceWithStreamingResponse: + def __init__(self, indexing_jobs: IndexingJobsResource) -> None: + self._indexing_jobs = indexing_jobs + + self.create = to_streamed_response_wrapper( + indexing_jobs.create, + ) + self.retrieve = to_streamed_response_wrapper( + indexing_jobs.retrieve, + ) + self.list = to_streamed_response_wrapper( + indexing_jobs.list, + ) + self.retrieve_data_sources = to_streamed_response_wrapper( + indexing_jobs.retrieve_data_sources, + ) + self.retrieve_signed_url = to_streamed_response_wrapper( + indexing_jobs.retrieve_signed_url, + ) + self.update_cancel = to_streamed_response_wrapper( + indexing_jobs.update_cancel, + ) + + +class AsyncIndexingJobsResourceWithStreamingResponse: + def __init__(self, indexing_jobs: AsyncIndexingJobsResource) -> None: + self._indexing_jobs = indexing_jobs + + self.create = async_to_streamed_response_wrapper( + indexing_jobs.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + indexing_jobs.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + indexing_jobs.list, + ) + self.retrieve_data_sources = async_to_streamed_response_wrapper( + indexing_jobs.retrieve_data_sources, + ) + self.retrieve_signed_url = async_to_streamed_response_wrapper( + indexing_jobs.retrieve_signed_url, + ) + self.update_cancel = async_to_streamed_response_wrapper( + indexing_jobs.update_cancel, + ) diff --git a/src/gradient/resources/knowledge_bases/knowledge_bases.py b/src/gradient/resources/knowledge_bases/knowledge_bases.py new file mode 100644 index 00000000..b297280f --- /dev/null +++ b/src/gradient/resources/knowledge_bases/knowledge_bases.py @@ -0,0 +1,824 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ...types import knowledge_base_list_params, knowledge_base_create_params, knowledge_base_update_params +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .data_sources import ( + DataSourcesResource, + AsyncDataSourcesResource, + DataSourcesResourceWithRawResponse, + AsyncDataSourcesResourceWithRawResponse, + DataSourcesResourceWithStreamingResponse, + AsyncDataSourcesResourceWithStreamingResponse, +) +from .indexing_jobs import ( + IndexingJobsResource, + AsyncIndexingJobsResource, + IndexingJobsResourceWithRawResponse, + AsyncIndexingJobsResourceWithRawResponse, + IndexingJobsResourceWithStreamingResponse, + AsyncIndexingJobsResourceWithStreamingResponse, +) +from ..._base_client import make_request_options +from ...types.knowledge_base_list_response import KnowledgeBaseListResponse +from ...types.knowledge_base_create_response import KnowledgeBaseCreateResponse +from ...types.knowledge_base_delete_response import KnowledgeBaseDeleteResponse +from ...types.knowledge_base_update_response import KnowledgeBaseUpdateResponse +from ...types.knowledge_base_retrieve_response import KnowledgeBaseRetrieveResponse +from ...types.knowledge_base_list_indexing_jobs_response import KnowledgeBaseListIndexingJobsResponse + +__all__ = ["KnowledgeBasesResource", "AsyncKnowledgeBasesResource"] + + +class KnowledgeBasesResource(SyncAPIResource): + @cached_property + def data_sources(self) -> DataSourcesResource: + return DataSourcesResource(self._client) + + @cached_property + def indexing_jobs(self) -> IndexingJobsResource: + return IndexingJobsResource(self._client) + + @cached_property + def with_raw_response(self) -> KnowledgeBasesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return KnowledgeBasesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> KnowledgeBasesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return KnowledgeBasesResourceWithStreamingResponse(self) + + def create( + self, + *, + database_id: str | Omit = omit, + datasources: Iterable[knowledge_base_create_params.Datasource] | Omit = omit, + embedding_model_uuid: str | Omit = omit, + name: str | Omit = omit, + project_id: str | Omit = omit, + region: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseCreateResponse: + """ + To create a knowledge base, send a POST request to `/v2/gen-ai/knowledge_bases`. + + Args: + database_id: Identifier of the DigitalOcean OpenSearch database this knowledge base will use, + optional. If not provided, we create a new database for the knowledge base in + the same region as the knowledge base. + + datasources: The data sources to use for this knowledge base. See + [Organize Data Sources](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#spaces-buckets) + for more information on data sources best practices. + + embedding_model_uuid: Identifier for the + [embedding model](https://docs.digitalocean.com/products/genai-platform/details/models/#embedding-models). + + name: Name of the knowledge base. + + project_id: Identifier of the DigitalOcean project this knowledge base will belong to. + + region: The datacenter region to deploy the knowledge base in. + + tags: Tags to organize your knowledge base. + + vpc_uuid: The VPC to deploy the knowledge base database in + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/knowledge_bases" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/knowledge_bases", + body=maybe_transform( + { + "database_id": database_id, + "datasources": datasources, + "embedding_model_uuid": embedding_model_uuid, + "name": name, + "project_id": project_id, + "region": region, + "tags": tags, + "vpc_uuid": vpc_uuid, + }, + knowledge_base_create_params.KnowledgeBaseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseCreateResponse, + ) + + def retrieve( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseRetrieveResponse: + """ + To retrive information about an existing knowledge base, send a GET request to + `/v2/gen-ai/knowledge_bases/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/knowledge_bases/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseRetrieveResponse, + ) + + def update( + self, + path_uuid: str, + *, + database_id: str | Omit = omit, + embedding_model_uuid: str | Omit = omit, + name: str | Omit = omit, + project_id: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + body_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseUpdateResponse: + """ + To update a knowledge base, send a PUT request to + `/v2/gen-ai/knowledge_bases/{uuid}`. + + Args: + database_id: The id of the DigitalOcean database this knowledge base will use, optiona. + + embedding_model_uuid: Identifier for the foundation model. + + name: Knowledge base name + + project_id: The id of the DigitalOcean project this knowledge base will belong to + + tags: Tags to organize your knowledge base. + + body_uuid: Knowledge base id + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return self._put( + f"/v2/gen-ai/knowledge_bases/{path_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{path_uuid}", + body=maybe_transform( + { + "database_id": database_id, + "embedding_model_uuid": embedding_model_uuid, + "name": name, + "project_id": project_id, + "tags": tags, + "body_uuid": body_uuid, + }, + knowledge_base_update_params.KnowledgeBaseUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseListResponse: + """ + To list all knowledge bases, send a GET request to `/v2/gen-ai/knowledge_bases`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/knowledge_bases" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/knowledge_bases", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + knowledge_base_list_params.KnowledgeBaseListParams, + ), + ), + cast_to=KnowledgeBaseListResponse, + ) + + def delete( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseDeleteResponse: + """ + To delete a knowledge base, send a DELETE request to + `/v2/gen-ai/knowledge_bases/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._delete( + f"/v2/gen-ai/knowledge_bases/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseDeleteResponse, + ) + + def list_indexing_jobs( + self, + knowledge_base_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseListIndexingJobsResponse: + """ + To list latest 15 indexing jobs for a knowledge base, send a GET request to + `/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/indexing_jobs`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return self._get( + f"/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/indexing_jobs" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/indexing_jobs", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseListIndexingJobsResponse, + ) + + +class AsyncKnowledgeBasesResource(AsyncAPIResource): + @cached_property + def data_sources(self) -> AsyncDataSourcesResource: + return AsyncDataSourcesResource(self._client) + + @cached_property + def indexing_jobs(self) -> AsyncIndexingJobsResource: + return AsyncIndexingJobsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncKnowledgeBasesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncKnowledgeBasesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncKnowledgeBasesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncKnowledgeBasesResourceWithStreamingResponse(self) + + async def create( + self, + *, + database_id: str | Omit = omit, + datasources: Iterable[knowledge_base_create_params.Datasource] | Omit = omit, + embedding_model_uuid: str | Omit = omit, + name: str | Omit = omit, + project_id: str | Omit = omit, + region: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + vpc_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseCreateResponse: + """ + To create a knowledge base, send a POST request to `/v2/gen-ai/knowledge_bases`. + + Args: + database_id: Identifier of the DigitalOcean OpenSearch database this knowledge base will use, + optional. If not provided, we create a new database for the knowledge base in + the same region as the knowledge base. + + datasources: The data sources to use for this knowledge base. See + [Organize Data Sources](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#spaces-buckets) + for more information on data sources best practices. + + embedding_model_uuid: Identifier for the + [embedding model](https://docs.digitalocean.com/products/genai-platform/details/models/#embedding-models). + + name: Name of the knowledge base. + + project_id: Identifier of the DigitalOcean project this knowledge base will belong to. + + region: The datacenter region to deploy the knowledge base in. + + tags: Tags to organize your knowledge base. + + vpc_uuid: The VPC to deploy the knowledge base database in + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/knowledge_bases" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/knowledge_bases", + body=await async_maybe_transform( + { + "database_id": database_id, + "datasources": datasources, + "embedding_model_uuid": embedding_model_uuid, + "name": name, + "project_id": project_id, + "region": region, + "tags": tags, + "vpc_uuid": vpc_uuid, + }, + knowledge_base_create_params.KnowledgeBaseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseCreateResponse, + ) + + async def retrieve( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseRetrieveResponse: + """ + To retrive information about an existing knowledge base, send a GET request to + `/v2/gen-ai/knowledge_bases/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/knowledge_bases/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseRetrieveResponse, + ) + + async def update( + self, + path_uuid: str, + *, + database_id: str | Omit = omit, + embedding_model_uuid: str | Omit = omit, + name: str | Omit = omit, + project_id: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + body_uuid: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseUpdateResponse: + """ + To update a knowledge base, send a PUT request to + `/v2/gen-ai/knowledge_bases/{uuid}`. + + Args: + database_id: The id of the DigitalOcean database this knowledge base will use, optiona. + + embedding_model_uuid: Identifier for the foundation model. + + name: Knowledge base name + + project_id: The id of the DigitalOcean project this knowledge base will belong to + + tags: Tags to organize your knowledge base. + + body_uuid: Knowledge base id + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_uuid: + raise ValueError(f"Expected a non-empty value for `path_uuid` but received {path_uuid!r}") + return await self._put( + f"/v2/gen-ai/knowledge_bases/{path_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{path_uuid}", + body=await async_maybe_transform( + { + "database_id": database_id, + "embedding_model_uuid": embedding_model_uuid, + "name": name, + "project_id": project_id, + "tags": tags, + "body_uuid": body_uuid, + }, + knowledge_base_update_params.KnowledgeBaseUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseListResponse: + """ + To list all knowledge bases, send a GET request to `/v2/gen-ai/knowledge_bases`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/knowledge_bases" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/knowledge_bases", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + knowledge_base_list_params.KnowledgeBaseListParams, + ), + ), + cast_to=KnowledgeBaseListResponse, + ) + + async def delete( + self, + uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseDeleteResponse: + """ + To delete a knowledge base, send a DELETE request to + `/v2/gen-ai/knowledge_bases/{uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._delete( + f"/v2/gen-ai/knowledge_bases/{uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseDeleteResponse, + ) + + async def list_indexing_jobs( + self, + knowledge_base_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> KnowledgeBaseListIndexingJobsResponse: + """ + To list latest 15 indexing jobs for a knowledge base, send a GET request to + `/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/indexing_jobs`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not knowledge_base_uuid: + raise ValueError( + f"Expected a non-empty value for `knowledge_base_uuid` but received {knowledge_base_uuid!r}" + ) + return await self._get( + f"/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/indexing_jobs" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/knowledge_bases/{knowledge_base_uuid}/indexing_jobs", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KnowledgeBaseListIndexingJobsResponse, + ) + + +class KnowledgeBasesResourceWithRawResponse: + def __init__(self, knowledge_bases: KnowledgeBasesResource) -> None: + self._knowledge_bases = knowledge_bases + + self.create = to_raw_response_wrapper( + knowledge_bases.create, + ) + self.retrieve = to_raw_response_wrapper( + knowledge_bases.retrieve, + ) + self.update = to_raw_response_wrapper( + knowledge_bases.update, + ) + self.list = to_raw_response_wrapper( + knowledge_bases.list, + ) + self.delete = to_raw_response_wrapper( + knowledge_bases.delete, + ) + self.list_indexing_jobs = to_raw_response_wrapper( + knowledge_bases.list_indexing_jobs, + ) + + @cached_property + def data_sources(self) -> DataSourcesResourceWithRawResponse: + return DataSourcesResourceWithRawResponse(self._knowledge_bases.data_sources) + + @cached_property + def indexing_jobs(self) -> IndexingJobsResourceWithRawResponse: + return IndexingJobsResourceWithRawResponse(self._knowledge_bases.indexing_jobs) + + +class AsyncKnowledgeBasesResourceWithRawResponse: + def __init__(self, knowledge_bases: AsyncKnowledgeBasesResource) -> None: + self._knowledge_bases = knowledge_bases + + self.create = async_to_raw_response_wrapper( + knowledge_bases.create, + ) + self.retrieve = async_to_raw_response_wrapper( + knowledge_bases.retrieve, + ) + self.update = async_to_raw_response_wrapper( + knowledge_bases.update, + ) + self.list = async_to_raw_response_wrapper( + knowledge_bases.list, + ) + self.delete = async_to_raw_response_wrapper( + knowledge_bases.delete, + ) + self.list_indexing_jobs = async_to_raw_response_wrapper( + knowledge_bases.list_indexing_jobs, + ) + + @cached_property + def data_sources(self) -> AsyncDataSourcesResourceWithRawResponse: + return AsyncDataSourcesResourceWithRawResponse(self._knowledge_bases.data_sources) + + @cached_property + def indexing_jobs(self) -> AsyncIndexingJobsResourceWithRawResponse: + return AsyncIndexingJobsResourceWithRawResponse(self._knowledge_bases.indexing_jobs) + + +class KnowledgeBasesResourceWithStreamingResponse: + def __init__(self, knowledge_bases: KnowledgeBasesResource) -> None: + self._knowledge_bases = knowledge_bases + + self.create = to_streamed_response_wrapper( + knowledge_bases.create, + ) + self.retrieve = to_streamed_response_wrapper( + knowledge_bases.retrieve, + ) + self.update = to_streamed_response_wrapper( + knowledge_bases.update, + ) + self.list = to_streamed_response_wrapper( + knowledge_bases.list, + ) + self.delete = to_streamed_response_wrapper( + knowledge_bases.delete, + ) + self.list_indexing_jobs = to_streamed_response_wrapper( + knowledge_bases.list_indexing_jobs, + ) + + @cached_property + def data_sources(self) -> DataSourcesResourceWithStreamingResponse: + return DataSourcesResourceWithStreamingResponse(self._knowledge_bases.data_sources) + + @cached_property + def indexing_jobs(self) -> IndexingJobsResourceWithStreamingResponse: + return IndexingJobsResourceWithStreamingResponse(self._knowledge_bases.indexing_jobs) + + +class AsyncKnowledgeBasesResourceWithStreamingResponse: + def __init__(self, knowledge_bases: AsyncKnowledgeBasesResource) -> None: + self._knowledge_bases = knowledge_bases + + self.create = async_to_streamed_response_wrapper( + knowledge_bases.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + knowledge_bases.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + knowledge_bases.update, + ) + self.list = async_to_streamed_response_wrapper( + knowledge_bases.list, + ) + self.delete = async_to_streamed_response_wrapper( + knowledge_bases.delete, + ) + self.list_indexing_jobs = async_to_streamed_response_wrapper( + knowledge_bases.list_indexing_jobs, + ) + + @cached_property + def data_sources(self) -> AsyncDataSourcesResourceWithStreamingResponse: + return AsyncDataSourcesResourceWithStreamingResponse(self._knowledge_bases.data_sources) + + @cached_property + def indexing_jobs(self) -> AsyncIndexingJobsResourceWithStreamingResponse: + return AsyncIndexingJobsResourceWithStreamingResponse(self._knowledge_bases.indexing_jobs) diff --git a/src/gradient/resources/models/__init__.py b/src/gradient/resources/models/__init__.py new file mode 100644 index 00000000..e30dd201 --- /dev/null +++ b/src/gradient/resources/models/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .models import ( + ModelsResource, + AsyncModelsResource, + ModelsResourceWithRawResponse, + AsyncModelsResourceWithRawResponse, + ModelsResourceWithStreamingResponse, + AsyncModelsResourceWithStreamingResponse, +) +from .providers import ( + ProvidersResource, + AsyncProvidersResource, + ProvidersResourceWithRawResponse, + AsyncProvidersResourceWithRawResponse, + ProvidersResourceWithStreamingResponse, + AsyncProvidersResourceWithStreamingResponse, +) + +__all__ = [ + "ProvidersResource", + "AsyncProvidersResource", + "ProvidersResourceWithRawResponse", + "AsyncProvidersResourceWithRawResponse", + "ProvidersResourceWithStreamingResponse", + "AsyncProvidersResourceWithStreamingResponse", + "ModelsResource", + "AsyncModelsResource", + "ModelsResourceWithRawResponse", + "AsyncModelsResourceWithRawResponse", + "ModelsResourceWithStreamingResponse", + "AsyncModelsResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/models/models.py b/src/gradient/resources/models/models.py new file mode 100644 index 00000000..650c49c9 --- /dev/null +++ b/src/gradient/resources/models/models.py @@ -0,0 +1,286 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx + +from ...types import model_list_params +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from .providers.providers import ( + ProvidersResource, + AsyncProvidersResource, + ProvidersResourceWithRawResponse, + AsyncProvidersResourceWithRawResponse, + ProvidersResourceWithStreamingResponse, + AsyncProvidersResourceWithStreamingResponse, +) +from ...types.model_list_response import ModelListResponse + +__all__ = ["ModelsResource", "AsyncModelsResource"] + + +class ModelsResource(SyncAPIResource): + @cached_property + def providers(self) -> ProvidersResource: + return ProvidersResource(self._client) + + @cached_property + def with_raw_response(self) -> ModelsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ModelsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModelsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ModelsResourceWithStreamingResponse(self) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + public_only: bool | Omit = omit, + usecases: List[ + Literal[ + "MODEL_USECASE_UNKNOWN", + "MODEL_USECASE_AGENT", + "MODEL_USECASE_FINETUNED", + "MODEL_USECASE_KNOWLEDGEBASE", + "MODEL_USECASE_GUARDRAIL", + "MODEL_USECASE_REASONING", + "MODEL_USECASE_SERVERLESS", + ] + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ModelListResponse: + """ + To list all models, send a GET request to `/v2/gen-ai/models`. + + Args: + page: Page number. + + per_page: Items per page. + + public_only: Only include models that are publicly available. + + usecases: Include only models defined for the listed usecases. + + - MODEL_USECASE_UNKNOWN: The use case of the model is unknown + - MODEL_USECASE_AGENT: The model maybe used in an agent + - MODEL_USECASE_FINETUNED: The model maybe used for fine tuning + - MODEL_USECASE_KNOWLEDGEBASE: The model maybe used for knowledge bases + (embedding models) + - MODEL_USECASE_GUARDRAIL: The model maybe used for guardrails + - MODEL_USECASE_REASONING: The model usecase for reasoning + - MODEL_USECASE_SERVERLESS: The model usecase for serverless inference + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/models" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/models", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + "public_only": public_only, + "usecases": usecases, + }, + model_list_params.ModelListParams, + ), + ), + cast_to=ModelListResponse, + ) + + +class AsyncModelsResource(AsyncAPIResource): + @cached_property + def providers(self) -> AsyncProvidersResource: + return AsyncProvidersResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncModelsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncModelsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModelsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncModelsResourceWithStreamingResponse(self) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + public_only: bool | Omit = omit, + usecases: List[ + Literal[ + "MODEL_USECASE_UNKNOWN", + "MODEL_USECASE_AGENT", + "MODEL_USECASE_FINETUNED", + "MODEL_USECASE_KNOWLEDGEBASE", + "MODEL_USECASE_GUARDRAIL", + "MODEL_USECASE_REASONING", + "MODEL_USECASE_SERVERLESS", + ] + ] + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ModelListResponse: + """ + To list all models, send a GET request to `/v2/gen-ai/models`. + + Args: + page: Page number. + + per_page: Items per page. + + public_only: Only include models that are publicly available. + + usecases: Include only models defined for the listed usecases. + + - MODEL_USECASE_UNKNOWN: The use case of the model is unknown + - MODEL_USECASE_AGENT: The model maybe used in an agent + - MODEL_USECASE_FINETUNED: The model maybe used for fine tuning + - MODEL_USECASE_KNOWLEDGEBASE: The model maybe used for knowledge bases + (embedding models) + - MODEL_USECASE_GUARDRAIL: The model maybe used for guardrails + - MODEL_USECASE_REASONING: The model usecase for reasoning + - MODEL_USECASE_SERVERLESS: The model usecase for serverless inference + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/models" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/models", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + "public_only": public_only, + "usecases": usecases, + }, + model_list_params.ModelListParams, + ), + ), + cast_to=ModelListResponse, + ) + + +class ModelsResourceWithRawResponse: + def __init__(self, models: ModelsResource) -> None: + self._models = models + + self.list = to_raw_response_wrapper( + models.list, + ) + + @cached_property + def providers(self) -> ProvidersResourceWithRawResponse: + return ProvidersResourceWithRawResponse(self._models.providers) + + +class AsyncModelsResourceWithRawResponse: + def __init__(self, models: AsyncModelsResource) -> None: + self._models = models + + self.list = async_to_raw_response_wrapper( + models.list, + ) + + @cached_property + def providers(self) -> AsyncProvidersResourceWithRawResponse: + return AsyncProvidersResourceWithRawResponse(self._models.providers) + + +class ModelsResourceWithStreamingResponse: + def __init__(self, models: ModelsResource) -> None: + self._models = models + + self.list = to_streamed_response_wrapper( + models.list, + ) + + @cached_property + def providers(self) -> ProvidersResourceWithStreamingResponse: + return ProvidersResourceWithStreamingResponse(self._models.providers) + + +class AsyncModelsResourceWithStreamingResponse: + def __init__(self, models: AsyncModelsResource) -> None: + self._models = models + + self.list = async_to_streamed_response_wrapper( + models.list, + ) + + @cached_property + def providers(self) -> AsyncProvidersResourceWithStreamingResponse: + return AsyncProvidersResourceWithStreamingResponse(self._models.providers) diff --git a/src/gradient/resources/models/providers/__init__.py b/src/gradient/resources/models/providers/__init__.py new file mode 100644 index 00000000..1731e057 --- /dev/null +++ b/src/gradient/resources/models/providers/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .openai import ( + OpenAIResource, + AsyncOpenAIResource, + OpenAIResourceWithRawResponse, + AsyncOpenAIResourceWithRawResponse, + OpenAIResourceWithStreamingResponse, + AsyncOpenAIResourceWithStreamingResponse, +) +from .anthropic import ( + AnthropicResource, + AsyncAnthropicResource, + AnthropicResourceWithRawResponse, + AsyncAnthropicResourceWithRawResponse, + AnthropicResourceWithStreamingResponse, + AsyncAnthropicResourceWithStreamingResponse, +) +from .providers import ( + ProvidersResource, + AsyncProvidersResource, + ProvidersResourceWithRawResponse, + AsyncProvidersResourceWithRawResponse, + ProvidersResourceWithStreamingResponse, + AsyncProvidersResourceWithStreamingResponse, +) + +__all__ = [ + "AnthropicResource", + "AsyncAnthropicResource", + "AnthropicResourceWithRawResponse", + "AsyncAnthropicResourceWithRawResponse", + "AnthropicResourceWithStreamingResponse", + "AsyncAnthropicResourceWithStreamingResponse", + "OpenAIResource", + "AsyncOpenAIResource", + "OpenAIResourceWithRawResponse", + "AsyncOpenAIResourceWithRawResponse", + "OpenAIResourceWithStreamingResponse", + "AsyncOpenAIResourceWithStreamingResponse", + "ProvidersResource", + "AsyncProvidersResource", + "ProvidersResourceWithRawResponse", + "AsyncProvidersResourceWithRawResponse", + "ProvidersResourceWithStreamingResponse", + "AsyncProvidersResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/models/providers/anthropic.py b/src/gradient/resources/models/providers/anthropic.py new file mode 100644 index 00000000..33b2ec80 --- /dev/null +++ b/src/gradient/resources/models/providers/anthropic.py @@ -0,0 +1,711 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.models.providers import ( + anthropic_list_params, + anthropic_create_params, + anthropic_update_params, + anthropic_list_agents_params, +) +from ....types.models.providers.anthropic_list_response import AnthropicListResponse +from ....types.models.providers.anthropic_create_response import AnthropicCreateResponse +from ....types.models.providers.anthropic_delete_response import AnthropicDeleteResponse +from ....types.models.providers.anthropic_update_response import AnthropicUpdateResponse +from ....types.models.providers.anthropic_retrieve_response import AnthropicRetrieveResponse +from ....types.models.providers.anthropic_list_agents_response import AnthropicListAgentsResponse + +__all__ = ["AnthropicResource", "AsyncAnthropicResource"] + + +class AnthropicResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AnthropicResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AnthropicResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AnthropicResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AnthropicResourceWithStreamingResponse(self) + + def create( + self, + *, + api_key: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicCreateResponse: + """ + To create an Anthropic API key, send a POST request to + `/v2/gen-ai/anthropic/keys`. + + Args: + api_key: Anthropic API key + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/anthropic/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/anthropic/keys", + body=maybe_transform( + { + "api_key": api_key, + "name": name, + }, + anthropic_create_params.AnthropicCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnthropicCreateResponse, + ) + + def retrieve( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicRetrieveResponse: + """ + To retrieve details of an Anthropic API key, send a GET request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._get( + f"/v2/gen-ai/anthropic/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnthropicRetrieveResponse, + ) + + def update( + self, + path_api_key_uuid: str, + *, + api_key: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicUpdateResponse: + """ + To update an Anthropic API key, send a PUT request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + api_key: Anthropic API key + + body_api_key_uuid: API key ID + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return self._put( + f"/v2/gen-ai/anthropic/keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{path_api_key_uuid}", + body=maybe_transform( + { + "api_key": api_key, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + anthropic_update_params.AnthropicUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnthropicUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicListResponse: + """ + To list all Anthropic API keys, send a GET request to + `/v2/gen-ai/anthropic/keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/anthropic/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/anthropic/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + anthropic_list_params.AnthropicListParams, + ), + ), + cast_to=AnthropicListResponse, + ) + + def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicDeleteResponse: + """ + To delete an Anthropic API key, send a DELETE request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._delete( + f"/v2/gen-ai/anthropic/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnthropicDeleteResponse, + ) + + def list_agents( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicListAgentsResponse: + """ + List Agents by Anthropic Key. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/anthropic/keys/{uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + anthropic_list_agents_params.AnthropicListAgentsParams, + ), + ), + cast_to=AnthropicListAgentsResponse, + ) + + +class AsyncAnthropicResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAnthropicResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncAnthropicResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAnthropicResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncAnthropicResourceWithStreamingResponse(self) + + async def create( + self, + *, + api_key: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicCreateResponse: + """ + To create an Anthropic API key, send a POST request to + `/v2/gen-ai/anthropic/keys`. + + Args: + api_key: Anthropic API key + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/anthropic/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/anthropic/keys", + body=await async_maybe_transform( + { + "api_key": api_key, + "name": name, + }, + anthropic_create_params.AnthropicCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnthropicCreateResponse, + ) + + async def retrieve( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicRetrieveResponse: + """ + To retrieve details of an Anthropic API key, send a GET request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._get( + f"/v2/gen-ai/anthropic/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnthropicRetrieveResponse, + ) + + async def update( + self, + path_api_key_uuid: str, + *, + api_key: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicUpdateResponse: + """ + To update an Anthropic API key, send a PUT request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + api_key: Anthropic API key + + body_api_key_uuid: API key ID + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return await self._put( + f"/v2/gen-ai/anthropic/keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{path_api_key_uuid}", + body=await async_maybe_transform( + { + "api_key": api_key, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + anthropic_update_params.AnthropicUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnthropicUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicListResponse: + """ + To list all Anthropic API keys, send a GET request to + `/v2/gen-ai/anthropic/keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/anthropic/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/anthropic/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + anthropic_list_params.AnthropicListParams, + ), + ), + cast_to=AnthropicListResponse, + ) + + async def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicDeleteResponse: + """ + To delete an Anthropic API key, send a DELETE request to + `/v2/gen-ai/anthropic/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._delete( + f"/v2/gen-ai/anthropic/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnthropicDeleteResponse, + ) + + async def list_agents( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnthropicListAgentsResponse: + """ + List Agents by Anthropic Key. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/anthropic/keys/{uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/anthropic/keys/{uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + anthropic_list_agents_params.AnthropicListAgentsParams, + ), + ), + cast_to=AnthropicListAgentsResponse, + ) + + +class AnthropicResourceWithRawResponse: + def __init__(self, anthropic: AnthropicResource) -> None: + self._anthropic = anthropic + + self.create = to_raw_response_wrapper( + anthropic.create, + ) + self.retrieve = to_raw_response_wrapper( + anthropic.retrieve, + ) + self.update = to_raw_response_wrapper( + anthropic.update, + ) + self.list = to_raw_response_wrapper( + anthropic.list, + ) + self.delete = to_raw_response_wrapper( + anthropic.delete, + ) + self.list_agents = to_raw_response_wrapper( + anthropic.list_agents, + ) + + +class AsyncAnthropicResourceWithRawResponse: + def __init__(self, anthropic: AsyncAnthropicResource) -> None: + self._anthropic = anthropic + + self.create = async_to_raw_response_wrapper( + anthropic.create, + ) + self.retrieve = async_to_raw_response_wrapper( + anthropic.retrieve, + ) + self.update = async_to_raw_response_wrapper( + anthropic.update, + ) + self.list = async_to_raw_response_wrapper( + anthropic.list, + ) + self.delete = async_to_raw_response_wrapper( + anthropic.delete, + ) + self.list_agents = async_to_raw_response_wrapper( + anthropic.list_agents, + ) + + +class AnthropicResourceWithStreamingResponse: + def __init__(self, anthropic: AnthropicResource) -> None: + self._anthropic = anthropic + + self.create = to_streamed_response_wrapper( + anthropic.create, + ) + self.retrieve = to_streamed_response_wrapper( + anthropic.retrieve, + ) + self.update = to_streamed_response_wrapper( + anthropic.update, + ) + self.list = to_streamed_response_wrapper( + anthropic.list, + ) + self.delete = to_streamed_response_wrapper( + anthropic.delete, + ) + self.list_agents = to_streamed_response_wrapper( + anthropic.list_agents, + ) + + +class AsyncAnthropicResourceWithStreamingResponse: + def __init__(self, anthropic: AsyncAnthropicResource) -> None: + self._anthropic = anthropic + + self.create = async_to_streamed_response_wrapper( + anthropic.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + anthropic.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + anthropic.update, + ) + self.list = async_to_streamed_response_wrapper( + anthropic.list, + ) + self.delete = async_to_streamed_response_wrapper( + anthropic.delete, + ) + self.list_agents = async_to_streamed_response_wrapper( + anthropic.list_agents, + ) diff --git a/src/gradient/resources/models/providers/openai.py b/src/gradient/resources/models/providers/openai.py new file mode 100644 index 00000000..5bdc3f20 --- /dev/null +++ b/src/gradient/resources/models/providers/openai.py @@ -0,0 +1,707 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.models.providers import ( + openai_list_params, + openai_create_params, + openai_update_params, + openai_retrieve_agents_params, +) +from ....types.models.providers.openai_list_response import OpenAIListResponse +from ....types.models.providers.openai_create_response import OpenAICreateResponse +from ....types.models.providers.openai_delete_response import OpenAIDeleteResponse +from ....types.models.providers.openai_update_response import OpenAIUpdateResponse +from ....types.models.providers.openai_retrieve_response import OpenAIRetrieveResponse +from ....types.models.providers.openai_retrieve_agents_response import OpenAIRetrieveAgentsResponse + +__all__ = ["OpenAIResource", "AsyncOpenAIResource"] + + +class OpenAIResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> OpenAIResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return OpenAIResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OpenAIResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return OpenAIResourceWithStreamingResponse(self) + + def create( + self, + *, + api_key: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAICreateResponse: + """ + To create an OpenAI API key, send a POST request to `/v2/gen-ai/openai/keys`. + + Args: + api_key: OpenAI API key + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/gen-ai/openai/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/openai/keys", + body=maybe_transform( + { + "api_key": api_key, + "name": name, + }, + openai_create_params.OpenAICreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OpenAICreateResponse, + ) + + def retrieve( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIRetrieveResponse: + """ + To retrieve details of an OpenAI API key, send a GET request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._get( + f"/v2/gen-ai/openai/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OpenAIRetrieveResponse, + ) + + def update( + self, + path_api_key_uuid: str, + *, + api_key: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIUpdateResponse: + """ + To update an OpenAI API key, send a PUT request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + api_key: OpenAI API key + + body_api_key_uuid: API key ID + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return self._put( + f"/v2/gen-ai/openai/keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{path_api_key_uuid}", + body=maybe_transform( + { + "api_key": api_key, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + openai_update_params.OpenAIUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OpenAIUpdateResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIListResponse: + """ + To list all OpenAI API keys, send a GET request to `/v2/gen-ai/openai/keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/gen-ai/openai/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/openai/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + openai_list_params.OpenAIListParams, + ), + ), + cast_to=OpenAIListResponse, + ) + + def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIDeleteResponse: + """ + To delete an OpenAI API key, send a DELETE request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return self._delete( + f"/v2/gen-ai/openai/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OpenAIDeleteResponse, + ) + + def retrieve_agents( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIRetrieveAgentsResponse: + """ + List Agents by OpenAI Key. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return self._get( + f"/v2/gen-ai/openai/keys/{uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + openai_retrieve_agents_params.OpenAIRetrieveAgentsParams, + ), + ), + cast_to=OpenAIRetrieveAgentsResponse, + ) + + +class AsyncOpenAIResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncOpenAIResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncOpenAIResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOpenAIResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncOpenAIResourceWithStreamingResponse(self) + + async def create( + self, + *, + api_key: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAICreateResponse: + """ + To create an OpenAI API key, send a POST request to `/v2/gen-ai/openai/keys`. + + Args: + api_key: OpenAI API key + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/gen-ai/openai/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/openai/keys", + body=await async_maybe_transform( + { + "api_key": api_key, + "name": name, + }, + openai_create_params.OpenAICreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OpenAICreateResponse, + ) + + async def retrieve( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIRetrieveResponse: + """ + To retrieve details of an OpenAI API key, send a GET request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._get( + f"/v2/gen-ai/openai/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OpenAIRetrieveResponse, + ) + + async def update( + self, + path_api_key_uuid: str, + *, + api_key: str | Omit = omit, + body_api_key_uuid: str | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIUpdateResponse: + """ + To update an OpenAI API key, send a PUT request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + api_key: OpenAI API key + + body_api_key_uuid: API key ID + + name: Name of the key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_api_key_uuid: + raise ValueError(f"Expected a non-empty value for `path_api_key_uuid` but received {path_api_key_uuid!r}") + return await self._put( + f"/v2/gen-ai/openai/keys/{path_api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{path_api_key_uuid}", + body=await async_maybe_transform( + { + "api_key": api_key, + "body_api_key_uuid": body_api_key_uuid, + "name": name, + }, + openai_update_params.OpenAIUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OpenAIUpdateResponse, + ) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIListResponse: + """ + To list all OpenAI API keys, send a GET request to `/v2/gen-ai/openai/keys`. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/gen-ai/openai/keys" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/gen-ai/openai/keys", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + openai_list_params.OpenAIListParams, + ), + ), + cast_to=OpenAIListResponse, + ) + + async def delete( + self, + api_key_uuid: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIDeleteResponse: + """ + To delete an OpenAI API key, send a DELETE request to + `/v2/gen-ai/openai/keys/{api_key_uuid}`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not api_key_uuid: + raise ValueError(f"Expected a non-empty value for `api_key_uuid` but received {api_key_uuid!r}") + return await self._delete( + f"/v2/gen-ai/openai/keys/{api_key_uuid}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{api_key_uuid}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OpenAIDeleteResponse, + ) + + async def retrieve_agents( + self, + uuid: str, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OpenAIRetrieveAgentsResponse: + """ + List Agents by OpenAI Key. + + Args: + page: Page number. + + per_page: Items per page. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not uuid: + raise ValueError(f"Expected a non-empty value for `uuid` but received {uuid!r}") + return await self._get( + f"/v2/gen-ai/openai/keys/{uuid}/agents" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/gen-ai/openai/keys/{uuid}/agents", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + openai_retrieve_agents_params.OpenAIRetrieveAgentsParams, + ), + ), + cast_to=OpenAIRetrieveAgentsResponse, + ) + + +class OpenAIResourceWithRawResponse: + def __init__(self, openai: OpenAIResource) -> None: + self._openai = openai + + self.create = to_raw_response_wrapper( + openai.create, + ) + self.retrieve = to_raw_response_wrapper( + openai.retrieve, + ) + self.update = to_raw_response_wrapper( + openai.update, + ) + self.list = to_raw_response_wrapper( + openai.list, + ) + self.delete = to_raw_response_wrapper( + openai.delete, + ) + self.retrieve_agents = to_raw_response_wrapper( + openai.retrieve_agents, + ) + + +class AsyncOpenAIResourceWithRawResponse: + def __init__(self, openai: AsyncOpenAIResource) -> None: + self._openai = openai + + self.create = async_to_raw_response_wrapper( + openai.create, + ) + self.retrieve = async_to_raw_response_wrapper( + openai.retrieve, + ) + self.update = async_to_raw_response_wrapper( + openai.update, + ) + self.list = async_to_raw_response_wrapper( + openai.list, + ) + self.delete = async_to_raw_response_wrapper( + openai.delete, + ) + self.retrieve_agents = async_to_raw_response_wrapper( + openai.retrieve_agents, + ) + + +class OpenAIResourceWithStreamingResponse: + def __init__(self, openai: OpenAIResource) -> None: + self._openai = openai + + self.create = to_streamed_response_wrapper( + openai.create, + ) + self.retrieve = to_streamed_response_wrapper( + openai.retrieve, + ) + self.update = to_streamed_response_wrapper( + openai.update, + ) + self.list = to_streamed_response_wrapper( + openai.list, + ) + self.delete = to_streamed_response_wrapper( + openai.delete, + ) + self.retrieve_agents = to_streamed_response_wrapper( + openai.retrieve_agents, + ) + + +class AsyncOpenAIResourceWithStreamingResponse: + def __init__(self, openai: AsyncOpenAIResource) -> None: + self._openai = openai + + self.create = async_to_streamed_response_wrapper( + openai.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + openai.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + openai.update, + ) + self.list = async_to_streamed_response_wrapper( + openai.list, + ) + self.delete = async_to_streamed_response_wrapper( + openai.delete, + ) + self.retrieve_agents = async_to_streamed_response_wrapper( + openai.retrieve_agents, + ) diff --git a/src/gradient/resources/models/providers/providers.py b/src/gradient/resources/models/providers/providers.py new file mode 100644 index 00000000..efb71ec5 --- /dev/null +++ b/src/gradient/resources/models/providers/providers.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .openai import ( + OpenAIResource, + AsyncOpenAIResource, + OpenAIResourceWithRawResponse, + AsyncOpenAIResourceWithRawResponse, + OpenAIResourceWithStreamingResponse, + AsyncOpenAIResourceWithStreamingResponse, +) +from .anthropic import ( + AnthropicResource, + AsyncAnthropicResource, + AnthropicResourceWithRawResponse, + AsyncAnthropicResourceWithRawResponse, + AnthropicResourceWithStreamingResponse, + AsyncAnthropicResourceWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["ProvidersResource", "AsyncProvidersResource"] + + +class ProvidersResource(SyncAPIResource): + @cached_property + def anthropic(self) -> AnthropicResource: + return AnthropicResource(self._client) + + @cached_property + def openai(self) -> OpenAIResource: + return OpenAIResource(self._client) + + @cached_property + def with_raw_response(self) -> ProvidersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return ProvidersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ProvidersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return ProvidersResourceWithStreamingResponse(self) + + +class AsyncProvidersResource(AsyncAPIResource): + @cached_property + def anthropic(self) -> AsyncAnthropicResource: + return AsyncAnthropicResource(self._client) + + @cached_property + def openai(self) -> AsyncOpenAIResource: + return AsyncOpenAIResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncProvidersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncProvidersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncProvidersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncProvidersResourceWithStreamingResponse(self) + + +class ProvidersResourceWithRawResponse: + def __init__(self, providers: ProvidersResource) -> None: + self._providers = providers + + @cached_property + def anthropic(self) -> AnthropicResourceWithRawResponse: + return AnthropicResourceWithRawResponse(self._providers.anthropic) + + @cached_property + def openai(self) -> OpenAIResourceWithRawResponse: + return OpenAIResourceWithRawResponse(self._providers.openai) + + +class AsyncProvidersResourceWithRawResponse: + def __init__(self, providers: AsyncProvidersResource) -> None: + self._providers = providers + + @cached_property + def anthropic(self) -> AsyncAnthropicResourceWithRawResponse: + return AsyncAnthropicResourceWithRawResponse(self._providers.anthropic) + + @cached_property + def openai(self) -> AsyncOpenAIResourceWithRawResponse: + return AsyncOpenAIResourceWithRawResponse(self._providers.openai) + + +class ProvidersResourceWithStreamingResponse: + def __init__(self, providers: ProvidersResource) -> None: + self._providers = providers + + @cached_property + def anthropic(self) -> AnthropicResourceWithStreamingResponse: + return AnthropicResourceWithStreamingResponse(self._providers.anthropic) + + @cached_property + def openai(self) -> OpenAIResourceWithStreamingResponse: + return OpenAIResourceWithStreamingResponse(self._providers.openai) + + +class AsyncProvidersResourceWithStreamingResponse: + def __init__(self, providers: AsyncProvidersResource) -> None: + self._providers = providers + + @cached_property + def anthropic(self) -> AsyncAnthropicResourceWithStreamingResponse: + return AsyncAnthropicResourceWithStreamingResponse(self._providers.anthropic) + + @cached_property + def openai(self) -> AsyncOpenAIResourceWithStreamingResponse: + return AsyncOpenAIResourceWithStreamingResponse(self._providers.openai) diff --git a/src/gradient/resources/nfs/__init__.py b/src/gradient/resources/nfs/__init__.py new file mode 100644 index 00000000..28f843c0 --- /dev/null +++ b/src/gradient/resources/nfs/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .nfs import ( + NfsResource, + AsyncNfsResource, + NfsResourceWithRawResponse, + AsyncNfsResourceWithRawResponse, + NfsResourceWithStreamingResponse, + AsyncNfsResourceWithStreamingResponse, +) +from .snapshots import ( + SnapshotsResource, + AsyncSnapshotsResource, + SnapshotsResourceWithRawResponse, + AsyncSnapshotsResourceWithRawResponse, + SnapshotsResourceWithStreamingResponse, + AsyncSnapshotsResourceWithStreamingResponse, +) + +__all__ = [ + "SnapshotsResource", + "AsyncSnapshotsResource", + "SnapshotsResourceWithRawResponse", + "AsyncSnapshotsResourceWithRawResponse", + "SnapshotsResourceWithStreamingResponse", + "AsyncSnapshotsResourceWithStreamingResponse", + "NfsResource", + "AsyncNfsResource", + "NfsResourceWithRawResponse", + "AsyncNfsResourceWithRawResponse", + "NfsResourceWithStreamingResponse", + "AsyncNfsResourceWithStreamingResponse", +] diff --git a/src/gradient/resources/nfs/nfs.py b/src/gradient/resources/nfs/nfs.py new file mode 100644 index 00000000..1510bb69 --- /dev/null +++ b/src/gradient/resources/nfs/nfs.py @@ -0,0 +1,780 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, overload + +import httpx + +from ...types import nf_list_params, nf_create_params, nf_delete_params, nf_retrieve_params, nf_initiate_action_params +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from .snapshots import ( + SnapshotsResource, + AsyncSnapshotsResource, + SnapshotsResourceWithRawResponse, + AsyncSnapshotsResourceWithRawResponse, + SnapshotsResourceWithStreamingResponse, + AsyncSnapshotsResourceWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.nf_list_response import NfListResponse +from ...types.nf_create_response import NfCreateResponse +from ...types.nf_retrieve_response import NfRetrieveResponse +from ...types.nf_initiate_action_response import NfInitiateActionResponse + +__all__ = ["NfsResource", "AsyncNfsResource"] + + +class NfsResource(SyncAPIResource): + @cached_property + def snapshots(self) -> SnapshotsResource: + return SnapshotsResource(self._client) + + @cached_property + def with_raw_response(self) -> NfsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return NfsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> NfsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return NfsResourceWithStreamingResponse(self) + + def create( + self, + *, + name: str, + region: str, + size_gib: int, + vpc_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfCreateResponse: + """ + To create a new NFS share, send a POST request to `/v2/nfs`. + + Args: + name: The human-readable name of the share. + + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + size_gib: The desired/provisioned size of the share in GiB (Gibibytes). Must be >= 50. + + vpc_ids: List of VPC IDs that should be able to access the share. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/nfs" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/nfs", + body=maybe_transform( + { + "name": name, + "region": region, + "size_gib": size_gib, + "vpc_ids": vpc_ids, + }, + nf_create_params.NfCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NfCreateResponse, + ) + + def retrieve( + self, + nfs_id: str, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfRetrieveResponse: + """ + To get an NFS share, send a GET request to `/v2/nfs/{nfs_id}?region=${region}`. + + A successful request will return the NFS share. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not nfs_id: + raise ValueError(f"Expected a non-empty value for `nfs_id` but received {nfs_id!r}") + return self._get( + f"/v2/nfs/{nfs_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/{nfs_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"region": region}, nf_retrieve_params.NfRetrieveParams), + ), + cast_to=NfRetrieveResponse, + ) + + def list( + self, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfListResponse: + """ + To list NFS shares, send a GET request to `/v2/nfs?region=${region}`. + + A successful request will return all NFS shares belonging to the authenticated + user. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/nfs" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/nfs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"region": region}, nf_list_params.NfListParams), + ), + cast_to=NfListResponse, + ) + + def delete( + self, + nfs_id: str, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete an NFS share, send a DELETE request to + `/v2/nfs/{nfs_id}?region=${region}`. + + A successful request will return a `204 No Content` status code. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not nfs_id: + raise ValueError(f"Expected a non-empty value for `nfs_id` but received {nfs_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/nfs/{nfs_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/{nfs_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"region": region}, nf_delete_params.NfDeleteParams), + ), + cast_to=NoneType, + ) + + @overload + def initiate_action( + self, + nfs_id: str, + *, + region: str, + type: Literal["resize", "snapshot"], + params: nf_initiate_action_params.NfsActionResizeParams | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfInitiateActionResponse: + """ + To execute an action (such as resize) on a specified NFS share, send a POST + request to `/v2/nfs/{nfs_id}/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | + | ----------------------- | -------------------------------------------------------------------------- | + | `resize` | Resizes an NFS share. Set the size_gib attribute to a desired value in GiB | + | `snapshot` | Takes a snapshot of an NFS share | + + Args: + region: The DigitalOcean region slug (e.g. atl1, nyc2) where the NFS snapshot resides. + + type: The type of action to initiate for the NFS share (such as resize or snapshot). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def initiate_action( + self, + nfs_id: str, + *, + region: str, + type: Literal["resize", "snapshot"], + params: nf_initiate_action_params.NfsActionSnapshotParams | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfInitiateActionResponse: + """ + To execute an action (such as resize) on a specified NFS share, send a POST + request to `/v2/nfs/{nfs_id}/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | + | ----------------------- | -------------------------------------------------------------------------- | + | `resize` | Resizes an NFS share. Set the size_gib attribute to a desired value in GiB | + | `snapshot` | Takes a snapshot of an NFS share | + + Args: + region: The DigitalOcean region slug (e.g. atl1, nyc2) where the NFS snapshot resides. + + type: The type of action to initiate for the NFS share (such as resize or snapshot). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["region", "type"]) + def initiate_action( + self, + nfs_id: str, + *, + region: str, + type: Literal["resize", "snapshot"], + params: nf_initiate_action_params.NfsActionResizeParams + | nf_initiate_action_params.NfsActionSnapshotParams + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfInitiateActionResponse: + if not nfs_id: + raise ValueError(f"Expected a non-empty value for `nfs_id` but received {nfs_id!r}") + return self._post( + f"/v2/nfs/{nfs_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/{nfs_id}/actions", + body=maybe_transform( + { + "region": region, + "type": type, + "params": params, + }, + nf_initiate_action_params.NfInitiateActionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NfInitiateActionResponse, + ) + + +class AsyncNfsResource(AsyncAPIResource): + @cached_property + def snapshots(self) -> AsyncSnapshotsResource: + return AsyncSnapshotsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncNfsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncNfsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncNfsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncNfsResourceWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + region: str, + size_gib: int, + vpc_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfCreateResponse: + """ + To create a new NFS share, send a POST request to `/v2/nfs`. + + Args: + name: The human-readable name of the share. + + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + size_gib: The desired/provisioned size of the share in GiB (Gibibytes). Must be >= 50. + + vpc_ids: List of VPC IDs that should be able to access the share. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/nfs" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/nfs", + body=await async_maybe_transform( + { + "name": name, + "region": region, + "size_gib": size_gib, + "vpc_ids": vpc_ids, + }, + nf_create_params.NfCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NfCreateResponse, + ) + + async def retrieve( + self, + nfs_id: str, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfRetrieveResponse: + """ + To get an NFS share, send a GET request to `/v2/nfs/{nfs_id}?region=${region}`. + + A successful request will return the NFS share. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not nfs_id: + raise ValueError(f"Expected a non-empty value for `nfs_id` but received {nfs_id!r}") + return await self._get( + f"/v2/nfs/{nfs_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/{nfs_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"region": region}, nf_retrieve_params.NfRetrieveParams), + ), + cast_to=NfRetrieveResponse, + ) + + async def list( + self, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfListResponse: + """ + To list NFS shares, send a GET request to `/v2/nfs?region=${region}`. + + A successful request will return all NFS shares belonging to the authenticated + user. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/nfs" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/nfs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"region": region}, nf_list_params.NfListParams), + ), + cast_to=NfListResponse, + ) + + async def delete( + self, + nfs_id: str, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete an NFS share, send a DELETE request to + `/v2/nfs/{nfs_id}?region=${region}`. + + A successful request will return a `204 No Content` status code. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not nfs_id: + raise ValueError(f"Expected a non-empty value for `nfs_id` but received {nfs_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/nfs/{nfs_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/{nfs_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"region": region}, nf_delete_params.NfDeleteParams), + ), + cast_to=NoneType, + ) + + @overload + async def initiate_action( + self, + nfs_id: str, + *, + region: str, + type: Literal["resize", "snapshot"], + params: nf_initiate_action_params.NfsActionResizeParams | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfInitiateActionResponse: + """ + To execute an action (such as resize) on a specified NFS share, send a POST + request to `/v2/nfs/{nfs_id}/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | + | ----------------------- | -------------------------------------------------------------------------- | + | `resize` | Resizes an NFS share. Set the size_gib attribute to a desired value in GiB | + | `snapshot` | Takes a snapshot of an NFS share | + + Args: + region: The DigitalOcean region slug (e.g. atl1, nyc2) where the NFS snapshot resides. + + type: The type of action to initiate for the NFS share (such as resize or snapshot). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def initiate_action( + self, + nfs_id: str, + *, + region: str, + type: Literal["resize", "snapshot"], + params: nf_initiate_action_params.NfsActionSnapshotParams | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfInitiateActionResponse: + """ + To execute an action (such as resize) on a specified NFS share, send a POST + request to `/v2/nfs/{nfs_id}/actions`. In the JSON body to the request, set the + `type` attribute to on of the supported action types: + + | Action | Details | + | ----------------------- | -------------------------------------------------------------------------- | + | `resize` | Resizes an NFS share. Set the size_gib attribute to a desired value in GiB | + | `snapshot` | Takes a snapshot of an NFS share | + + Args: + region: The DigitalOcean region slug (e.g. atl1, nyc2) where the NFS snapshot resides. + + type: The type of action to initiate for the NFS share (such as resize or snapshot). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["region", "type"]) + async def initiate_action( + self, + nfs_id: str, + *, + region: str, + type: Literal["resize", "snapshot"], + params: nf_initiate_action_params.NfsActionResizeParams + | nf_initiate_action_params.NfsActionSnapshotParams + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NfInitiateActionResponse: + if not nfs_id: + raise ValueError(f"Expected a non-empty value for `nfs_id` but received {nfs_id!r}") + return await self._post( + f"/v2/nfs/{nfs_id}/actions" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/{nfs_id}/actions", + body=await async_maybe_transform( + { + "region": region, + "type": type, + "params": params, + }, + nf_initiate_action_params.NfInitiateActionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NfInitiateActionResponse, + ) + + +class NfsResourceWithRawResponse: + def __init__(self, nfs: NfsResource) -> None: + self._nfs = nfs + + self.create = to_raw_response_wrapper( + nfs.create, + ) + self.retrieve = to_raw_response_wrapper( + nfs.retrieve, + ) + self.list = to_raw_response_wrapper( + nfs.list, + ) + self.delete = to_raw_response_wrapper( + nfs.delete, + ) + self.initiate_action = to_raw_response_wrapper( + nfs.initiate_action, + ) + + @cached_property + def snapshots(self) -> SnapshotsResourceWithRawResponse: + return SnapshotsResourceWithRawResponse(self._nfs.snapshots) + + +class AsyncNfsResourceWithRawResponse: + def __init__(self, nfs: AsyncNfsResource) -> None: + self._nfs = nfs + + self.create = async_to_raw_response_wrapper( + nfs.create, + ) + self.retrieve = async_to_raw_response_wrapper( + nfs.retrieve, + ) + self.list = async_to_raw_response_wrapper( + nfs.list, + ) + self.delete = async_to_raw_response_wrapper( + nfs.delete, + ) + self.initiate_action = async_to_raw_response_wrapper( + nfs.initiate_action, + ) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResourceWithRawResponse: + return AsyncSnapshotsResourceWithRawResponse(self._nfs.snapshots) + + +class NfsResourceWithStreamingResponse: + def __init__(self, nfs: NfsResource) -> None: + self._nfs = nfs + + self.create = to_streamed_response_wrapper( + nfs.create, + ) + self.retrieve = to_streamed_response_wrapper( + nfs.retrieve, + ) + self.list = to_streamed_response_wrapper( + nfs.list, + ) + self.delete = to_streamed_response_wrapper( + nfs.delete, + ) + self.initiate_action = to_streamed_response_wrapper( + nfs.initiate_action, + ) + + @cached_property + def snapshots(self) -> SnapshotsResourceWithStreamingResponse: + return SnapshotsResourceWithStreamingResponse(self._nfs.snapshots) + + +class AsyncNfsResourceWithStreamingResponse: + def __init__(self, nfs: AsyncNfsResource) -> None: + self._nfs = nfs + + self.create = async_to_streamed_response_wrapper( + nfs.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + nfs.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + nfs.list, + ) + self.delete = async_to_streamed_response_wrapper( + nfs.delete, + ) + self.initiate_action = async_to_streamed_response_wrapper( + nfs.initiate_action, + ) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResourceWithStreamingResponse: + return AsyncSnapshotsResourceWithStreamingResponse(self._nfs.snapshots) diff --git a/src/gradient/resources/nfs/snapshots.py b/src/gradient/resources/nfs/snapshots.py new file mode 100644 index 00000000..65b56e03 --- /dev/null +++ b/src/gradient/resources/nfs/snapshots.py @@ -0,0 +1,418 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...types.nfs import snapshot_list_params, snapshot_delete_params, snapshot_retrieve_params +from ..._base_client import make_request_options +from ...types.nfs.snapshot_list_response import SnapshotListResponse +from ...types.nfs.snapshot_retrieve_response import SnapshotRetrieveResponse + +__all__ = ["SnapshotsResource", "AsyncSnapshotsResource"] + + +class SnapshotsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SnapshotsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return SnapshotsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SnapshotsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return SnapshotsResourceWithStreamingResponse(self) + + def retrieve( + self, + nfs_snapshot_id: str, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotRetrieveResponse: + """ + To get an NFS snapshot, send a GET request to + `/v2/nfs/snapshots/{nfs_snapshot_id}?region=${region}`. + + A successful request will return the NFS snapshot. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not nfs_snapshot_id: + raise ValueError(f"Expected a non-empty value for `nfs_snapshot_id` but received {nfs_snapshot_id!r}") + return self._get( + f"/v2/nfs/snapshots/{nfs_snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/snapshots/{nfs_snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"region": region}, snapshot_retrieve_params.SnapshotRetrieveParams), + ), + cast_to=SnapshotRetrieveResponse, + ) + + def list( + self, + *, + region: str, + share_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotListResponse: + """ + To list all NFS snapshots, send a GET request to + `/v2/nfs/snapshots?region=${region}&share_id={share_id}`. + + A successful request will return all NFS snapshots belonging to the + authenticated user in the specified region. + + Optionally, you can filter snapshots by a specific NFS share by including the + `share_id` query parameter. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + share_id: The unique ID of an NFS share. If provided, only snapshots of this specific + share will be returned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/nfs/snapshots" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/nfs/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "region": region, + "share_id": share_id, + }, + snapshot_list_params.SnapshotListParams, + ), + ), + cast_to=SnapshotListResponse, + ) + + def delete( + self, + nfs_snapshot_id: str, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete an NFS snapshot, send a DELETE request to + `/v2/nfs/snapshots/{nfs_snapshot_id}?region=${region}`. + + A successful request will return a `204 No Content` status code. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not nfs_snapshot_id: + raise ValueError(f"Expected a non-empty value for `nfs_snapshot_id` but received {nfs_snapshot_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/v2/nfs/snapshots/{nfs_snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/snapshots/{nfs_snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"region": region}, snapshot_delete_params.SnapshotDeleteParams), + ), + cast_to=NoneType, + ) + + +class AsyncSnapshotsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSnapshotsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncSnapshotsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSnapshotsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncSnapshotsResourceWithStreamingResponse(self) + + async def retrieve( + self, + nfs_snapshot_id: str, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotRetrieveResponse: + """ + To get an NFS snapshot, send a GET request to + `/v2/nfs/snapshots/{nfs_snapshot_id}?region=${region}`. + + A successful request will return the NFS snapshot. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not nfs_snapshot_id: + raise ValueError(f"Expected a non-empty value for `nfs_snapshot_id` but received {nfs_snapshot_id!r}") + return await self._get( + f"/v2/nfs/snapshots/{nfs_snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/snapshots/{nfs_snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"region": region}, snapshot_retrieve_params.SnapshotRetrieveParams), + ), + cast_to=SnapshotRetrieveResponse, + ) + + async def list( + self, + *, + region: str, + share_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotListResponse: + """ + To list all NFS snapshots, send a GET request to + `/v2/nfs/snapshots?region=${region}&share_id={share_id}`. + + A successful request will return all NFS snapshots belonging to the + authenticated user in the specified region. + + Optionally, you can filter snapshots by a specific NFS share by including the + `share_id` query parameter. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + share_id: The unique ID of an NFS share. If provided, only snapshots of this specific + share will be returned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/nfs/snapshots" + if self._client._base_url_overridden + else "https://api.digitalocean.com/v2/nfs/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "region": region, + "share_id": share_id, + }, + snapshot_list_params.SnapshotListParams, + ), + ), + cast_to=SnapshotListResponse, + ) + + async def delete( + self, + nfs_snapshot_id: str, + *, + region: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + To delete an NFS snapshot, send a DELETE request to + `/v2/nfs/snapshots/{nfs_snapshot_id}?region=${region}`. + + A successful request will return a `204 No Content` status code. + + Args: + region: The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not nfs_snapshot_id: + raise ValueError(f"Expected a non-empty value for `nfs_snapshot_id` but received {nfs_snapshot_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/v2/nfs/snapshots/{nfs_snapshot_id}" + if self._client._base_url_overridden + else f"https://api.digitalocean.com/v2/nfs/snapshots/{nfs_snapshot_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"region": region}, snapshot_delete_params.SnapshotDeleteParams), + ), + cast_to=NoneType, + ) + + +class SnapshotsResourceWithRawResponse: + def __init__(self, snapshots: SnapshotsResource) -> None: + self._snapshots = snapshots + + self.retrieve = to_raw_response_wrapper( + snapshots.retrieve, + ) + self.list = to_raw_response_wrapper( + snapshots.list, + ) + self.delete = to_raw_response_wrapper( + snapshots.delete, + ) + + +class AsyncSnapshotsResourceWithRawResponse: + def __init__(self, snapshots: AsyncSnapshotsResource) -> None: + self._snapshots = snapshots + + self.retrieve = async_to_raw_response_wrapper( + snapshots.retrieve, + ) + self.list = async_to_raw_response_wrapper( + snapshots.list, + ) + self.delete = async_to_raw_response_wrapper( + snapshots.delete, + ) + + +class SnapshotsResourceWithStreamingResponse: + def __init__(self, snapshots: SnapshotsResource) -> None: + self._snapshots = snapshots + + self.retrieve = to_streamed_response_wrapper( + snapshots.retrieve, + ) + self.list = to_streamed_response_wrapper( + snapshots.list, + ) + self.delete = to_streamed_response_wrapper( + snapshots.delete, + ) + + +class AsyncSnapshotsResourceWithStreamingResponse: + def __init__(self, snapshots: AsyncSnapshotsResource) -> None: + self._snapshots = snapshots + + self.retrieve = async_to_streamed_response_wrapper( + snapshots.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + snapshots.list, + ) + self.delete = async_to_streamed_response_wrapper( + snapshots.delete, + ) diff --git a/src/gradient/resources/regions.py b/src/gradient/resources/regions.py new file mode 100644 index 00000000..3b0f22fa --- /dev/null +++ b/src/gradient/resources/regions.py @@ -0,0 +1,197 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import region_list_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.region_list_response import RegionListResponse + +__all__ = ["RegionsResource", "AsyncRegionsResource"] + + +class RegionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RegionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return RegionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RegionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return RegionsResourceWithStreamingResponse(self) + + def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RegionListResponse: + """ + To list all of the regions that are available, send a GET request to + `/v2/regions`. The response will be a JSON object with a key called `regions`. + The value of this will be an array of `region` objects, each of which will + contain the standard region attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/regions" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/regions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + region_list_params.RegionListParams, + ), + ), + cast_to=RegionListResponse, + ) + + +class AsyncRegionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRegionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/digitalocean/gradient-python#accessing-raw-response-data-eg-headers + """ + return AsyncRegionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRegionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/digitalocean/gradient-python#with_streaming_response + """ + return AsyncRegionsResourceWithStreamingResponse(self) + + async def list( + self, + *, + page: int | Omit = omit, + per_page: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RegionListResponse: + """ + To list all of the regions that are available, send a GET request to + `/v2/regions`. The response will be a JSON object with a key called `regions`. + The value of this will be an array of `region` objects, each of which will + contain the standard region attributes. + + Args: + page: Which 'page' of paginated results to return. + + per_page: Number of items returned per page + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/regions" if self._client._base_url_overridden else "https://api.digitalocean.com/v2/regions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + region_list_params.RegionListParams, + ), + ), + cast_to=RegionListResponse, + ) + + +class RegionsResourceWithRawResponse: + def __init__(self, regions: RegionsResource) -> None: + self._regions = regions + + self.list = to_raw_response_wrapper( + regions.list, + ) + + +class AsyncRegionsResourceWithRawResponse: + def __init__(self, regions: AsyncRegionsResource) -> None: + self._regions = regions + + self.list = async_to_raw_response_wrapper( + regions.list, + ) + + +class RegionsResourceWithStreamingResponse: + def __init__(self, regions: RegionsResource) -> None: + self._regions = regions + + self.list = to_streamed_response_wrapper( + regions.list, + ) + + +class AsyncRegionsResourceWithStreamingResponse: + def __init__(self, regions: AsyncRegionsResource) -> None: + self._regions = regions + + self.list = async_to_streamed_response_wrapper( + regions.list, + ) diff --git a/src/gradient/types/__init__.py b/src/gradient/types/__init__.py new file mode 100644 index 00000000..4cd87a44 --- /dev/null +++ b/src/gradient/types/__init__.py @@ -0,0 +1,174 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from . import ( + agents, + models, + api_agent, + api_workspace, + agent_create_response, + agent_delete_response, + agent_update_response, + agent_retrieve_response, + agent_update_status_response, +) +from .. import _compat +from .shared import ( + Size as Size, + Image as Image, + Action as Action, + Kernel as Kernel, + Region as Region, + APIMeta as APIMeta, + Droplet as Droplet, + GPUInfo as GPUInfo, + APILinks as APILinks, + DiskInfo as DiskInfo, + NetworkV4 as NetworkV4, + NetworkV6 as NetworkV6, + PageLinks as PageLinks, + Snapshots as Snapshots, + ActionLink as ActionLink, + VpcPeering as VpcPeering, + ForwardLinks as ForwardLinks, + Subscription as Subscription, + BackwardLinks as BackwardLinks, + MetaProperties as MetaProperties, + CompletionUsage as CompletionUsage, + GarbageCollection as GarbageCollection, + FirewallRuleTarget as FirewallRuleTarget, + ChatCompletionChunk as ChatCompletionChunk, + ImageGenStreamEvent as ImageGenStreamEvent, + SubscriptionTierBase as SubscriptionTierBase, + ImageGenCompletedEvent as ImageGenCompletedEvent, + DropletNextBackupWindow as DropletNextBackupWindow, + ImageGenPartialImageEvent as ImageGenPartialImageEvent, + ChatCompletionTokenLogprob as ChatCompletionTokenLogprob, +) +from .api_agent import APIAgent as APIAgent +from .api_model import APIModel as APIModel +from .api_agreement import APIAgreement as APIAgreement +from .api_workspace import APIWorkspace as APIWorkspace +from .nf_list_params import NfListParams as NfListParams +from .api_agent_model import APIAgentModel as APIAgentModel +from .nf_create_params import NfCreateParams as NfCreateParams +from .nf_delete_params import NfDeleteParams as NfDeleteParams +from .nf_list_response import NfListResponse as NfListResponse +from .agent_list_params import AgentListParams as AgentListParams +from .api_model_version import APIModelVersion as APIModelVersion +from .model_list_params import ModelListParams as ModelListParams +from .api_knowledge_base import APIKnowledgeBase as APIKnowledgeBase +from .nf_create_response import NfCreateResponse as NfCreateResponse +from .nf_retrieve_params import NfRetrieveParams as NfRetrieveParams +from .region_list_params import RegionListParams as RegionListParams +from .agent_create_params import AgentCreateParams as AgentCreateParams +from .agent_list_response import AgentListResponse as AgentListResponse +from .agent_update_params import AgentUpdateParams as AgentUpdateParams +from .model_list_response import ModelListResponse as ModelListResponse +from .api_retrieval_method import APIRetrievalMethod as APIRetrievalMethod +from .nf_retrieve_response import NfRetrieveResponse as NfRetrieveResponse +from .region_list_response import RegionListResponse as RegionListResponse +from .agent_create_response import AgentCreateResponse as AgentCreateResponse +from .agent_delete_response import AgentDeleteResponse as AgentDeleteResponse +from .agent_update_response import AgentUpdateResponse as AgentUpdateResponse +from .droplet_backup_policy import DropletBackupPolicy as DropletBackupPolicy +from .image_generate_params import ImageGenerateParams as ImageGenerateParams +from .api_agent_api_key_info import APIAgentAPIKeyInfo as APIAgentAPIKeyInfo +from .agent_retrieve_response import AgentRetrieveResponse as AgentRetrieveResponse +from .api_openai_api_key_info import APIOpenAIAPIKeyInfo as APIOpenAIAPIKeyInfo +from .gpu_droplet_list_params import GPUDropletListParams as GPUDropletListParams +from .image_generate_response import ImageGenerateResponse as ImageGenerateResponse +from .api_deployment_visibility import APIDeploymentVisibility as APIDeploymentVisibility +from .gpu_droplet_create_params import GPUDropletCreateParams as GPUDropletCreateParams +from .gpu_droplet_list_response import GPUDropletListResponse as GPUDropletListResponse +from .nf_initiate_action_params import NfInitiateActionParams as NfInitiateActionParams +from .agent_update_status_params import AgentUpdateStatusParams as AgentUpdateStatusParams +from .api_anthropic_api_key_info import APIAnthropicAPIKeyInfo as APIAnthropicAPIKeyInfo +from .knowledge_base_list_params import KnowledgeBaseListParams as KnowledgeBaseListParams +from .agent_retrieve_usage_params import AgentRetrieveUsageParams as AgentRetrieveUsageParams +from .droplet_backup_policy_param import DropletBackupPolicyParam as DropletBackupPolicyParam +from .gpu_droplet_create_response import GPUDropletCreateResponse as GPUDropletCreateResponse +from .nf_initiate_action_response import NfInitiateActionResponse as NfInitiateActionResponse +from .agent_update_status_response import AgentUpdateStatusResponse as AgentUpdateStatusResponse +from .knowledge_base_create_params import KnowledgeBaseCreateParams as KnowledgeBaseCreateParams +from .knowledge_base_list_response import KnowledgeBaseListResponse as KnowledgeBaseListResponse +from .knowledge_base_update_params import KnowledgeBaseUpdateParams as KnowledgeBaseUpdateParams +from .agent_retrieve_usage_response import AgentRetrieveUsageResponse as AgentRetrieveUsageResponse +from .gpu_droplet_retrieve_response import GPUDropletRetrieveResponse as GPUDropletRetrieveResponse +from .knowledge_base_create_response import KnowledgeBaseCreateResponse as KnowledgeBaseCreateResponse +from .knowledge_base_delete_response import KnowledgeBaseDeleteResponse as KnowledgeBaseDeleteResponse +from .knowledge_base_update_response import KnowledgeBaseUpdateResponse as KnowledgeBaseUpdateResponse +from .gpu_droplet_list_kernels_params import GPUDropletListKernelsParams as GPUDropletListKernelsParams +from .gpu_droplet_delete_by_tag_params import GPUDropletDeleteByTagParams as GPUDropletDeleteByTagParams +from .knowledge_base_retrieve_response import KnowledgeBaseRetrieveResponse as KnowledgeBaseRetrieveResponse +from .gpu_droplet_list_firewalls_params import GPUDropletListFirewallsParams as GPUDropletListFirewallsParams +from .gpu_droplet_list_kernels_response import GPUDropletListKernelsResponse as GPUDropletListKernelsResponse +from .gpu_droplet_list_snapshots_params import GPUDropletListSnapshotsParams as GPUDropletListSnapshotsParams +from .gpu_droplet_list_firewalls_response import GPUDropletListFirewallsResponse as GPUDropletListFirewallsResponse +from .gpu_droplet_list_neighbors_response import GPUDropletListNeighborsResponse as GPUDropletListNeighborsResponse +from .gpu_droplet_list_snapshots_response import GPUDropletListSnapshotsResponse as GPUDropletListSnapshotsResponse +from .knowledge_base_list_indexing_jobs_response import ( + KnowledgeBaseListIndexingJobsResponse as KnowledgeBaseListIndexingJobsResponse, +) + +# Rebuild cyclical models only after all modules are imported. +# This ensures that, when building the deferred (due to cyclical references) model schema, +# Pydantic can resolve the necessary references. +# See: https://github.com/pydantic/pydantic/issues/11250 for more context. +if _compat.PYDANTIC_V1: + api_agent.APIAgent.update_forward_refs() # type: ignore + api_workspace.APIWorkspace.update_forward_refs() # type: ignore + agent_create_response.AgentCreateResponse.update_forward_refs() # type: ignore + agent_retrieve_response.AgentRetrieveResponse.update_forward_refs() # type: ignore + agent_update_response.AgentUpdateResponse.update_forward_refs() # type: ignore + agent_delete_response.AgentDeleteResponse.update_forward_refs() # type: ignore + agent_update_status_response.AgentUpdateStatusResponse.update_forward_refs() # type: ignore + agents.evaluation_metrics.workspace_create_response.WorkspaceCreateResponse.update_forward_refs() # type: ignore + agents.evaluation_metrics.workspace_retrieve_response.WorkspaceRetrieveResponse.update_forward_refs() # type: ignore + agents.evaluation_metrics.workspace_update_response.WorkspaceUpdateResponse.update_forward_refs() # type: ignore + agents.evaluation_metrics.workspace_list_response.WorkspaceListResponse.update_forward_refs() # type: ignore + agents.evaluation_metrics.workspaces.agent_list_response.AgentListResponse.update_forward_refs() # type: ignore + agents.evaluation_metrics.workspaces.agent_move_response.AgentMoveResponse.update_forward_refs() # type: ignore + agents.evaluation_metrics.anthropic.key_list_agents_response.KeyListAgentsResponse.update_forward_refs() # type: ignore + agents.evaluation_metrics.openai.key_list_agents_response.KeyListAgentsResponse.update_forward_refs() # type: ignore + agents.function_create_response.FunctionCreateResponse.update_forward_refs() # type: ignore + agents.function_update_response.FunctionUpdateResponse.update_forward_refs() # type: ignore + agents.function_delete_response.FunctionDeleteResponse.update_forward_refs() # type: ignore + agents.api_link_knowledge_base_output.APILinkKnowledgeBaseOutput.update_forward_refs() # type: ignore + agents.knowledge_base_detach_response.KnowledgeBaseDetachResponse.update_forward_refs() # type: ignore + agents.route_view_response.RouteViewResponse.update_forward_refs() # type: ignore + models.providers.anthropic_list_agents_response.AnthropicListAgentsResponse.update_forward_refs() # type: ignore + models.providers.openai_retrieve_agents_response.OpenAIRetrieveAgentsResponse.update_forward_refs() # type: ignore +else: + api_agent.APIAgent.model_rebuild(_parent_namespace_depth=0) + api_workspace.APIWorkspace.model_rebuild(_parent_namespace_depth=0) + agent_create_response.AgentCreateResponse.model_rebuild(_parent_namespace_depth=0) + agent_retrieve_response.AgentRetrieveResponse.model_rebuild(_parent_namespace_depth=0) + agent_update_response.AgentUpdateResponse.model_rebuild(_parent_namespace_depth=0) + agent_delete_response.AgentDeleteResponse.model_rebuild(_parent_namespace_depth=0) + agent_update_status_response.AgentUpdateStatusResponse.model_rebuild(_parent_namespace_depth=0) + agents.evaluation_metrics.workspace_create_response.WorkspaceCreateResponse.model_rebuild(_parent_namespace_depth=0) + agents.evaluation_metrics.workspace_retrieve_response.WorkspaceRetrieveResponse.model_rebuild( + _parent_namespace_depth=0 + ) + agents.evaluation_metrics.workspace_update_response.WorkspaceUpdateResponse.model_rebuild(_parent_namespace_depth=0) + agents.evaluation_metrics.workspace_list_response.WorkspaceListResponse.model_rebuild(_parent_namespace_depth=0) + agents.evaluation_metrics.workspaces.agent_list_response.AgentListResponse.model_rebuild(_parent_namespace_depth=0) + agents.evaluation_metrics.workspaces.agent_move_response.AgentMoveResponse.model_rebuild(_parent_namespace_depth=0) + agents.evaluation_metrics.anthropic.key_list_agents_response.KeyListAgentsResponse.model_rebuild( + _parent_namespace_depth=0 + ) + agents.evaluation_metrics.openai.key_list_agents_response.KeyListAgentsResponse.model_rebuild( + _parent_namespace_depth=0 + ) + agents.function_create_response.FunctionCreateResponse.model_rebuild(_parent_namespace_depth=0) + agents.function_update_response.FunctionUpdateResponse.model_rebuild(_parent_namespace_depth=0) + agents.function_delete_response.FunctionDeleteResponse.model_rebuild(_parent_namespace_depth=0) + agents.api_link_knowledge_base_output.APILinkKnowledgeBaseOutput.model_rebuild(_parent_namespace_depth=0) + agents.knowledge_base_detach_response.KnowledgeBaseDetachResponse.model_rebuild(_parent_namespace_depth=0) + agents.route_view_response.RouteViewResponse.model_rebuild(_parent_namespace_depth=0) + models.providers.anthropic_list_agents_response.AnthropicListAgentsResponse.model_rebuild(_parent_namespace_depth=0) + models.providers.openai_retrieve_agents_response.OpenAIRetrieveAgentsResponse.model_rebuild( + _parent_namespace_depth=0 + ) diff --git a/src/gradient/types/agent_create_params.py b/src/gradient/types/agent_create_params.py new file mode 100644 index 00000000..343c5d70 --- /dev/null +++ b/src/gradient/types/agent_create_params.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["AgentCreateParams"] + + +class AgentCreateParams(TypedDict, total=False): + anthropic_key_uuid: str + """Optional Anthropic API key ID to use with Anthropic models""" + + description: str + """A text description of the agent, not used in inference""" + + instruction: str + """Agent instruction. + + Instructions help your agent to perform its job effectively. See + [Write Effective Agent Instructions](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#agent-instructions) + for best practices. + """ + + knowledge_base_uuid: SequenceNotStr[str] + """Ids of the knowledge base(s) to attach to the agent""" + + model_provider_key_uuid: str + + model_uuid: str + """Identifier for the foundation model.""" + + name: str + """Agent name""" + + openai_key_uuid: Annotated[str, PropertyInfo(alias="open_ai_key_uuid")] + """Optional OpenAI API key ID to use with OpenAI models""" + + project_id: str + """The id of the DigitalOcean project this agent will belong to""" + + region: str + """The DigitalOcean region to deploy your agent in""" + + tags: SequenceNotStr[str] + """Agent tag to organize related resources""" + + workspace_uuid: str + """Identifier for the workspace""" diff --git a/src/gradient/types/agent_create_response.py b/src/gradient/types/agent_create_response.py new file mode 100644 index 00000000..edd48b7d --- /dev/null +++ b/src/gradient/types/agent_create_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["AgentCreateResponse"] + + +class AgentCreateResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from .api_agent import APIAgent diff --git a/src/gradient/types/agent_delete_response.py b/src/gradient/types/agent_delete_response.py new file mode 100644 index 00000000..8c2b2e14 --- /dev/null +++ b/src/gradient/types/agent_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["AgentDeleteResponse"] + + +class AgentDeleteResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from .api_agent import APIAgent diff --git a/src/gradient/types/agent_list_params.py b/src/gradient/types/agent_list_params.py new file mode 100644 index 00000000..b56d0395 --- /dev/null +++ b/src/gradient/types/agent_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AgentListParams"] + + +class AgentListParams(TypedDict, total=False): + only_deployed: bool + """Only list agents that are deployed.""" + + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agent_list_response.py b/src/gradient/types/agent_list_response.py new file mode 100644 index 00000000..c461f152 --- /dev/null +++ b/src/gradient/types/agent_list_response.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .api_agent_model import APIAgentModel +from .shared.api_meta import APIMeta +from .shared.api_links import APILinks +from .api_knowledge_base import APIKnowledgeBase +from .api_retrieval_method import APIRetrievalMethod +from .api_deployment_visibility import APIDeploymentVisibility + +__all__ = [ + "AgentListResponse", + "Agent", + "AgentChatbot", + "AgentChatbotIdentifier", + "AgentDeployment", + "AgentTemplate", + "AgentTemplateGuardrail", +] + + +class AgentChatbot(BaseModel): + allowed_domains: Optional[List[str]] = None + + button_background_color: Optional[str] = None + + logo: Optional[str] = None + + name: Optional[str] = None + """Name of chatbot""" + + primary_color: Optional[str] = None + + secondary_color: Optional[str] = None + + starting_message: Optional[str] = None + + +class AgentChatbotIdentifier(BaseModel): + agent_chatbot_identifier: Optional[str] = None + """Agent chatbot identifier""" + + +class AgentDeployment(BaseModel): + created_at: Optional[datetime] = None + """Creation date / time""" + + name: Optional[str] = None + """Name""" + + status: Optional[ + Literal[ + "STATUS_UNKNOWN", + "STATUS_WAITING_FOR_DEPLOYMENT", + "STATUS_DEPLOYING", + "STATUS_RUNNING", + "STATUS_FAILED", + "STATUS_WAITING_FOR_UNDEPLOYMENT", + "STATUS_UNDEPLOYING", + "STATUS_UNDEPLOYMENT_FAILED", + "STATUS_DELETED", + "STATUS_BUILDING", + ] + ] = None + + updated_at: Optional[datetime] = None + """Last modified""" + + url: Optional[str] = None + """Access your deployed agent here""" + + uuid: Optional[str] = None + """Unique id""" + + visibility: Optional[APIDeploymentVisibility] = None + """ + - VISIBILITY_UNKNOWN: The status of the deployment is unknown + - VISIBILITY_DISABLED: The deployment is disabled and will no longer service + requests + - VISIBILITY_PLAYGROUND: Deprecated: No longer a valid state + - VISIBILITY_PUBLIC: The deployment is public and will service requests from the + public internet + - VISIBILITY_PRIVATE: The deployment is private and will only service requests + from other agents, or through API keys + """ + + +class AgentTemplateGuardrail(BaseModel): + priority: Optional[int] = None + """Priority of the guardrail""" + + uuid: Optional[str] = None + """Uuid of the guardrail""" + + +class AgentTemplate(BaseModel): + created_at: Optional[datetime] = None + """The agent template's creation date""" + + description: Optional[str] = None + """Deprecated - Use summary instead""" + + guardrails: Optional[List[AgentTemplateGuardrail]] = None + """List of guardrails associated with the agent template""" + + instruction: Optional[str] = None + """Instructions for the agent template""" + + k: Optional[int] = None + """The 'k' value for the agent template""" + + knowledge_bases: Optional[List[APIKnowledgeBase]] = None + """List of knowledge bases associated with the agent template""" + + long_description: Optional[str] = None + """The long description of the agent template""" + + max_tokens: Optional[int] = None + """The max_tokens setting for the agent template""" + + model: Optional[APIAgentModel] = None + """Description of a Model""" + + name: Optional[str] = None + """Name of the agent template""" + + short_description: Optional[str] = None + """The short description of the agent template""" + + summary: Optional[str] = None + """The summary of the agent template""" + + tags: Optional[List[str]] = None + """List of tags associated with the agent template""" + + temperature: Optional[float] = None + """The temperature setting for the agent template""" + + template_type: Optional[Literal["AGENT_TEMPLATE_TYPE_STANDARD", "AGENT_TEMPLATE_TYPE_ONE_CLICK"]] = None + """ + - AGENT_TEMPLATE_TYPE_STANDARD: The standard agent template + - AGENT_TEMPLATE_TYPE_ONE_CLICK: The one click agent template + """ + + top_p: Optional[float] = None + """The top_p setting for the agent template""" + + updated_at: Optional[datetime] = None + """The agent template's last updated date""" + + uuid: Optional[str] = None + """Unique id""" + + +class Agent(BaseModel): + chatbot: Optional[AgentChatbot] = None + """A Chatbot""" + + chatbot_identifiers: Optional[List[AgentChatbotIdentifier]] = None + """Chatbot identifiers""" + + created_at: Optional[datetime] = None + """Creation date / time""" + + deployment: Optional[AgentDeployment] = None + """Description of deployment""" + + description: Optional[str] = None + """Description of agent""" + + if_case: Optional[str] = None + """Instructions to the agent on how to use the route""" + + instruction: Optional[str] = None + """Agent instruction. + + Instructions help your agent to perform its job effectively. See + [Write Effective Agent Instructions](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#agent-instructions) + for best practices. + """ + + k: Optional[int] = None + """How many results should be considered from an attached knowledge base""" + + max_tokens: Optional[int] = None + """ + Specifies the maximum number of tokens the model can process in a single input + or output, set as a number between 1 and 512. This determines the length of each + response. + """ + + model: Optional[APIAgentModel] = None + """Description of a Model""" + + name: Optional[str] = None + """Agent name""" + + project_id: Optional[str] = None + """The DigitalOcean project ID associated with the agent""" + + provide_citations: Optional[bool] = None + """Whether the agent should provide in-response citations""" + + region: Optional[str] = None + """Region code""" + + retrieval_method: Optional[APIRetrievalMethod] = None + """ + - RETRIEVAL_METHOD_UNKNOWN: The retrieval method is unknown + - RETRIEVAL_METHOD_REWRITE: The retrieval method is rewrite + - RETRIEVAL_METHOD_STEP_BACK: The retrieval method is step back + - RETRIEVAL_METHOD_SUB_QUERIES: The retrieval method is sub queries + - RETRIEVAL_METHOD_NONE: The retrieval method is none + """ + + route_created_at: Optional[datetime] = None + """Creation of route date / time""" + + route_created_by: Optional[str] = None + """Id of user that created the route""" + + route_name: Optional[str] = None + """Route name""" + + route_uuid: Optional[str] = None + """Route uuid""" + + tags: Optional[List[str]] = None + """A set of abitrary tags to organize your agent""" + + temperature: Optional[float] = None + """Controls the model’s creativity, specified as a number between 0 and 1. + + Lower values produce more predictable and conservative responses, while higher + values encourage creativity and variation. + """ + + template: Optional[AgentTemplate] = None + """Represents an AgentTemplate entity""" + + top_p: Optional[float] = None + """ + Defines the cumulative probability threshold for word selection, specified as a + number between 0 and 1. Higher values allow for more diverse outputs, while + lower values ensure focused and coherent responses. + """ + + updated_at: Optional[datetime] = None + """Last modified""" + + url: Optional[str] = None + """Access your agent under this url""" + + user_id: Optional[str] = None + """Id of user that created the agent""" + + uuid: Optional[str] = None + """Unique agent id""" + + version_hash: Optional[str] = None + """The latest version of the agent""" + + +class AgentListResponse(BaseModel): + agents: Optional[List[Agent]] = None + """Agents""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/agent_retrieve_response.py b/src/gradient/types/agent_retrieve_response.py new file mode 100644 index 00000000..2836558b --- /dev/null +++ b/src/gradient/types/agent_retrieve_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["AgentRetrieveResponse"] + + +class AgentRetrieveResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from .api_agent import APIAgent diff --git a/src/gradient/types/agent_retrieve_usage_params.py b/src/gradient/types/agent_retrieve_usage_params.py new file mode 100644 index 00000000..f5471151 --- /dev/null +++ b/src/gradient/types/agent_retrieve_usage_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AgentRetrieveUsageParams"] + + +class AgentRetrieveUsageParams(TypedDict, total=False): + start: str + """Return all usage data from this date.""" + + stop: str + """ + Return all usage data up to this date, if omitted, will return up to the current + date. + """ diff --git a/src/gradient/types/agent_retrieve_usage_response.py b/src/gradient/types/agent_retrieve_usage_response.py new file mode 100644 index 00000000..1d65addd --- /dev/null +++ b/src/gradient/types/agent_retrieve_usage_response.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["AgentRetrieveUsageResponse", "LogInsightsUsage", "LogInsightsUsageMeasurement", "Usage", "UsageMeasurement"] + + +class LogInsightsUsageMeasurement(BaseModel): + tokens: Optional[int] = None + + usage_type: Optional[str] = None + + +class LogInsightsUsage(BaseModel): + measurements: Optional[List[LogInsightsUsageMeasurement]] = None + + resource_uuid: Optional[str] = None + + start: Optional[datetime] = None + + stop: Optional[datetime] = None + + +class UsageMeasurement(BaseModel): + tokens: Optional[int] = None + + usage_type: Optional[str] = None + + +class Usage(BaseModel): + measurements: Optional[List[UsageMeasurement]] = None + + resource_uuid: Optional[str] = None + + start: Optional[datetime] = None + + stop: Optional[datetime] = None + + +class AgentRetrieveUsageResponse(BaseModel): + log_insights_usage: Optional[LogInsightsUsage] = None + """Resource Usage Description""" + + usage: Optional[Usage] = None + """Resource Usage Description""" diff --git a/src/gradient/types/agent_update_params.py b/src/gradient/types/agent_update_params.py new file mode 100644 index 00000000..5026beaa --- /dev/null +++ b/src/gradient/types/agent_update_params.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .api_retrieval_method import APIRetrievalMethod + +__all__ = ["AgentUpdateParams"] + + +class AgentUpdateParams(TypedDict, total=False): + agent_log_insights_enabled: bool + + allowed_domains: SequenceNotStr[str] + """ + Optional list of allowed domains for the chatbot - Must use fully qualified + domain name (FQDN) such as https://example.com + """ + + anthropic_key_uuid: str + """Optional anthropic key uuid for use with anthropic models""" + + conversation_logs_enabled: bool + """Optional update of conversation logs enabled""" + + description: str + """Agent description""" + + instruction: str + """Agent instruction. + + Instructions help your agent to perform its job effectively. See + [Write Effective Agent Instructions](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#agent-instructions) + for best practices. + """ + + k: int + """How many results should be considered from an attached knowledge base""" + + max_tokens: int + """ + Specifies the maximum number of tokens the model can process in a single input + or output, set as a number between 1 and 512. This determines the length of each + response. + """ + + model_provider_key_uuid: str + """Optional Model Provider uuid for use with provider models""" + + model_uuid: str + """Identifier for the foundation model.""" + + name: str + """Agent name""" + + openai_key_uuid: Annotated[str, PropertyInfo(alias="open_ai_key_uuid")] + """Optional OpenAI key uuid for use with OpenAI models""" + + project_id: str + """The id of the DigitalOcean project this agent will belong to""" + + provide_citations: bool + + retrieval_method: APIRetrievalMethod + """ + - RETRIEVAL_METHOD_UNKNOWN: The retrieval method is unknown + - RETRIEVAL_METHOD_REWRITE: The retrieval method is rewrite + - RETRIEVAL_METHOD_STEP_BACK: The retrieval method is step back + - RETRIEVAL_METHOD_SUB_QUERIES: The retrieval method is sub queries + - RETRIEVAL_METHOD_NONE: The retrieval method is none + """ + + tags: SequenceNotStr[str] + """A set of abitrary tags to organize your agent""" + + temperature: float + """Controls the model’s creativity, specified as a number between 0 and 1. + + Lower values produce more predictable and conservative responses, while higher + values encourage creativity and variation. + """ + + top_p: float + """ + Defines the cumulative probability threshold for word selection, specified as a + number between 0 and 1. Higher values allow for more diverse outputs, while + lower values ensure focused and coherent responses. + """ + + body_uuid: Annotated[str, PropertyInfo(alias="uuid")] + """Unique agent id""" diff --git a/src/gradient/types/agent_update_response.py b/src/gradient/types/agent_update_response.py new file mode 100644 index 00000000..1976089b --- /dev/null +++ b/src/gradient/types/agent_update_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["AgentUpdateResponse"] + + +class AgentUpdateResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from .api_agent import APIAgent diff --git a/src/gradient/types/agent_update_status_params.py b/src/gradient/types/agent_update_status_params.py new file mode 100644 index 00000000..3f16fdc2 --- /dev/null +++ b/src/gradient/types/agent_update_status_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo +from .api_deployment_visibility import APIDeploymentVisibility + +__all__ = ["AgentUpdateStatusParams"] + + +class AgentUpdateStatusParams(TypedDict, total=False): + body_uuid: Annotated[str, PropertyInfo(alias="uuid")] + """Unique id""" + + visibility: APIDeploymentVisibility + """ + - VISIBILITY_UNKNOWN: The status of the deployment is unknown + - VISIBILITY_DISABLED: The deployment is disabled and will no longer service + requests + - VISIBILITY_PLAYGROUND: Deprecated: No longer a valid state + - VISIBILITY_PUBLIC: The deployment is public and will service requests from the + public internet + - VISIBILITY_PRIVATE: The deployment is private and will only service requests + from other agents, or through API keys + """ diff --git a/src/gradient/types/agent_update_status_response.py b/src/gradient/types/agent_update_status_response.py new file mode 100644 index 00000000..84457d85 --- /dev/null +++ b/src/gradient/types/agent_update_status_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["AgentUpdateStatusResponse"] + + +class AgentUpdateStatusResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from .api_agent import APIAgent diff --git a/src/gradient/types/agents/__init__.py b/src/gradient/types/agents/__init__.py new file mode 100644 index 00000000..39b82ebc --- /dev/null +++ b/src/gradient/types/agents/__init__.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .api_star_metric import APIStarMetric as APIStarMetric +from .route_add_params import RouteAddParams as RouteAddParams +from .api_evaluation_run import APIEvaluationRun as APIEvaluationRun +from .route_add_response import RouteAddResponse as RouteAddResponse +from .api_key_list_params import APIKeyListParams as APIKeyListParams +from .route_update_params import RouteUpdateParams as RouteUpdateParams +from .route_view_response import RouteViewResponse as RouteViewResponse +from .version_list_params import VersionListParams as VersionListParams +from .api_evaluation_metric import APIEvaluationMetric as APIEvaluationMetric +from .api_evaluation_prompt import APIEvaluationPrompt as APIEvaluationPrompt +from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams +from .api_key_list_response import APIKeyListResponse as APIKeyListResponse +from .api_key_update_params import APIKeyUpdateParams as APIKeyUpdateParams +from .api_star_metric_param import APIStarMetricParam as APIStarMetricParam +from .route_delete_response import RouteDeleteResponse as RouteDeleteResponse +from .route_update_response import RouteUpdateResponse as RouteUpdateResponse +from .version_list_response import VersionListResponse as VersionListResponse +from .version_update_params import VersionUpdateParams as VersionUpdateParams +from .function_create_params import FunctionCreateParams as FunctionCreateParams +from .function_update_params import FunctionUpdateParams as FunctionUpdateParams +from .api_key_create_response import APIKeyCreateResponse as APIKeyCreateResponse +from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse +from .api_key_update_response import APIKeyUpdateResponse as APIKeyUpdateResponse +from .version_update_response import VersionUpdateResponse as VersionUpdateResponse +from .api_evaluation_test_case import APIEvaluationTestCase as APIEvaluationTestCase +from .function_create_response import FunctionCreateResponse as FunctionCreateResponse +from .function_delete_response import FunctionDeleteResponse as FunctionDeleteResponse +from .function_update_response import FunctionUpdateResponse as FunctionUpdateResponse +from .api_key_regenerate_response import APIKeyRegenerateResponse as APIKeyRegenerateResponse +from .api_evaluation_metric_result import APIEvaluationMetricResult as APIEvaluationMetricResult +from .evaluation_run_create_params import EvaluationRunCreateParams as EvaluationRunCreateParams +from .api_link_knowledge_base_output import APILinkKnowledgeBaseOutput as APILinkKnowledgeBaseOutput +from .evaluation_run_create_response import EvaluationRunCreateResponse as EvaluationRunCreateResponse +from .knowledge_base_detach_response import KnowledgeBaseDetachResponse as KnowledgeBaseDetachResponse +from .evaluation_metric_list_response import EvaluationMetricListResponse as EvaluationMetricListResponse +from .evaluation_dataset_create_params import EvaluationDatasetCreateParams as EvaluationDatasetCreateParams +from .evaluation_run_retrieve_response import EvaluationRunRetrieveResponse as EvaluationRunRetrieveResponse +from .evaluation_dataset_create_response import EvaluationDatasetCreateResponse as EvaluationDatasetCreateResponse +from .evaluation_run_list_results_params import EvaluationRunListResultsParams as EvaluationRunListResultsParams +from .evaluation_test_case_create_params import EvaluationTestCaseCreateParams as EvaluationTestCaseCreateParams +from .evaluation_test_case_list_response import EvaluationTestCaseListResponse as EvaluationTestCaseListResponse +from .evaluation_test_case_update_params import EvaluationTestCaseUpdateParams as EvaluationTestCaseUpdateParams +from .evaluation_run_list_results_response import EvaluationRunListResultsResponse as EvaluationRunListResultsResponse +from .evaluation_test_case_create_response import EvaluationTestCaseCreateResponse as EvaluationTestCaseCreateResponse +from .evaluation_test_case_retrieve_params import EvaluationTestCaseRetrieveParams as EvaluationTestCaseRetrieveParams +from .evaluation_test_case_update_response import EvaluationTestCaseUpdateResponse as EvaluationTestCaseUpdateResponse +from .evaluation_metric_list_regions_params import ( + EvaluationMetricListRegionsParams as EvaluationMetricListRegionsParams, +) +from .evaluation_test_case_retrieve_response import ( + EvaluationTestCaseRetrieveResponse as EvaluationTestCaseRetrieveResponse, +) +from .evaluation_metric_list_regions_response import ( + EvaluationMetricListRegionsResponse as EvaluationMetricListRegionsResponse, +) +from .evaluation_run_retrieve_results_response import ( + EvaluationRunRetrieveResultsResponse as EvaluationRunRetrieveResultsResponse, +) +from .evaluation_test_case_list_evaluation_runs_params import ( + EvaluationTestCaseListEvaluationRunsParams as EvaluationTestCaseListEvaluationRunsParams, +) +from .evaluation_test_case_list_evaluation_runs_response import ( + EvaluationTestCaseListEvaluationRunsResponse as EvaluationTestCaseListEvaluationRunsResponse, +) +from .evaluation_dataset_create_file_upload_presigned_urls_params import ( + EvaluationDatasetCreateFileUploadPresignedURLsParams as EvaluationDatasetCreateFileUploadPresignedURLsParams, +) +from .evaluation_dataset_create_file_upload_presigned_urls_response import ( + EvaluationDatasetCreateFileUploadPresignedURLsResponse as EvaluationDatasetCreateFileUploadPresignedURLsResponse, +) diff --git a/src/gradient/types/agents/api_evaluation_metric.py b/src/gradient/types/agents/api_evaluation_metric.py new file mode 100644 index 00000000..2d3b4194 --- /dev/null +++ b/src/gradient/types/agents/api_evaluation_metric.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["APIEvaluationMetric"] + + +class APIEvaluationMetric(BaseModel): + description: Optional[str] = None + + inverted: Optional[bool] = None + """If true, the metric is inverted, meaning that a lower value is better.""" + + metric_name: Optional[str] = None + + metric_type: Optional[ + Literal["METRIC_TYPE_UNSPECIFIED", "METRIC_TYPE_GENERAL_QUALITY", "METRIC_TYPE_RAG_AND_TOOL"] + ] = None + + metric_uuid: Optional[str] = None + + metric_value_type: Optional[ + Literal[ + "METRIC_VALUE_TYPE_UNSPECIFIED", + "METRIC_VALUE_TYPE_NUMBER", + "METRIC_VALUE_TYPE_STRING", + "METRIC_VALUE_TYPE_PERCENTAGE", + ] + ] = None + + range_max: Optional[float] = None + """The maximum value for the metric.""" + + range_min: Optional[float] = None + """The minimum value for the metric.""" diff --git a/src/gradient/types/agents/api_evaluation_metric_result.py b/src/gradient/types/agents/api_evaluation_metric_result.py new file mode 100644 index 00000000..3d6ea84f --- /dev/null +++ b/src/gradient/types/agents/api_evaluation_metric_result.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["APIEvaluationMetricResult"] + + +class APIEvaluationMetricResult(BaseModel): + error_description: Optional[str] = None + """Error description if the metric could not be calculated.""" + + metric_name: Optional[str] = None + """Metric name""" + + metric_value_type: Optional[ + Literal[ + "METRIC_VALUE_TYPE_UNSPECIFIED", + "METRIC_VALUE_TYPE_NUMBER", + "METRIC_VALUE_TYPE_STRING", + "METRIC_VALUE_TYPE_PERCENTAGE", + ] + ] = None + + number_value: Optional[float] = None + """The value of the metric as a number.""" + + reasoning: Optional[str] = None + """Reasoning of the metric result.""" + + string_value: Optional[str] = None + """The value of the metric as a string.""" diff --git a/src/gradient/types/agents/api_evaluation_prompt.py b/src/gradient/types/agents/api_evaluation_prompt.py new file mode 100644 index 00000000..7471e9ae --- /dev/null +++ b/src/gradient/types/agents/api_evaluation_prompt.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .api_evaluation_metric_result import APIEvaluationMetricResult + +__all__ = ["APIEvaluationPrompt", "PromptChunk"] + + +class PromptChunk(BaseModel): + chunk_usage_pct: Optional[float] = None + """The usage percentage of the chunk.""" + + chunk_used: Optional[bool] = None + """Indicates if the chunk was used in the prompt.""" + + index_uuid: Optional[str] = None + """The index uuid (Knowledge Base) of the chunk.""" + + source_name: Optional[str] = None + """The source name for the chunk, e.g., the file name or document title.""" + + text: Optional[str] = None + """Text content of the chunk.""" + + +class APIEvaluationPrompt(BaseModel): + ground_truth: Optional[str] = None + """The ground truth for the prompt.""" + + input: Optional[str] = None + + input_tokens: Optional[str] = None + """The number of input tokens used in the prompt.""" + + output: Optional[str] = None + + output_tokens: Optional[str] = None + """The number of output tokens used in the prompt.""" + + prompt_chunks: Optional[List[PromptChunk]] = None + """The list of prompt chunks.""" + + prompt_id: Optional[int] = None + """Prompt ID""" + + prompt_level_metric_results: Optional[List[APIEvaluationMetricResult]] = None + """The metric results for the prompt.""" diff --git a/src/gradient/types/agents/api_evaluation_run.py b/src/gradient/types/agents/api_evaluation_run.py new file mode 100644 index 00000000..5a758898 --- /dev/null +++ b/src/gradient/types/agents/api_evaluation_run.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel +from .api_evaluation_metric_result import APIEvaluationMetricResult + +__all__ = ["APIEvaluationRun"] + + +class APIEvaluationRun(BaseModel): + agent_deleted: Optional[bool] = None + """Whether agent is deleted""" + + agent_name: Optional[str] = None + """Agent name""" + + agent_uuid: Optional[str] = None + """Agent UUID.""" + + agent_version_hash: Optional[str] = None + """Version hash""" + + agent_workspace_uuid: Optional[str] = None + """Agent workspace uuid""" + + created_by_user_email: Optional[str] = None + + created_by_user_id: Optional[str] = None + + error_description: Optional[str] = None + """The error description""" + + evaluation_run_uuid: Optional[str] = None + """Evaluation run UUID.""" + + evaluation_test_case_workspace_uuid: Optional[str] = None + """Evaluation test case workspace uuid""" + + finished_at: Optional[datetime] = None + """Run end time.""" + + pass_status: Optional[bool] = None + """The pass status of the evaluation run based on the star metric.""" + + queued_at: Optional[datetime] = None + """Run queued time.""" + + run_level_metric_results: Optional[List[APIEvaluationMetricResult]] = None + + run_name: Optional[str] = None + """Run name.""" + + star_metric_result: Optional[APIEvaluationMetricResult] = None + + started_at: Optional[datetime] = None + """Run start time.""" + + status: Optional[ + Literal[ + "EVALUATION_RUN_STATUS_UNSPECIFIED", + "EVALUATION_RUN_QUEUED", + "EVALUATION_RUN_RUNNING_DATASET", + "EVALUATION_RUN_EVALUATING_RESULTS", + "EVALUATION_RUN_CANCELLING", + "EVALUATION_RUN_CANCELLED", + "EVALUATION_RUN_SUCCESSFUL", + "EVALUATION_RUN_PARTIALLY_SUCCESSFUL", + "EVALUATION_RUN_FAILED", + ] + ] = None + """Evaluation Run Statuses""" + + test_case_description: Optional[str] = None + """Test case description.""" + + test_case_name: Optional[str] = None + """Test case name.""" + + test_case_uuid: Optional[str] = None + """Test-case UUID.""" + + test_case_version: Optional[int] = None + """Test-case-version.""" diff --git a/src/gradient/types/agents/api_evaluation_test_case.py b/src/gradient/types/agents/api_evaluation_test_case.py new file mode 100644 index 00000000..dc4c55f0 --- /dev/null +++ b/src/gradient/types/agents/api_evaluation_test_case.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ..._models import BaseModel +from .api_star_metric import APIStarMetric +from .api_evaluation_metric import APIEvaluationMetric + +__all__ = ["APIEvaluationTestCase", "Dataset"] + + +class Dataset(BaseModel): + created_at: Optional[datetime] = None + """Time created at.""" + + dataset_name: Optional[str] = None + """Name of the dataset.""" + + dataset_uuid: Optional[str] = None + """UUID of the dataset.""" + + file_size: Optional[str] = None + """The size of the dataset uploaded file in bytes.""" + + has_ground_truth: Optional[bool] = None + """Does the dataset have a ground truth column?""" + + row_count: Optional[int] = None + """Number of rows in the dataset.""" + + +class APIEvaluationTestCase(BaseModel): + archived_at: Optional[datetime] = None + + created_at: Optional[datetime] = None + + created_by_user_email: Optional[str] = None + + created_by_user_id: Optional[str] = None + + dataset: Optional[Dataset] = None + + dataset_name: Optional[str] = None + + dataset_uuid: Optional[str] = None + + description: Optional[str] = None + + latest_version_number_of_runs: Optional[int] = None + + metrics: Optional[List[APIEvaluationMetric]] = None + + name: Optional[str] = None + + star_metric: Optional[APIStarMetric] = None + + test_case_uuid: Optional[str] = None + + total_runs: Optional[int] = None + + updated_at: Optional[datetime] = None + + updated_by_user_email: Optional[str] = None + + updated_by_user_id: Optional[str] = None + + version: Optional[int] = None diff --git a/src/gradient/types/agents/api_key_create_params.py b/src/gradient/types/agents/api_key_create_params.py new file mode 100644 index 00000000..184c330c --- /dev/null +++ b/src/gradient/types/agents/api_key_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["APIKeyCreateParams"] + + +class APIKeyCreateParams(TypedDict, total=False): + body_agent_uuid: Annotated[str, PropertyInfo(alias="agent_uuid")] + """Agent id""" + + name: str + """A human friendly name to identify the key""" diff --git a/src/gradient/types/agents/api_key_create_response.py b/src/gradient/types/agents/api_key_create_response.py new file mode 100644 index 00000000..ed8906c8 --- /dev/null +++ b/src/gradient/types/agents/api_key_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..api_agent_api_key_info import APIAgentAPIKeyInfo + +__all__ = ["APIKeyCreateResponse"] + + +class APIKeyCreateResponse(BaseModel): + api_key_info: Optional[APIAgentAPIKeyInfo] = None + """Agent API Key Info""" diff --git a/src/gradient/types/agents/api_key_delete_response.py b/src/gradient/types/agents/api_key_delete_response.py new file mode 100644 index 00000000..1f38c52e --- /dev/null +++ b/src/gradient/types/agents/api_key_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..api_agent_api_key_info import APIAgentAPIKeyInfo + +__all__ = ["APIKeyDeleteResponse"] + + +class APIKeyDeleteResponse(BaseModel): + api_key_info: Optional[APIAgentAPIKeyInfo] = None + """Agent API Key Info""" diff --git a/src/gradient/types/agents/api_key_list_params.py b/src/gradient/types/agents/api_key_list_params.py new file mode 100644 index 00000000..1f8f96b7 --- /dev/null +++ b/src/gradient/types/agents/api_key_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["APIKeyListParams"] + + +class APIKeyListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agents/api_key_list_response.py b/src/gradient/types/agents/api_key_list_response.py new file mode 100644 index 00000000..0040e91c --- /dev/null +++ b/src/gradient/types/agents/api_key_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.api_meta import APIMeta +from ..shared.api_links import APILinks +from ..api_agent_api_key_info import APIAgentAPIKeyInfo + +__all__ = ["APIKeyListResponse"] + + +class APIKeyListResponse(BaseModel): + api_key_infos: Optional[List[APIAgentAPIKeyInfo]] = None + """Api key infos""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/agents/api_key_regenerate_response.py b/src/gradient/types/agents/api_key_regenerate_response.py new file mode 100644 index 00000000..400140fb --- /dev/null +++ b/src/gradient/types/agents/api_key_regenerate_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..api_agent_api_key_info import APIAgentAPIKeyInfo + +__all__ = ["APIKeyRegenerateResponse"] + + +class APIKeyRegenerateResponse(BaseModel): + api_key_info: Optional[APIAgentAPIKeyInfo] = None + """Agent API Key Info""" diff --git a/src/gradient/types/agents/api_key_update_params.py b/src/gradient/types/agents/api_key_update_params.py new file mode 100644 index 00000000..ba997a2f --- /dev/null +++ b/src/gradient/types/agents/api_key_update_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["APIKeyUpdateParams"] + + +class APIKeyUpdateParams(TypedDict, total=False): + path_agent_uuid: Required[Annotated[str, PropertyInfo(alias="agent_uuid")]] + + body_agent_uuid: Annotated[str, PropertyInfo(alias="agent_uuid")] + """Agent id""" + + body_api_key_uuid: Annotated[str, PropertyInfo(alias="api_key_uuid")] + """API key ID""" + + name: str + """Name""" diff --git a/src/gradient/types/agents/api_key_update_response.py b/src/gradient/types/agents/api_key_update_response.py new file mode 100644 index 00000000..56154b16 --- /dev/null +++ b/src/gradient/types/agents/api_key_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..api_agent_api_key_info import APIAgentAPIKeyInfo + +__all__ = ["APIKeyUpdateResponse"] + + +class APIKeyUpdateResponse(BaseModel): + api_key_info: Optional[APIAgentAPIKeyInfo] = None + """Agent API Key Info""" diff --git a/src/gradient/types/agents/api_link_knowledge_base_output.py b/src/gradient/types/agents/api_link_knowledge_base_output.py new file mode 100644 index 00000000..2e7cec1e --- /dev/null +++ b/src/gradient/types/agents/api_link_knowledge_base_output.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["APILinkKnowledgeBaseOutput"] + + +class APILinkKnowledgeBaseOutput(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from ..api_agent import APIAgent diff --git a/src/gradient/types/agents/api_star_metric.py b/src/gradient/types/agents/api_star_metric.py new file mode 100644 index 00000000..0d04dea9 --- /dev/null +++ b/src/gradient/types/agents/api_star_metric.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["APIStarMetric"] + + +class APIStarMetric(BaseModel): + metric_uuid: Optional[str] = None + + name: Optional[str] = None + + success_threshold: Optional[float] = None + """ + The success threshold for the star metric. This is a value that the metric must + reach to be considered successful. + """ + + success_threshold_pct: Optional[int] = None + """ + The success threshold for the star metric. This is a percentage value between 0 + and 100. + """ diff --git a/src/gradient/types/agents/api_star_metric_param.py b/src/gradient/types/agents/api_star_metric_param.py new file mode 100644 index 00000000..781fb2b1 --- /dev/null +++ b/src/gradient/types/agents/api_star_metric_param.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["APIStarMetricParam"] + + +class APIStarMetricParam(TypedDict, total=False): + metric_uuid: str + + name: str + + success_threshold: float + """ + The success threshold for the star metric. This is a value that the metric must + reach to be considered successful. + """ + + success_threshold_pct: int + """ + The success threshold for the star metric. This is a percentage value between 0 + and 100. + """ diff --git a/src/gradient/types/agents/chat/__init__.py b/src/gradient/types/agents/chat/__init__.py new file mode 100644 index 00000000..9384ac14 --- /dev/null +++ b/src/gradient/types/agents/chat/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .completion_create_response import CompletionCreateResponse as CompletionCreateResponse diff --git a/src/gradient/types/agents/chat/completion_create_params.py b/src/gradient/types/agents/chat/completion_create_params.py new file mode 100644 index 00000000..0980132e --- /dev/null +++ b/src/gradient/types/agents/chat/completion_create_params.py @@ -0,0 +1,375 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ...._types import SequenceNotStr + +__all__ = [ + "CompletionCreateParamsBase", + "Message", + "MessageChatCompletionRequestSystemMessage", + "MessageChatCompletionRequestSystemMessageContentArrayOfContentPart", + "MessageChatCompletionRequestSystemMessageContentArrayOfContentPartUnionMember1", + "MessageChatCompletionRequestDeveloperMessage", + "MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPart", + "MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPartUnionMember1", + "MessageChatCompletionRequestUserMessage", + "MessageChatCompletionRequestUserMessageContentArrayOfContentPart", + "MessageChatCompletionRequestUserMessageContentArrayOfContentPartUnionMember1", + "MessageChatCompletionRequestAssistantMessage", + "MessageChatCompletionRequestAssistantMessageContentArrayOfContentPart", + "MessageChatCompletionRequestAssistantMessageContentArrayOfContentPartUnionMember1", + "MessageChatCompletionRequestAssistantMessageToolCall", + "MessageChatCompletionRequestAssistantMessageToolCallFunction", + "MessageChatCompletionRequestToolMessage", + "StreamOptions", + "ToolChoice", + "ToolChoiceChatCompletionNamedToolChoice", + "ToolChoiceChatCompletionNamedToolChoiceFunction", + "Tool", + "ToolFunction", + "CompletionCreateParamsNonStreaming", + "CompletionCreateParamsStreaming", +] + + +class CompletionCreateParamsBase(TypedDict, total=False): + messages: Required[Iterable[Message]] + """A list of messages comprising the conversation so far.""" + + model: Required[str] + """Model ID used to generate the response.""" + + frequency_penalty: Optional[float] + """Number between -2.0 and 2.0. + + Positive values penalize new tokens based on their existing frequency in the + text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + logit_bias: Optional[Dict[str, int]] + """Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + """ + + logprobs: Optional[bool] + """Whether to return log probabilities of the output tokens or not. + + If true, returns the log probabilities of each output token returned in the + `content` of `message`. + """ + + max_completion_tokens: Optional[int] + """ + The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + """ + + max_tokens: Optional[int] + """The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + """ + + metadata: Optional[Dict[str, str]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + n: Optional[int] + """How many chat completion choices to generate for each input message. + + Note that you will be charged based on the number of generated tokens across all + of the choices. Keep `n` as `1` to minimize costs. + """ + + presence_penalty: Optional[float] + """Number between -2.0 and 2.0. + + Positive values penalize new tokens based on whether they appear in the text so + far, increasing the model's likelihood to talk about new topics. + """ + + stop: Union[Optional[str], SequenceNotStr[str], None] + """Up to 4 sequences where the API will stop generating further tokens. + + The returned text will not contain the stop sequence. + """ + + stream_options: Optional[StreamOptions] + """Options for streaming response. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + tool_choice: ToolChoice + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + """ + + tools: Iterable[Tool] + """A list of tools the model may call. + + Currently, only functions are supported as a tool. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + user: str + """ + A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + """ + + +class MessageChatCompletionRequestSystemMessageContentArrayOfContentPartUnionMember1(TypedDict, total=False): + text: Required[str] + """The text content""" + + type: Required[Literal["text"]] + """The type of content part""" + + +MessageChatCompletionRequestSystemMessageContentArrayOfContentPart: TypeAlias = Union[ + str, MessageChatCompletionRequestSystemMessageContentArrayOfContentPartUnionMember1 +] + + +class MessageChatCompletionRequestSystemMessage(TypedDict, total=False): + content: Required[Union[str, SequenceNotStr[MessageChatCompletionRequestSystemMessageContentArrayOfContentPart]]] + """The contents of the system message.""" + + role: Required[Literal["system"]] + """The role of the messages author, in this case `system`.""" + + +class MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPartUnionMember1(TypedDict, total=False): + text: Required[str] + """The text content""" + + type: Required[Literal["text"]] + """The type of content part""" + + +MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPart: TypeAlias = Union[ + str, MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPartUnionMember1 +] + + +class MessageChatCompletionRequestDeveloperMessage(TypedDict, total=False): + content: Required[Union[str, SequenceNotStr[MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPart]]] + """The contents of the developer message.""" + + role: Required[Literal["developer"]] + """The role of the messages author, in this case `developer`.""" + + +class MessageChatCompletionRequestUserMessageContentArrayOfContentPartUnionMember1(TypedDict, total=False): + text: Required[str] + """The text content""" + + type: Required[Literal["text"]] + """The type of content part""" + + +MessageChatCompletionRequestUserMessageContentArrayOfContentPart: TypeAlias = Union[ + str, MessageChatCompletionRequestUserMessageContentArrayOfContentPartUnionMember1 +] + + +class MessageChatCompletionRequestUserMessage(TypedDict, total=False): + content: Required[Union[str, SequenceNotStr[MessageChatCompletionRequestUserMessageContentArrayOfContentPart]]] + """The contents of the user message.""" + + role: Required[Literal["user"]] + """The role of the messages author, in this case `user`.""" + + +class MessageChatCompletionRequestAssistantMessageContentArrayOfContentPartUnionMember1(TypedDict, total=False): + text: Required[str] + """The text content""" + + type: Required[Literal["text"]] + """The type of content part""" + + +MessageChatCompletionRequestAssistantMessageContentArrayOfContentPart: TypeAlias = Union[ + str, MessageChatCompletionRequestAssistantMessageContentArrayOfContentPartUnionMember1 +] + + +class MessageChatCompletionRequestAssistantMessageToolCallFunction(TypedDict, total=False): + arguments: Required[str] + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Required[str] + """The name of the function to call.""" + + +class MessageChatCompletionRequestAssistantMessageToolCall(TypedDict, total=False): + id: Required[str] + """The ID of the tool call.""" + + function: Required[MessageChatCompletionRequestAssistantMessageToolCallFunction] + """The function that the model called.""" + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" + + +class MessageChatCompletionRequestAssistantMessage(TypedDict, total=False): + role: Required[Literal["assistant"]] + """The role of the messages author, in this case `assistant`.""" + + content: Union[str, SequenceNotStr[MessageChatCompletionRequestAssistantMessageContentArrayOfContentPart], None] + """The contents of the assistant message.""" + + tool_calls: Iterable[MessageChatCompletionRequestAssistantMessageToolCall] + """The tool calls generated by the model, such as function calls.""" + + +class MessageChatCompletionRequestToolMessage(TypedDict, total=False): + content: Required[str] + """The contents of the tool message.""" + + role: Required[Literal["tool"]] + """The role of the messages author, in this case `tool`.""" + + tool_call_id: Required[str] + """Tool call that this message is responding to.""" + + +Message: TypeAlias = Union[ + MessageChatCompletionRequestSystemMessage, + MessageChatCompletionRequestDeveloperMessage, + MessageChatCompletionRequestUserMessage, + MessageChatCompletionRequestAssistantMessage, + MessageChatCompletionRequestToolMessage, +] + + +class StreamOptions(TypedDict, total=False): + include_usage: bool + """If set, an additional chunk will be streamed before the `data: [DONE]` message. + + The `usage` field on this chunk shows the token usage statistics for the entire + request, and the `choices` field will always be an empty array. + + All other chunks will also include a `usage` field, but with a null value. + **NOTE:** If the stream is interrupted, you may not receive the final usage + chunk which contains the total token usage for the request. + """ + + +class ToolChoiceChatCompletionNamedToolChoiceFunction(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" + + +class ToolChoiceChatCompletionNamedToolChoice(TypedDict, total=False): + function: Required[ToolChoiceChatCompletionNamedToolChoiceFunction] + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" + + +ToolChoice: TypeAlias = Union[Literal["none", "auto", "required"], ToolChoiceChatCompletionNamedToolChoice] + + +class ToolFunction(TypedDict, total=False): + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + parameters: Dict[str, object] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](/docs/guides/function-calling) for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + Omitting `parameters` defines a function with an empty parameter list. + """ + + +class Tool(TypedDict, total=False): + function: Required[ToolFunction] + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" + + +class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + """ + + +class CompletionCreateParamsStreaming(CompletionCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + """ + + +CompletionCreateParams = Union[CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming] diff --git a/src/gradient/types/agents/chat/completion_create_response.py b/src/gradient/types/agents/chat/completion_create_response.py new file mode 100644 index 00000000..69b3d203 --- /dev/null +++ b/src/gradient/types/agents/chat/completion_create_response.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from ...shared.completion_usage import CompletionUsage +from ...shared.chat_completion_token_logprob import ChatCompletionTokenLogprob + +__all__ = [ + "CompletionCreateResponse", + "Choice", + "ChoiceLogprobs", + "ChoiceMessage", + "ChoiceMessageToolCall", + "ChoiceMessageToolCallFunction", +] + + +class ChoiceLogprobs(BaseModel): + content: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message content tokens with log probability information.""" + + refusal: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message refusal tokens with log probability information.""" + + +class ChoiceMessageToolCallFunction(BaseModel): + arguments: str + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: str + """The name of the function to call.""" + + +class ChoiceMessageToolCall(BaseModel): + id: str + """The ID of the tool call.""" + + function: ChoiceMessageToolCallFunction + """The function that the model called.""" + + type: Literal["function"] + """The type of the tool. Currently, only `function` is supported.""" + + +class ChoiceMessage(BaseModel): + content: Optional[str] = None + """The contents of the message.""" + + reasoning_content: Optional[str] = None + """The reasoning content generated by the model.""" + + refusal: Optional[str] = None + """The refusal message generated by the model.""" + + role: Literal["assistant"] + """The role of the author of this message.""" + + tool_calls: Optional[List[ChoiceMessageToolCall]] = None + """The tool calls generated by the model, such as function calls.""" + + +class Choice(BaseModel): + finish_reason: Literal["stop", "length", "tool_calls", "content_filter"] + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, or `length` if the maximum number of tokens specified in the request + was reached, `tool_calls` if the model called a tool. + """ + + index: int + """The index of the choice in the list of choices.""" + + logprobs: Optional[ChoiceLogprobs] = None + """Log probability information for the choice.""" + + message: ChoiceMessage + """A chat completion message generated by the model.""" + + +class CompletionCreateResponse(BaseModel): + id: str + """A unique identifier for the chat completion.""" + + choices: List[Choice] + """A list of chat completion choices. + + Can be more than one if `n` is greater than 1. + """ + + created: int + """The Unix timestamp (in seconds) of when the chat completion was created.""" + + model: str + """The model used for the chat completion.""" + + object: Literal["chat.completion"] + """The object type, which is always `chat.completion`.""" + + usage: Optional[CompletionUsage] = None + """Usage statistics for the completion request.""" diff --git a/src/gradient/types/agents/evaluation_dataset_create_file_upload_presigned_urls_params.py b/src/gradient/types/agents/evaluation_dataset_create_file_upload_presigned_urls_params.py new file mode 100644 index 00000000..9a4000c0 --- /dev/null +++ b/src/gradient/types/agents/evaluation_dataset_create_file_upload_presigned_urls_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import TypedDict + +__all__ = ["EvaluationDatasetCreateFileUploadPresignedURLsParams", "File"] + + +class EvaluationDatasetCreateFileUploadPresignedURLsParams(TypedDict, total=False): + files: Iterable[File] + """A list of files to generate presigned URLs for.""" + + +class File(TypedDict, total=False): + file_name: str + """Local filename""" + + file_size: str + """The size of the file in bytes.""" diff --git a/src/gradient/types/agents/evaluation_dataset_create_file_upload_presigned_urls_response.py b/src/gradient/types/agents/evaluation_dataset_create_file_upload_presigned_urls_response.py new file mode 100644 index 00000000..bee94c93 --- /dev/null +++ b/src/gradient/types/agents/evaluation_dataset_create_file_upload_presigned_urls_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["EvaluationDatasetCreateFileUploadPresignedURLsResponse", "Upload"] + + +class Upload(BaseModel): + expires_at: Optional[datetime] = None + """The time the url expires at.""" + + object_key: Optional[str] = None + """The unique object key to store the file as.""" + + original_file_name: Optional[str] = None + """The original file name.""" + + presigned_url: Optional[str] = None + """The actual presigned URL the client can use to upload the file directly.""" + + +class EvaluationDatasetCreateFileUploadPresignedURLsResponse(BaseModel): + request_id: Optional[str] = None + """The ID generated for the request for Presigned URLs.""" + + uploads: Optional[List[Upload]] = None + """A list of generated presigned URLs and object keys, one per file.""" diff --git a/src/gradient/types/agents/evaluation_dataset_create_params.py b/src/gradient/types/agents/evaluation_dataset_create_params.py new file mode 100644 index 00000000..c8a84c23 --- /dev/null +++ b/src/gradient/types/agents/evaluation_dataset_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..knowledge_bases.api_file_upload_data_source_param import APIFileUploadDataSourceParam + +__all__ = ["EvaluationDatasetCreateParams"] + + +class EvaluationDatasetCreateParams(TypedDict, total=False): + file_upload_dataset: APIFileUploadDataSourceParam + """File to upload as data source for knowledge base.""" + + name: str + """The name of the agent evaluation dataset.""" diff --git a/src/gradient/types/agents/evaluation_dataset_create_response.py b/src/gradient/types/agents/evaluation_dataset_create_response.py new file mode 100644 index 00000000..f5c7fbac --- /dev/null +++ b/src/gradient/types/agents/evaluation_dataset_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["EvaluationDatasetCreateResponse"] + + +class EvaluationDatasetCreateResponse(BaseModel): + evaluation_dataset_uuid: Optional[str] = None + """Evaluation dataset uuid.""" diff --git a/src/gradient/types/agents/evaluation_metric_list_regions_params.py b/src/gradient/types/agents/evaluation_metric_list_regions_params.py new file mode 100644 index 00000000..701e7d4e --- /dev/null +++ b/src/gradient/types/agents/evaluation_metric_list_regions_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["EvaluationMetricListRegionsParams"] + + +class EvaluationMetricListRegionsParams(TypedDict, total=False): + serves_batch: bool + """Include datacenters that are capable of running batch jobs.""" + + serves_inference: bool + """Include datacenters that serve inference.""" diff --git a/src/gradient/types/agents/evaluation_metric_list_regions_response.py b/src/gradient/types/agents/evaluation_metric_list_regions_response.py new file mode 100644 index 00000000..7246d484 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metric_list_regions_response.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["EvaluationMetricListRegionsResponse", "Region"] + + +class Region(BaseModel): + inference_url: Optional[str] = None + """Url for inference server""" + + region: Optional[str] = None + """Region code""" + + serves_batch: Optional[bool] = None + """This datacenter is capable of running batch jobs""" + + serves_inference: Optional[bool] = None + """This datacenter is capable of serving inference""" + + stream_inference_url: Optional[str] = None + """The url for the inference streaming server""" + + +class EvaluationMetricListRegionsResponse(BaseModel): + regions: Optional[List[Region]] = None + """Region code""" diff --git a/src/gradient/types/agents/evaluation_metric_list_response.py b/src/gradient/types/agents/evaluation_metric_list_response.py new file mode 100644 index 00000000..0708f1ba --- /dev/null +++ b/src/gradient/types/agents/evaluation_metric_list_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .api_evaluation_metric import APIEvaluationMetric + +__all__ = ["EvaluationMetricListResponse"] + + +class EvaluationMetricListResponse(BaseModel): + metrics: Optional[List[APIEvaluationMetric]] = None diff --git a/src/gradient/types/agents/evaluation_metrics/__init__.py b/src/gradient/types/agents/evaluation_metrics/__init__.py new file mode 100644 index 00000000..f74dce5c --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/__init__.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .workspace_create_params import WorkspaceCreateParams as WorkspaceCreateParams +from .workspace_list_response import WorkspaceListResponse as WorkspaceListResponse +from .workspace_update_params import WorkspaceUpdateParams as WorkspaceUpdateParams +from .workspace_create_response import WorkspaceCreateResponse as WorkspaceCreateResponse +from .workspace_delete_response import WorkspaceDeleteResponse as WorkspaceDeleteResponse +from .workspace_update_response import WorkspaceUpdateResponse as WorkspaceUpdateResponse +from .oauth2_generate_url_params import Oauth2GenerateURLParams as Oauth2GenerateURLParams +from .workspace_retrieve_response import WorkspaceRetrieveResponse as WorkspaceRetrieveResponse +from .oauth2_generate_url_response import Oauth2GenerateURLResponse as Oauth2GenerateURLResponse +from .scheduled_indexing_create_params import ScheduledIndexingCreateParams as ScheduledIndexingCreateParams +from .scheduled_indexing_create_response import ScheduledIndexingCreateResponse as ScheduledIndexingCreateResponse +from .scheduled_indexing_delete_response import ScheduledIndexingDeleteResponse as ScheduledIndexingDeleteResponse +from .scheduled_indexing_retrieve_response import ScheduledIndexingRetrieveResponse as ScheduledIndexingRetrieveResponse +from .workspace_list_evaluation_test_cases_response import ( + WorkspaceListEvaluationTestCasesResponse as WorkspaceListEvaluationTestCasesResponse, +) diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/__init__.py b/src/gradient/types/agents/evaluation_metrics/anthropic/__init__.py new file mode 100644 index 00000000..eb47e709 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/__init__.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .key_list_params import KeyListParams as KeyListParams +from .key_create_params import KeyCreateParams as KeyCreateParams +from .key_list_response import KeyListResponse as KeyListResponse +from .key_update_params import KeyUpdateParams as KeyUpdateParams +from .key_create_response import KeyCreateResponse as KeyCreateResponse +from .key_delete_response import KeyDeleteResponse as KeyDeleteResponse +from .key_update_response import KeyUpdateResponse as KeyUpdateResponse +from .key_retrieve_response import KeyRetrieveResponse as KeyRetrieveResponse +from .key_list_agents_params import KeyListAgentsParams as KeyListAgentsParams +from .key_list_agents_response import KeyListAgentsResponse as KeyListAgentsResponse diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_create_params.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_create_params.py new file mode 100644 index 00000000..55f44139 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KeyCreateParams"] + + +class KeyCreateParams(TypedDict, total=False): + api_key: str + """Anthropic API key""" + + name: str + """Name of the key""" diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_create_response.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_create_response.py new file mode 100644 index 00000000..24b7bbb2 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel +from ....api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["KeyCreateResponse"] + + +class KeyCreateResponse(BaseModel): + api_key_info: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_delete_response.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_delete_response.py new file mode 100644 index 00000000..b5d8584e --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel +from ....api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["KeyDeleteResponse"] + + +class KeyDeleteResponse(BaseModel): + api_key_info: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_agents_params.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_agents_params.py new file mode 100644 index 00000000..566c39f7 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_agents_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KeyListAgentsParams"] + + +class KeyListAgentsParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_agents_response.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_agents_response.py new file mode 100644 index 00000000..633211cc --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_agents_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +from ....._models import BaseModel +from ....shared.api_meta import APIMeta +from ....shared.api_links import APILinks + +__all__ = ["KeyListAgentsResponse"] + + +class KeyListAgentsResponse(BaseModel): + agents: Optional[List["APIAgent"]] = None + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" + + +from ....api_agent import APIAgent diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_params.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_params.py new file mode 100644 index 00000000..1611dc03 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KeyListParams"] + + +class KeyListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_response.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_response.py new file mode 100644 index 00000000..edc9e75a --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ....._models import BaseModel +from ....shared.api_meta import APIMeta +from ....shared.api_links import APILinks +from ....api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["KeyListResponse"] + + +class KeyListResponse(BaseModel): + api_key_infos: Optional[List[APIAnthropicAPIKeyInfo]] = None + """Api key infos""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_retrieve_response.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_retrieve_response.py new file mode 100644 index 00000000..a100ec29 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_retrieve_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel +from ....api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["KeyRetrieveResponse"] + + +class KeyRetrieveResponse(BaseModel): + api_key_info: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_update_params.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_update_params.py new file mode 100644 index 00000000..0d542bbb --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ....._utils import PropertyInfo + +__all__ = ["KeyUpdateParams"] + + +class KeyUpdateParams(TypedDict, total=False): + api_key: str + """Anthropic API key""" + + body_api_key_uuid: Annotated[str, PropertyInfo(alias="api_key_uuid")] + """API key ID""" + + name: str + """Name of the key""" diff --git a/src/gradient/types/agents/evaluation_metrics/anthropic/key_update_response.py b/src/gradient/types/agents/evaluation_metrics/anthropic/key_update_response.py new file mode 100644 index 00000000..06fa2d18 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel +from ....api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["KeyUpdateResponse"] + + +class KeyUpdateResponse(BaseModel): + api_key_info: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" diff --git a/src/gradient/types/agents/evaluation_metrics/oauth2/__init__.py b/src/gradient/types/agents/evaluation_metrics/oauth2/__init__.py new file mode 100644 index 00000000..e686ce35 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/oauth2/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .dropbox_create_tokens_params import DropboxCreateTokensParams as DropboxCreateTokensParams +from .dropbox_create_tokens_response import DropboxCreateTokensResponse as DropboxCreateTokensResponse diff --git a/src/gradient/types/agents/evaluation_metrics/oauth2/dropbox_create_tokens_params.py b/src/gradient/types/agents/evaluation_metrics/oauth2/dropbox_create_tokens_params.py new file mode 100644 index 00000000..00d22cce --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/oauth2/dropbox_create_tokens_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["DropboxCreateTokensParams"] + + +class DropboxCreateTokensParams(TypedDict, total=False): + code: str + """The oauth2 code from google""" + + redirect_url: str + """Redirect url""" diff --git a/src/gradient/types/agents/evaluation_metrics/oauth2/dropbox_create_tokens_response.py b/src/gradient/types/agents/evaluation_metrics/oauth2/dropbox_create_tokens_response.py new file mode 100644 index 00000000..816b89f4 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/oauth2/dropbox_create_tokens_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel + +__all__ = ["DropboxCreateTokensResponse"] + + +class DropboxCreateTokensResponse(BaseModel): + token: Optional[str] = None + """The access token""" + + refresh_token: Optional[str] = None + """The refresh token""" diff --git a/src/gradient/types/agents/evaluation_metrics/oauth2_generate_url_params.py b/src/gradient/types/agents/evaluation_metrics/oauth2_generate_url_params.py new file mode 100644 index 00000000..68924774 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/oauth2_generate_url_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["Oauth2GenerateURLParams"] + + +class Oauth2GenerateURLParams(TypedDict, total=False): + redirect_url: str + """The redirect url.""" + + type: str + """Type "google" / "dropbox".""" diff --git a/src/gradient/types/agents/evaluation_metrics/oauth2_generate_url_response.py b/src/gradient/types/agents/evaluation_metrics/oauth2_generate_url_response.py new file mode 100644 index 00000000..8be21b8a --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/oauth2_generate_url_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["Oauth2GenerateURLResponse"] + + +class Oauth2GenerateURLResponse(BaseModel): + url: Optional[str] = None + """The oauth2 url""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/__init__.py b/src/gradient/types/agents/evaluation_metrics/openai/__init__.py new file mode 100644 index 00000000..eb47e709 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/__init__.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .key_list_params import KeyListParams as KeyListParams +from .key_create_params import KeyCreateParams as KeyCreateParams +from .key_list_response import KeyListResponse as KeyListResponse +from .key_update_params import KeyUpdateParams as KeyUpdateParams +from .key_create_response import KeyCreateResponse as KeyCreateResponse +from .key_delete_response import KeyDeleteResponse as KeyDeleteResponse +from .key_update_response import KeyUpdateResponse as KeyUpdateResponse +from .key_retrieve_response import KeyRetrieveResponse as KeyRetrieveResponse +from .key_list_agents_params import KeyListAgentsParams as KeyListAgentsParams +from .key_list_agents_response import KeyListAgentsResponse as KeyListAgentsResponse diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_create_params.py b/src/gradient/types/agents/evaluation_metrics/openai/key_create_params.py new file mode 100644 index 00000000..5f4975dd --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KeyCreateParams"] + + +class KeyCreateParams(TypedDict, total=False): + api_key: str + """OpenAI API key""" + + name: str + """Name of the key""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_create_response.py b/src/gradient/types/agents/evaluation_metrics/openai/key_create_response.py new file mode 100644 index 00000000..4af7b872 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel +from ....api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["KeyCreateResponse"] + + +class KeyCreateResponse(BaseModel): + api_key_info: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_delete_response.py b/src/gradient/types/agents/evaluation_metrics/openai/key_delete_response.py new file mode 100644 index 00000000..f1ebc73a --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel +from ....api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["KeyDeleteResponse"] + + +class KeyDeleteResponse(BaseModel): + api_key_info: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_list_agents_params.py b/src/gradient/types/agents/evaluation_metrics/openai/key_list_agents_params.py new file mode 100644 index 00000000..566c39f7 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_list_agents_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KeyListAgentsParams"] + + +class KeyListAgentsParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_list_agents_response.py b/src/gradient/types/agents/evaluation_metrics/openai/key_list_agents_response.py new file mode 100644 index 00000000..633211cc --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_list_agents_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +from ....._models import BaseModel +from ....shared.api_meta import APIMeta +from ....shared.api_links import APILinks + +__all__ = ["KeyListAgentsResponse"] + + +class KeyListAgentsResponse(BaseModel): + agents: Optional[List["APIAgent"]] = None + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" + + +from ....api_agent import APIAgent diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_list_params.py b/src/gradient/types/agents/evaluation_metrics/openai/key_list_params.py new file mode 100644 index 00000000..1611dc03 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KeyListParams"] + + +class KeyListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_list_response.py b/src/gradient/types/agents/evaluation_metrics/openai/key_list_response.py new file mode 100644 index 00000000..00738f68 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ....._models import BaseModel +from ....shared.api_meta import APIMeta +from ....shared.api_links import APILinks +from ....api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["KeyListResponse"] + + +class KeyListResponse(BaseModel): + api_key_infos: Optional[List[APIOpenAIAPIKeyInfo]] = None + """Api key infos""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_retrieve_response.py b/src/gradient/types/agents/evaluation_metrics/openai/key_retrieve_response.py new file mode 100644 index 00000000..9ba42cd2 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_retrieve_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel +from ....api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["KeyRetrieveResponse"] + + +class KeyRetrieveResponse(BaseModel): + api_key_info: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_update_params.py b/src/gradient/types/agents/evaluation_metrics/openai/key_update_params.py new file mode 100644 index 00000000..3960cf36 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ....._utils import PropertyInfo + +__all__ = ["KeyUpdateParams"] + + +class KeyUpdateParams(TypedDict, total=False): + api_key: str + """OpenAI API key""" + + body_api_key_uuid: Annotated[str, PropertyInfo(alias="api_key_uuid")] + """API key ID""" + + name: str + """Name of the key""" diff --git a/src/gradient/types/agents/evaluation_metrics/openai/key_update_response.py b/src/gradient/types/agents/evaluation_metrics/openai/key_update_response.py new file mode 100644 index 00000000..222a8416 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel +from ....api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["KeyUpdateResponse"] + + +class KeyUpdateResponse(BaseModel): + api_key_info: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" diff --git a/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_create_params.py b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_create_params.py new file mode 100644 index 00000000..209766b4 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_create_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import TypedDict + +__all__ = ["ScheduledIndexingCreateParams"] + + +class ScheduledIndexingCreateParams(TypedDict, total=False): + days: Iterable[int] + """Days for execution (day is represented same as in a cron expression, e.g. + + Monday begins with 1 ) + """ + + knowledge_base_uuid: str + """Knowledge base uuid for which the schedule is created""" + + time: str + """Time of execution (HH:MM) UTC""" diff --git a/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_create_response.py b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_create_response.py new file mode 100644 index 00000000..c306c5b1 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_create_response.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ...._models import BaseModel + +__all__ = ["ScheduledIndexingCreateResponse", "IndexingInfo"] + + +class IndexingInfo(BaseModel): + created_at: Optional[datetime] = None + """Created at timestamp""" + + days: Optional[List[int]] = None + """Days for execution (day is represented same as in a cron expression, e.g. + + Monday begins with 1 ) + """ + + deleted_at: Optional[datetime] = None + """Deleted at timestamp (if soft deleted)""" + + is_active: Optional[bool] = None + """Whether the schedule is currently active""" + + knowledge_base_uuid: Optional[str] = None + """Knowledge base uuid associated with this schedule""" + + last_ran_at: Optional[datetime] = None + """Last time the schedule was executed""" + + next_run_at: Optional[datetime] = None + """Next scheduled run""" + + time: Optional[str] = None + """Scheduled time of execution (HH:MM:SS format)""" + + updated_at: Optional[datetime] = None + """Updated at timestamp""" + + uuid: Optional[str] = None + """Unique identifier for the scheduled indexing entry""" + + +class ScheduledIndexingCreateResponse(BaseModel): + indexing_info: Optional[IndexingInfo] = None + """Metadata for scheduled indexing entries""" diff --git a/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_delete_response.py b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_delete_response.py new file mode 100644 index 00000000..febf3759 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_delete_response.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ...._models import BaseModel + +__all__ = ["ScheduledIndexingDeleteResponse", "IndexingInfo"] + + +class IndexingInfo(BaseModel): + created_at: Optional[datetime] = None + """Created at timestamp""" + + days: Optional[List[int]] = None + """Days for execution (day is represented same as in a cron expression, e.g. + + Monday begins with 1 ) + """ + + deleted_at: Optional[datetime] = None + """Deleted at timestamp (if soft deleted)""" + + is_active: Optional[bool] = None + """Whether the schedule is currently active""" + + knowledge_base_uuid: Optional[str] = None + """Knowledge base uuid associated with this schedule""" + + last_ran_at: Optional[datetime] = None + """Last time the schedule was executed""" + + next_run_at: Optional[datetime] = None + """Next scheduled run""" + + time: Optional[str] = None + """Scheduled time of execution (HH:MM:SS format)""" + + updated_at: Optional[datetime] = None + """Updated at timestamp""" + + uuid: Optional[str] = None + """Unique identifier for the scheduled indexing entry""" + + +class ScheduledIndexingDeleteResponse(BaseModel): + indexing_info: Optional[IndexingInfo] = None + """Metadata for scheduled indexing entries""" diff --git a/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_retrieve_response.py b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_retrieve_response.py new file mode 100644 index 00000000..1776c83d --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_retrieve_response.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ...._models import BaseModel + +__all__ = ["ScheduledIndexingRetrieveResponse", "IndexingInfo"] + + +class IndexingInfo(BaseModel): + created_at: Optional[datetime] = None + """Created at timestamp""" + + days: Optional[List[int]] = None + """Days for execution (day is represented same as in a cron expression, e.g. + + Monday begins with 1 ) + """ + + deleted_at: Optional[datetime] = None + """Deleted at timestamp (if soft deleted)""" + + is_active: Optional[bool] = None + """Whether the schedule is currently active""" + + knowledge_base_uuid: Optional[str] = None + """Knowledge base uuid associated with this schedule""" + + last_ran_at: Optional[datetime] = None + """Last time the schedule was executed""" + + next_run_at: Optional[datetime] = None + """Next scheduled run""" + + time: Optional[str] = None + """Scheduled time of execution (HH:MM:SS format)""" + + updated_at: Optional[datetime] = None + """Updated at timestamp""" + + uuid: Optional[str] = None + """Unique identifier for the scheduled indexing entry""" + + +class ScheduledIndexingRetrieveResponse(BaseModel): + indexing_info: Optional[IndexingInfo] = None + """Metadata for scheduled indexing entries""" diff --git a/src/gradient/types/agents/evaluation_metrics/workspace_create_params.py b/src/gradient/types/agents/evaluation_metrics/workspace_create_params.py new file mode 100644 index 00000000..443a6f43 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspace_create_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["WorkspaceCreateParams"] + + +class WorkspaceCreateParams(TypedDict, total=False): + agent_uuids: SequenceNotStr[str] + """Ids of the agents(s) to attach to the workspace""" + + description: str + """Description of the workspace""" + + name: str + """Name of the workspace""" diff --git a/src/gradient/types/agents/evaluation_metrics/workspace_create_response.py b/src/gradient/types/agents/evaluation_metrics/workspace_create_response.py new file mode 100644 index 00000000..419ec288 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspace_create_response.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["WorkspaceCreateResponse"] + + +class WorkspaceCreateResponse(BaseModel): + workspace: Optional["APIWorkspace"] = None + + +from ...api_workspace import APIWorkspace diff --git a/src/gradient/types/agents/evaluation_metrics/workspace_delete_response.py b/src/gradient/types/agents/evaluation_metrics/workspace_delete_response.py new file mode 100644 index 00000000..3e094515 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspace_delete_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["WorkspaceDeleteResponse"] + + +class WorkspaceDeleteResponse(BaseModel): + workspace_uuid: Optional[str] = None + """Workspace""" diff --git a/src/gradient/types/agents/evaluation_metrics/workspace_list_evaluation_test_cases_response.py b/src/gradient/types/agents/evaluation_metrics/workspace_list_evaluation_test_cases_response.py new file mode 100644 index 00000000..32c613f8 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspace_list_evaluation_test_cases_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel +from ..api_evaluation_test_case import APIEvaluationTestCase + +__all__ = ["WorkspaceListEvaluationTestCasesResponse"] + + +class WorkspaceListEvaluationTestCasesResponse(BaseModel): + evaluation_test_cases: Optional[List[APIEvaluationTestCase]] = None diff --git a/src/gradient/types/agents/evaluation_metrics/workspace_list_response.py b/src/gradient/types/agents/evaluation_metrics/workspace_list_response.py new file mode 100644 index 00000000..793623dd --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspace_list_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +from ...._models import BaseModel + +__all__ = ["WorkspaceListResponse"] + + +class WorkspaceListResponse(BaseModel): + workspaces: Optional[List["APIWorkspace"]] = None + """Workspaces""" + + +from ...api_workspace import APIWorkspace diff --git a/src/gradient/types/agents/evaluation_metrics/workspace_retrieve_response.py b/src/gradient/types/agents/evaluation_metrics/workspace_retrieve_response.py new file mode 100644 index 00000000..fa4a567c --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspace_retrieve_response.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["WorkspaceRetrieveResponse"] + + +class WorkspaceRetrieveResponse(BaseModel): + workspace: Optional["APIWorkspace"] = None + + +from ...api_workspace import APIWorkspace diff --git a/src/gradient/types/agents/evaluation_metrics/workspace_update_params.py b/src/gradient/types/agents/evaluation_metrics/workspace_update_params.py new file mode 100644 index 00000000..d5906bd9 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspace_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["WorkspaceUpdateParams"] + + +class WorkspaceUpdateParams(TypedDict, total=False): + description: str + """The new description of the workspace""" + + name: str + """The new name of the workspace""" + + body_workspace_uuid: Annotated[str, PropertyInfo(alias="workspace_uuid")] + """Workspace UUID.""" diff --git a/src/gradient/types/agents/evaluation_metrics/workspace_update_response.py b/src/gradient/types/agents/evaluation_metrics/workspace_update_response.py new file mode 100644 index 00000000..77dac88c --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspace_update_response.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["WorkspaceUpdateResponse"] + + +class WorkspaceUpdateResponse(BaseModel): + workspace: Optional["APIWorkspace"] = None + + +from ...api_workspace import APIWorkspace diff --git a/src/gradient/types/agents/evaluation_metrics/workspaces/__init__.py b/src/gradient/types/agents/evaluation_metrics/workspaces/__init__.py new file mode 100644 index 00000000..9f369c7c --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspaces/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .agent_list_params import AgentListParams as AgentListParams +from .agent_move_params import AgentMoveParams as AgentMoveParams +from .agent_list_response import AgentListResponse as AgentListResponse +from .agent_move_response import AgentMoveResponse as AgentMoveResponse diff --git a/src/gradient/types/agents/evaluation_metrics/workspaces/agent_list_params.py b/src/gradient/types/agents/evaluation_metrics/workspaces/agent_list_params.py new file mode 100644 index 00000000..b56d0395 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspaces/agent_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AgentListParams"] + + +class AgentListParams(TypedDict, total=False): + only_deployed: bool + """Only list agents that are deployed.""" + + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agents/evaluation_metrics/workspaces/agent_list_response.py b/src/gradient/types/agents/evaluation_metrics/workspaces/agent_list_response.py new file mode 100644 index 00000000..6f9ea948 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspaces/agent_list_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +from ....._models import BaseModel +from ....shared.api_meta import APIMeta +from ....shared.api_links import APILinks + +__all__ = ["AgentListResponse"] + + +class AgentListResponse(BaseModel): + agents: Optional[List["APIAgent"]] = None + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" + + +from ....api_agent import APIAgent diff --git a/src/gradient/types/agents/evaluation_metrics/workspaces/agent_move_params.py b/src/gradient/types/agents/evaluation_metrics/workspaces/agent_move_params.py new file mode 100644 index 00000000..7b451084 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspaces/agent_move_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ....._types import SequenceNotStr +from ....._utils import PropertyInfo + +__all__ = ["AgentMoveParams"] + + +class AgentMoveParams(TypedDict, total=False): + agent_uuids: SequenceNotStr[str] + """Agent uuids""" + + body_workspace_uuid: Annotated[str, PropertyInfo(alias="workspace_uuid")] + """Workspace uuid to move agents to""" diff --git a/src/gradient/types/agents/evaluation_metrics/workspaces/agent_move_response.py b/src/gradient/types/agents/evaluation_metrics/workspaces/agent_move_response.py new file mode 100644 index 00000000..d2d084d5 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/workspaces/agent_move_response.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ....._models import BaseModel + +__all__ = ["AgentMoveResponse"] + + +class AgentMoveResponse(BaseModel): + workspace: Optional["APIWorkspace"] = None + + +from ....api_workspace import APIWorkspace diff --git a/src/gradient/types/agents/evaluation_run_create_params.py b/src/gradient/types/agents/evaluation_run_create_params.py new file mode 100644 index 00000000..52bbee85 --- /dev/null +++ b/src/gradient/types/agents/evaluation_run_create_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["EvaluationRunCreateParams"] + + +class EvaluationRunCreateParams(TypedDict, total=False): + agent_uuids: SequenceNotStr[str] + """Agent UUIDs to run the test case against.""" + + run_name: str + """The name of the run.""" + + test_case_uuid: str + """Test-case UUID to run""" diff --git a/src/gradient/types/agents/evaluation_run_create_response.py b/src/gradient/types/agents/evaluation_run_create_response.py new file mode 100644 index 00000000..90da2e61 --- /dev/null +++ b/src/gradient/types/agents/evaluation_run_create_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["EvaluationRunCreateResponse"] + + +class EvaluationRunCreateResponse(BaseModel): + evaluation_run_uuids: Optional[List[str]] = None diff --git a/src/gradient/types/agents/evaluation_run_list_results_params.py b/src/gradient/types/agents/evaluation_run_list_results_params.py new file mode 100644 index 00000000..bcf96c14 --- /dev/null +++ b/src/gradient/types/agents/evaluation_run_list_results_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["EvaluationRunListResultsParams"] + + +class EvaluationRunListResultsParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agents/evaluation_run_list_results_response.py b/src/gradient/types/agents/evaluation_run_list_results_response.py new file mode 100644 index 00000000..df830a5b --- /dev/null +++ b/src/gradient/types/agents/evaluation_run_list_results_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.api_meta import APIMeta +from ..shared.api_links import APILinks +from .api_evaluation_run import APIEvaluationRun +from .api_evaluation_prompt import APIEvaluationPrompt + +__all__ = ["EvaluationRunListResultsResponse"] + + +class EvaluationRunListResultsResponse(BaseModel): + evaluation_run: Optional[APIEvaluationRun] = None + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" + + prompts: Optional[List[APIEvaluationPrompt]] = None + """The prompt level results.""" diff --git a/src/gradient/types/agents/evaluation_run_retrieve_response.py b/src/gradient/types/agents/evaluation_run_retrieve_response.py new file mode 100644 index 00000000..cedba220 --- /dev/null +++ b/src/gradient/types/agents/evaluation_run_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_evaluation_run import APIEvaluationRun + +__all__ = ["EvaluationRunRetrieveResponse"] + + +class EvaluationRunRetrieveResponse(BaseModel): + evaluation_run: Optional[APIEvaluationRun] = None diff --git a/src/gradient/types/agents/evaluation_run_retrieve_results_response.py b/src/gradient/types/agents/evaluation_run_retrieve_results_response.py new file mode 100644 index 00000000..4bb70732 --- /dev/null +++ b/src/gradient/types/agents/evaluation_run_retrieve_results_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_evaluation_prompt import APIEvaluationPrompt + +__all__ = ["EvaluationRunRetrieveResultsResponse"] + + +class EvaluationRunRetrieveResultsResponse(BaseModel): + prompt: Optional[APIEvaluationPrompt] = None diff --git a/src/gradient/types/agents/evaluation_test_case_create_params.py b/src/gradient/types/agents/evaluation_test_case_create_params.py new file mode 100644 index 00000000..af49d024 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_create_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr +from .api_star_metric_param import APIStarMetricParam + +__all__ = ["EvaluationTestCaseCreateParams"] + + +class EvaluationTestCaseCreateParams(TypedDict, total=False): + dataset_uuid: str + """Dataset against which the test‑case is executed.""" + + description: str + """Description of the test case.""" + + metrics: SequenceNotStr[str] + """Full metric list to use for evaluation test case.""" + + name: str + """Name of the test case.""" + + star_metric: APIStarMetricParam + + workspace_uuid: str + """The workspace uuid.""" diff --git a/src/gradient/types/agents/evaluation_test_case_create_response.py b/src/gradient/types/agents/evaluation_test_case_create_response.py new file mode 100644 index 00000000..9f8e37f4 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["EvaluationTestCaseCreateResponse"] + + +class EvaluationTestCaseCreateResponse(BaseModel): + test_case_uuid: Optional[str] = None + """Test‑case UUID.""" diff --git a/src/gradient/types/agents/evaluation_test_case_list_evaluation_runs_params.py b/src/gradient/types/agents/evaluation_test_case_list_evaluation_runs_params.py new file mode 100644 index 00000000..7f30ee28 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_list_evaluation_runs_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["EvaluationTestCaseListEvaluationRunsParams"] + + +class EvaluationTestCaseListEvaluationRunsParams(TypedDict, total=False): + evaluation_test_case_version: int + """Version of the test case.""" diff --git a/src/gradient/types/agents/evaluation_test_case_list_evaluation_runs_response.py b/src/gradient/types/agents/evaluation_test_case_list_evaluation_runs_response.py new file mode 100644 index 00000000..d9565e97 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_list_evaluation_runs_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .api_evaluation_run import APIEvaluationRun + +__all__ = ["EvaluationTestCaseListEvaluationRunsResponse"] + + +class EvaluationTestCaseListEvaluationRunsResponse(BaseModel): + evaluation_runs: Optional[List[APIEvaluationRun]] = None + """List of evaluation runs.""" diff --git a/src/gradient/types/agents/evaluation_test_case_list_response.py b/src/gradient/types/agents/evaluation_test_case_list_response.py new file mode 100644 index 00000000..62b97961 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_list_response.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .api_evaluation_test_case import APIEvaluationTestCase + +__all__ = ["EvaluationTestCaseListResponse"] + + +class EvaluationTestCaseListResponse(BaseModel): + evaluation_test_cases: Optional[List[APIEvaluationTestCase]] = None + """ + Alternative way of authentication for internal usage only - should not be + exposed to public api + """ diff --git a/src/gradient/types/agents/evaluation_test_case_retrieve_params.py b/src/gradient/types/agents/evaluation_test_case_retrieve_params.py new file mode 100644 index 00000000..f84fe876 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_retrieve_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["EvaluationTestCaseRetrieveParams"] + + +class EvaluationTestCaseRetrieveParams(TypedDict, total=False): + evaluation_test_case_version: int + """Version of the test case.""" diff --git a/src/gradient/types/agents/evaluation_test_case_retrieve_response.py b/src/gradient/types/agents/evaluation_test_case_retrieve_response.py new file mode 100644 index 00000000..1511ba74 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_evaluation_test_case import APIEvaluationTestCase + +__all__ = ["EvaluationTestCaseRetrieveResponse"] + + +class EvaluationTestCaseRetrieveResponse(BaseModel): + evaluation_test_case: Optional[APIEvaluationTestCase] = None diff --git a/src/gradient/types/agents/evaluation_test_case_update_params.py b/src/gradient/types/agents/evaluation_test_case_update_params.py new file mode 100644 index 00000000..d707d909 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_update_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._types import SequenceNotStr +from ..._utils import PropertyInfo +from .api_star_metric_param import APIStarMetricParam + +__all__ = ["EvaluationTestCaseUpdateParams", "Metrics"] + + +class EvaluationTestCaseUpdateParams(TypedDict, total=False): + dataset_uuid: str + """Dataset against which the test‑case is executed.""" + + description: str + """Description of the test case.""" + + metrics: Metrics + + name: str + """Name of the test case.""" + + star_metric: APIStarMetricParam + + body_test_case_uuid: Annotated[str, PropertyInfo(alias="test_case_uuid")] + """Test-case UUID to update""" + + +class Metrics(TypedDict, total=False): + metric_uuids: SequenceNotStr[str] diff --git a/src/gradient/types/agents/evaluation_test_case_update_response.py b/src/gradient/types/agents/evaluation_test_case_update_response.py new file mode 100644 index 00000000..6f8e3b04 --- /dev/null +++ b/src/gradient/types/agents/evaluation_test_case_update_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["EvaluationTestCaseUpdateResponse"] + + +class EvaluationTestCaseUpdateResponse(BaseModel): + test_case_uuid: Optional[str] = None + + version: Optional[int] = None + """The new verson of the test case.""" diff --git a/src/gradient/types/agents/function_create_params.py b/src/gradient/types/agents/function_create_params.py new file mode 100644 index 00000000..000de32b --- /dev/null +++ b/src/gradient/types/agents/function_create_params.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["FunctionCreateParams"] + + +class FunctionCreateParams(TypedDict, total=False): + body_agent_uuid: Annotated[str, PropertyInfo(alias="agent_uuid")] + """Agent id""" + + description: str + """Function description""" + + faas_name: str + """The name of the function in the DigitalOcean functions platform""" + + faas_namespace: str + """The namespace of the function in the DigitalOcean functions platform""" + + function_name: str + """Function name""" + + input_schema: object + """Describe the input schema for the function so the agent may call it""" + + output_schema: object + """Describe the output schema for the function so the agent handle its response""" diff --git a/src/gradient/types/agents/function_create_response.py b/src/gradient/types/agents/function_create_response.py new file mode 100644 index 00000000..65a4bb2b --- /dev/null +++ b/src/gradient/types/agents/function_create_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["FunctionCreateResponse"] + + +class FunctionCreateResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from ..api_agent import APIAgent diff --git a/src/gradient/types/agents/function_delete_response.py b/src/gradient/types/agents/function_delete_response.py new file mode 100644 index 00000000..26ad02e6 --- /dev/null +++ b/src/gradient/types/agents/function_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["FunctionDeleteResponse"] + + +class FunctionDeleteResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from ..api_agent import APIAgent diff --git a/src/gradient/types/agents/function_update_params.py b/src/gradient/types/agents/function_update_params.py new file mode 100644 index 00000000..67c6ea9b --- /dev/null +++ b/src/gradient/types/agents/function_update_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["FunctionUpdateParams"] + + +class FunctionUpdateParams(TypedDict, total=False): + path_agent_uuid: Required[Annotated[str, PropertyInfo(alias="agent_uuid")]] + + body_agent_uuid: Annotated[str, PropertyInfo(alias="agent_uuid")] + """Agent id""" + + description: str + """Funciton description""" + + faas_name: str + """The name of the function in the DigitalOcean functions platform""" + + faas_namespace: str + """The namespace of the function in the DigitalOcean functions platform""" + + function_name: str + """Function name""" + + body_function_uuid: Annotated[str, PropertyInfo(alias="function_uuid")] + """Function id""" + + input_schema: object + """Describe the input schema for the function so the agent may call it""" + + output_schema: object + """Describe the output schema for the function so the agent handle its response""" diff --git a/src/gradient/types/agents/function_update_response.py b/src/gradient/types/agents/function_update_response.py new file mode 100644 index 00000000..eebde3e6 --- /dev/null +++ b/src/gradient/types/agents/function_update_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["FunctionUpdateResponse"] + + +class FunctionUpdateResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from ..api_agent import APIAgent diff --git a/src/gradient/types/agents/knowledge_base_detach_response.py b/src/gradient/types/agents/knowledge_base_detach_response.py new file mode 100644 index 00000000..0dc90aaf --- /dev/null +++ b/src/gradient/types/agents/knowledge_base_detach_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["KnowledgeBaseDetachResponse"] + + +class KnowledgeBaseDetachResponse(BaseModel): + agent: Optional["APIAgent"] = None + """An Agent""" + + +from ..api_agent import APIAgent diff --git a/src/gradient/types/agents/route_add_params.py b/src/gradient/types/agents/route_add_params.py new file mode 100644 index 00000000..d8dbeff8 --- /dev/null +++ b/src/gradient/types/agents/route_add_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["RouteAddParams"] + + +class RouteAddParams(TypedDict, total=False): + path_parent_agent_uuid: Required[Annotated[str, PropertyInfo(alias="parent_agent_uuid")]] + + body_child_agent_uuid: Annotated[str, PropertyInfo(alias="child_agent_uuid")] + """Routed agent id""" + + if_case: str + + body_parent_agent_uuid: Annotated[str, PropertyInfo(alias="parent_agent_uuid")] + """A unique identifier for the parent agent.""" + + route_name: str + """Name of route""" diff --git a/src/gradient/types/agents/route_add_response.py b/src/gradient/types/agents/route_add_response.py new file mode 100644 index 00000000..b9cc2b7d --- /dev/null +++ b/src/gradient/types/agents/route_add_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RouteAddResponse"] + + +class RouteAddResponse(BaseModel): + child_agent_uuid: Optional[str] = None + """Routed agent id""" + + parent_agent_uuid: Optional[str] = None + """A unique identifier for the parent agent.""" diff --git a/src/gradient/types/agents/route_delete_response.py b/src/gradient/types/agents/route_delete_response.py new file mode 100644 index 00000000..b49c8b7c --- /dev/null +++ b/src/gradient/types/agents/route_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RouteDeleteResponse"] + + +class RouteDeleteResponse(BaseModel): + child_agent_uuid: Optional[str] = None + """Routed agent id""" + + parent_agent_uuid: Optional[str] = None + """Pagent agent id""" diff --git a/src/gradient/types/agents/route_update_params.py b/src/gradient/types/agents/route_update_params.py new file mode 100644 index 00000000..453a3b93 --- /dev/null +++ b/src/gradient/types/agents/route_update_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["RouteUpdateParams"] + + +class RouteUpdateParams(TypedDict, total=False): + path_parent_agent_uuid: Required[Annotated[str, PropertyInfo(alias="parent_agent_uuid")]] + + body_child_agent_uuid: Annotated[str, PropertyInfo(alias="child_agent_uuid")] + """Routed agent id""" + + if_case: str + """Describes the case in which the child agent should be used""" + + body_parent_agent_uuid: Annotated[str, PropertyInfo(alias="parent_agent_uuid")] + """A unique identifier for the parent agent.""" + + route_name: str + """Route name""" + + uuid: str + """Unique id of linkage""" diff --git a/src/gradient/types/agents/route_update_response.py b/src/gradient/types/agents/route_update_response.py new file mode 100644 index 00000000..b79fc9fe --- /dev/null +++ b/src/gradient/types/agents/route_update_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RouteUpdateResponse"] + + +class RouteUpdateResponse(BaseModel): + child_agent_uuid: Optional[str] = None + """Routed agent id""" + + parent_agent_uuid: Optional[str] = None + """A unique identifier for the parent agent.""" + + rollback: Optional[bool] = None + + uuid: Optional[str] = None + """Unique id of linkage""" diff --git a/src/gradient/types/agents/route_view_response.py b/src/gradient/types/agents/route_view_response.py new file mode 100644 index 00000000..f0ee2d71 --- /dev/null +++ b/src/gradient/types/agents/route_view_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["RouteViewResponse"] + + +class RouteViewResponse(BaseModel): + children: Optional[List["APIAgent"]] = None + """Child agents""" + + +from ..api_agent import APIAgent diff --git a/src/gradient/types/agents/version_list_params.py b/src/gradient/types/agents/version_list_params.py new file mode 100644 index 00000000..e8fa2f6d --- /dev/null +++ b/src/gradient/types/agents/version_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["VersionListParams"] + + +class VersionListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/agents/version_list_response.py b/src/gradient/types/agents/version_list_response.py new file mode 100644 index 00000000..c35a5ba4 --- /dev/null +++ b/src/gradient/types/agents/version_list_response.py @@ -0,0 +1,167 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from ..shared.api_meta import APIMeta +from ..shared.api_links import APILinks +from ..api_retrieval_method import APIRetrievalMethod + +__all__ = [ + "VersionListResponse", + "AgentVersion", + "AgentVersionAttachedChildAgent", + "AgentVersionAttachedFunction", + "AgentVersionAttachedGuardrail", + "AgentVersionAttachedKnowledgebase", +] + + +class AgentVersionAttachedChildAgent(BaseModel): + agent_name: Optional[str] = None + """Name of the child agent""" + + child_agent_uuid: Optional[str] = None + """Child agent unique identifier""" + + if_case: Optional[str] = None + """If case""" + + is_deleted: Optional[bool] = None + """Child agent is deleted""" + + route_name: Optional[str] = None + """Route name""" + + +class AgentVersionAttachedFunction(BaseModel): + description: Optional[str] = None + """Description of the function""" + + faas_name: Optional[str] = None + """FaaS name of the function""" + + faas_namespace: Optional[str] = None + """FaaS namespace of the function""" + + is_deleted: Optional[bool] = None + """Whether the function is deleted""" + + name: Optional[str] = None + """Name of the function""" + + +class AgentVersionAttachedGuardrail(BaseModel): + is_deleted: Optional[bool] = None + """Whether the guardrail is deleted""" + + name: Optional[str] = None + """Guardrail Name""" + + priority: Optional[int] = None + """Guardrail Priority""" + + uuid: Optional[str] = None + """Guardrail UUID""" + + +class AgentVersionAttachedKnowledgebase(BaseModel): + is_deleted: Optional[bool] = None + """Deletet at date / time""" + + name: Optional[str] = None + """Name of the knowledge base""" + + uuid: Optional[str] = None + """Unique id of the knowledge base""" + + +class AgentVersion(BaseModel): + id: Optional[str] = None + """Unique identifier""" + + agent_uuid: Optional[str] = None + """Uuid of the agent this version belongs to""" + + attached_child_agents: Optional[List[AgentVersionAttachedChildAgent]] = None + """List of child agent relationships""" + + attached_functions: Optional[List[AgentVersionAttachedFunction]] = None + """List of function versions""" + + attached_guardrails: Optional[List[AgentVersionAttachedGuardrail]] = None + """List of guardrail version""" + + attached_knowledgebases: Optional[List[AgentVersionAttachedKnowledgebase]] = None + """List of knowledge base agent versions""" + + can_rollback: Optional[bool] = None + """Whether the version is able to be rolled back to""" + + created_at: Optional[datetime] = None + """Creation date""" + + created_by_email: Optional[str] = None + """User who created this version""" + + currently_applied: Optional[bool] = None + """Whether this is the currently applied configuration""" + + description: Optional[str] = None + """Description of the agent""" + + instruction: Optional[str] = None + """Instruction for the agent""" + + k: Optional[int] = None + """K value for the agent's configuration""" + + max_tokens: Optional[int] = None + """Max tokens setting for the agent""" + + model: Optional[str] = FieldInfo(alias="model_name", default=None) + """Name of model associated to the agent version""" + + name: Optional[str] = None + """Name of the agent""" + + provide_citations: Optional[bool] = None + """Whether the agent should provide in-response citations""" + + retrieval_method: Optional[APIRetrievalMethod] = None + """ + - RETRIEVAL_METHOD_UNKNOWN: The retrieval method is unknown + - RETRIEVAL_METHOD_REWRITE: The retrieval method is rewrite + - RETRIEVAL_METHOD_STEP_BACK: The retrieval method is step back + - RETRIEVAL_METHOD_SUB_QUERIES: The retrieval method is sub queries + - RETRIEVAL_METHOD_NONE: The retrieval method is none + """ + + tags: Optional[List[str]] = None + """Tags associated with the agent""" + + temperature: Optional[float] = None + """Temperature setting for the agent""" + + top_p: Optional[float] = None + """Top_p setting for the agent""" + + trigger_action: Optional[str] = None + """Action triggering the configuration update""" + + version_hash: Optional[str] = None + """Version hash""" + + +class VersionListResponse(BaseModel): + agent_versions: Optional[List[AgentVersion]] = None + """Agents""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/agents/version_update_params.py b/src/gradient/types/agents/version_update_params.py new file mode 100644 index 00000000..212eb05c --- /dev/null +++ b/src/gradient/types/agents/version_update_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["VersionUpdateParams"] + + +class VersionUpdateParams(TypedDict, total=False): + body_uuid: Annotated[str, PropertyInfo(alias="uuid")] + """Agent unique identifier""" + + version_hash: str + """Unique identifier""" diff --git a/src/gradient/types/agents/version_update_response.py b/src/gradient/types/agents/version_update_response.py new file mode 100644 index 00000000..464ef12f --- /dev/null +++ b/src/gradient/types/agents/version_update_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["VersionUpdateResponse", "AuditHeader"] + + +class AuditHeader(BaseModel): + actor_id: Optional[str] = None + + actor_ip: Optional[str] = None + + actor_uuid: Optional[str] = None + + context_urn: Optional[str] = None + + origin_application: Optional[str] = None + + user_id: Optional[str] = None + + user_uuid: Optional[str] = None + + +class VersionUpdateResponse(BaseModel): + audit_header: Optional[AuditHeader] = None + """An alternative way to provide auth information. for internal use only.""" + + version_hash: Optional[str] = None + """Unique identifier""" diff --git a/src/gradient/types/api_agent.py b/src/gradient/types/api_agent.py new file mode 100644 index 00000000..f52e44c8 --- /dev/null +++ b/src/gradient/types/api_agent.py @@ -0,0 +1,418 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel +from .api_agent_model import APIAgentModel +from .api_knowledge_base import APIKnowledgeBase +from .api_retrieval_method import APIRetrievalMethod +from .api_agent_api_key_info import APIAgentAPIKeyInfo +from .api_openai_api_key_info import APIOpenAIAPIKeyInfo +from .api_deployment_visibility import APIDeploymentVisibility +from .api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = [ + "APIAgent", + "APIKey", + "Chatbot", + "ChatbotIdentifier", + "Deployment", + "Function", + "Guardrail", + "LoggingConfig", + "ModelProviderKey", + "Template", + "TemplateGuardrail", +] + + +class APIKey(BaseModel): + api_key: Optional[str] = None + """Api key""" + + +class Chatbot(BaseModel): + allowed_domains: Optional[List[str]] = None + + button_background_color: Optional[str] = None + + logo: Optional[str] = None + + name: Optional[str] = None + """Name of chatbot""" + + primary_color: Optional[str] = None + + secondary_color: Optional[str] = None + + starting_message: Optional[str] = None + + +class ChatbotIdentifier(BaseModel): + agent_chatbot_identifier: Optional[str] = None + """Agent chatbot identifier""" + + +class Deployment(BaseModel): + created_at: Optional[datetime] = None + """Creation date / time""" + + name: Optional[str] = None + """Name""" + + status: Optional[ + Literal[ + "STATUS_UNKNOWN", + "STATUS_WAITING_FOR_DEPLOYMENT", + "STATUS_DEPLOYING", + "STATUS_RUNNING", + "STATUS_FAILED", + "STATUS_WAITING_FOR_UNDEPLOYMENT", + "STATUS_UNDEPLOYING", + "STATUS_UNDEPLOYMENT_FAILED", + "STATUS_DELETED", + "STATUS_BUILDING", + ] + ] = None + + updated_at: Optional[datetime] = None + """Last modified""" + + url: Optional[str] = None + """Access your deployed agent here""" + + uuid: Optional[str] = None + """Unique id""" + + visibility: Optional[APIDeploymentVisibility] = None + """ + - VISIBILITY_UNKNOWN: The status of the deployment is unknown + - VISIBILITY_DISABLED: The deployment is disabled and will no longer service + requests + - VISIBILITY_PLAYGROUND: Deprecated: No longer a valid state + - VISIBILITY_PUBLIC: The deployment is public and will service requests from the + public internet + - VISIBILITY_PRIVATE: The deployment is private and will only service requests + from other agents, or through API keys + """ + + +class Function(BaseModel): + api_key: Optional[str] = None + """Api key""" + + created_at: Optional[datetime] = None + """Creation date / time""" + + created_by: Optional[str] = None + """Created by user id from DO""" + + description: Optional[str] = None + """Agent description""" + + faas_name: Optional[str] = None + + faas_namespace: Optional[str] = None + + input_schema: Optional[object] = None + + name: Optional[str] = None + """Name""" + + output_schema: Optional[object] = None + + updated_at: Optional[datetime] = None + """Last modified""" + + url: Optional[str] = None + """Download your agent here""" + + uuid: Optional[str] = None + """Unique id""" + + +class Guardrail(BaseModel): + agent_uuid: Optional[str] = None + + created_at: Optional[datetime] = None + + default_response: Optional[str] = None + + description: Optional[str] = None + + guardrail_uuid: Optional[str] = None + + is_attached: Optional[bool] = None + + is_default: Optional[bool] = None + + metadata: Optional[object] = None + + name: Optional[str] = None + + priority: Optional[int] = None + + type: Optional[ + Literal[ + "GUARDRAIL_TYPE_UNKNOWN", + "GUARDRAIL_TYPE_JAILBREAK", + "GUARDRAIL_TYPE_SENSITIVE_DATA", + "GUARDRAIL_TYPE_CONTENT_MODERATION", + ] + ] = None + + updated_at: Optional[datetime] = None + + uuid: Optional[str] = None + + +class LoggingConfig(BaseModel): + galileo_project_id: Optional[str] = None + """Galileo project identifier""" + + galileo_project_name: Optional[str] = None + """Name of the Galileo project""" + + insights_enabled: Optional[bool] = None + """Whether insights are enabled""" + + insights_enabled_at: Optional[datetime] = None + """Timestamp when insights were enabled""" + + log_stream_id: Optional[str] = None + """Identifier for the log stream""" + + log_stream_name: Optional[str] = None + """Name of the log stream""" + + +class ModelProviderKey(BaseModel): + api_key_uuid: Optional[str] = None + """API key ID""" + + created_at: Optional[datetime] = None + """Key creation date""" + + created_by: Optional[str] = None + """Created by user id from DO""" + + deleted_at: Optional[datetime] = None + """Key deleted date""" + + models: Optional[List[APIAgentModel]] = None + """Models supported by the openAI api key""" + + name: Optional[str] = None + """Name of the key""" + + provider: Optional[Literal["MODEL_PROVIDER_DIGITALOCEAN", "MODEL_PROVIDER_ANTHROPIC", "MODEL_PROVIDER_OPENAI"]] = ( + None + ) + + updated_at: Optional[datetime] = None + """Key last updated date""" + + +class TemplateGuardrail(BaseModel): + priority: Optional[int] = None + """Priority of the guardrail""" + + uuid: Optional[str] = None + """Uuid of the guardrail""" + + +class Template(BaseModel): + created_at: Optional[datetime] = None + """The agent template's creation date""" + + description: Optional[str] = None + """Deprecated - Use summary instead""" + + guardrails: Optional[List[TemplateGuardrail]] = None + """List of guardrails associated with the agent template""" + + instruction: Optional[str] = None + """Instructions for the agent template""" + + k: Optional[int] = None + """The 'k' value for the agent template""" + + knowledge_bases: Optional[List[APIKnowledgeBase]] = None + """List of knowledge bases associated with the agent template""" + + long_description: Optional[str] = None + """The long description of the agent template""" + + max_tokens: Optional[int] = None + """The max_tokens setting for the agent template""" + + model: Optional[APIAgentModel] = None + """Description of a Model""" + + name: Optional[str] = None + """Name of the agent template""" + + short_description: Optional[str] = None + """The short description of the agent template""" + + summary: Optional[str] = None + """The summary of the agent template""" + + tags: Optional[List[str]] = None + """List of tags associated with the agent template""" + + temperature: Optional[float] = None + """The temperature setting for the agent template""" + + template_type: Optional[Literal["AGENT_TEMPLATE_TYPE_STANDARD", "AGENT_TEMPLATE_TYPE_ONE_CLICK"]] = None + """ + - AGENT_TEMPLATE_TYPE_STANDARD: The standard agent template + - AGENT_TEMPLATE_TYPE_ONE_CLICK: The one click agent template + """ + + top_p: Optional[float] = None + """The top_p setting for the agent template""" + + updated_at: Optional[datetime] = None + """The agent template's last updated date""" + + uuid: Optional[str] = None + """Unique id""" + + +class APIAgent(BaseModel): + anthropic_api_key: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" + + api_key_infos: Optional[List[APIAgentAPIKeyInfo]] = None + """Api key infos""" + + api_keys: Optional[List[APIKey]] = None + """Api keys""" + + chatbot: Optional[Chatbot] = None + """A Chatbot""" + + chatbot_identifiers: Optional[List[ChatbotIdentifier]] = None + """Chatbot identifiers""" + + child_agents: Optional[List["APIAgent"]] = None + """Child agents""" + + conversation_logs_enabled: Optional[bool] = None + """Whether conversation logs are enabled for the agent""" + + created_at: Optional[datetime] = None + """Creation date / time""" + + deployment: Optional[Deployment] = None + """Description of deployment""" + + description: Optional[str] = None + """Description of agent""" + + functions: Optional[List[Function]] = None + + guardrails: Optional[List[Guardrail]] = None + """The guardrails the agent is attached to""" + + if_case: Optional[str] = None + + instruction: Optional[str] = None + """Agent instruction. + + Instructions help your agent to perform its job effectively. See + [Write Effective Agent Instructions](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#agent-instructions) + for best practices. + """ + + k: Optional[int] = None + + knowledge_bases: Optional[List[APIKnowledgeBase]] = None + """Knowledge bases""" + + logging_config: Optional[LoggingConfig] = None + + max_tokens: Optional[int] = None + + model: Optional[APIAgentModel] = None + """Description of a Model""" + + api_model_provider_key: Optional[ModelProviderKey] = FieldInfo(alias="model_provider_key", default=None) + + name: Optional[str] = None + """Agent name""" + + openai_api_key: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" + + parent_agents: Optional[List["APIAgent"]] = None + """Parent agents""" + + project_id: Optional[str] = None + + provide_citations: Optional[bool] = None + """Whether the agent should provide in-response citations""" + + region: Optional[str] = None + """Region code""" + + retrieval_method: Optional[APIRetrievalMethod] = None + """ + - RETRIEVAL_METHOD_UNKNOWN: The retrieval method is unknown + - RETRIEVAL_METHOD_REWRITE: The retrieval method is rewrite + - RETRIEVAL_METHOD_STEP_BACK: The retrieval method is step back + - RETRIEVAL_METHOD_SUB_QUERIES: The retrieval method is sub queries + - RETRIEVAL_METHOD_NONE: The retrieval method is none + """ + + route_created_at: Optional[datetime] = None + """Creation of route date / time""" + + route_created_by: Optional[str] = None + + route_name: Optional[str] = None + """Route name""" + + route_uuid: Optional[str] = None + + tags: Optional[List[str]] = None + """Agent tag to organize related resources""" + + temperature: Optional[float] = None + + template: Optional[Template] = None + """Represents an AgentTemplate entity""" + + top_p: Optional[float] = None + + updated_at: Optional[datetime] = None + """Last modified""" + + url: Optional[str] = None + """Access your agent under this url""" + + user_id: Optional[str] = None + """Id of user that created the agent""" + + uuid: Optional[str] = None + """Unique agent id""" + + version_hash: Optional[str] = None + """The latest version of the agent""" + + vpc_egress_ips: Optional[List[str]] = None + """VPC Egress IPs""" + + vpc_uuid: Optional[str] = None + + workspace: Optional["APIWorkspace"] = None + + +from .api_workspace import APIWorkspace diff --git a/src/gradient/types/api_agent_api_key_info.py b/src/gradient/types/api_agent_api_key_info.py new file mode 100644 index 00000000..7222153c --- /dev/null +++ b/src/gradient/types/api_agent_api_key_info.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["APIAgentAPIKeyInfo"] + + +class APIAgentAPIKeyInfo(BaseModel): + created_at: Optional[datetime] = None + """Creation date""" + + created_by: Optional[str] = None + """Created by""" + + deleted_at: Optional[datetime] = None + """Deleted date""" + + name: Optional[str] = None + """Name""" + + secret_key: Optional[str] = None + + uuid: Optional[str] = None + """Uuid""" diff --git a/src/gradient/types/api_agent_model.py b/src/gradient/types/api_agent_model.py new file mode 100644 index 00000000..f111bfb7 --- /dev/null +++ b/src/gradient/types/api_agent_model.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .api_agreement import APIAgreement +from .api_model_version import APIModelVersion + +__all__ = ["APIAgentModel"] + + +class APIAgentModel(BaseModel): + agreement: Optional[APIAgreement] = None + """Agreement Description""" + + created_at: Optional[datetime] = None + """Creation date / time""" + + inference_name: Optional[str] = None + """Internally used name""" + + inference_version: Optional[str] = None + """Internally used version""" + + is_foundational: Optional[bool] = None + """True if it is a foundational model provided by do""" + + metadata: Optional[object] = None + """Additional meta data""" + + name: Optional[str] = None + """Name of the model""" + + parent_uuid: Optional[str] = None + """Unique id of the model, this model is based on""" + + provider: Optional[Literal["MODEL_PROVIDER_DIGITALOCEAN", "MODEL_PROVIDER_ANTHROPIC", "MODEL_PROVIDER_OPENAI"]] = ( + None + ) + + updated_at: Optional[datetime] = None + """Last modified""" + + upload_complete: Optional[bool] = None + """Model has been fully uploaded""" + + url: Optional[str] = None + """Download url""" + + usecases: Optional[ + List[ + Literal[ + "MODEL_USECASE_UNKNOWN", + "MODEL_USECASE_AGENT", + "MODEL_USECASE_FINETUNED", + "MODEL_USECASE_KNOWLEDGEBASE", + "MODEL_USECASE_GUARDRAIL", + "MODEL_USECASE_REASONING", + "MODEL_USECASE_SERVERLESS", + ] + ] + ] = None + """Usecases of the model""" + + uuid: Optional[str] = None + """Unique id""" + + version: Optional[APIModelVersion] = None + """Version Information about a Model""" diff --git a/src/gradient/types/api_agreement.py b/src/gradient/types/api_agreement.py new file mode 100644 index 00000000..c4359f1f --- /dev/null +++ b/src/gradient/types/api_agreement.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["APIAgreement"] + + +class APIAgreement(BaseModel): + description: Optional[str] = None + + name: Optional[str] = None + + url: Optional[str] = None + + uuid: Optional[str] = None diff --git a/src/gradient/types/api_anthropic_api_key_info.py b/src/gradient/types/api_anthropic_api_key_info.py new file mode 100644 index 00000000..6440c5ef --- /dev/null +++ b/src/gradient/types/api_anthropic_api_key_info.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["APIAnthropicAPIKeyInfo"] + + +class APIAnthropicAPIKeyInfo(BaseModel): + created_at: Optional[datetime] = None + """Key creation date""" + + created_by: Optional[str] = None + """Created by user id from DO""" + + deleted_at: Optional[datetime] = None + """Key deleted date""" + + name: Optional[str] = None + """Name""" + + updated_at: Optional[datetime] = None + """Key last updated date""" + + uuid: Optional[str] = None + """Uuid""" diff --git a/src/gradient/types/api_deployment_visibility.py b/src/gradient/types/api_deployment_visibility.py new file mode 100644 index 00000000..a63e3acd --- /dev/null +++ b/src/gradient/types/api_deployment_visibility.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["APIDeploymentVisibility"] + +APIDeploymentVisibility: TypeAlias = Literal[ + "VISIBILITY_UNKNOWN", "VISIBILITY_DISABLED", "VISIBILITY_PLAYGROUND", "VISIBILITY_PUBLIC", "VISIBILITY_PRIVATE" +] diff --git a/src/gradient/types/api_knowledge_base.py b/src/gradient/types/api_knowledge_base.py new file mode 100644 index 00000000..4e4a6567 --- /dev/null +++ b/src/gradient/types/api_knowledge_base.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from .._models import BaseModel +from .knowledge_bases.api_indexing_job import APIIndexingJob + +__all__ = ["APIKnowledgeBase"] + + +class APIKnowledgeBase(BaseModel): + added_to_agent_at: Optional[datetime] = None + """Time when the knowledge base was added to the agent""" + + created_at: Optional[datetime] = None + """Creation date / time""" + + database_id: Optional[str] = None + + embedding_model_uuid: Optional[str] = None + + is_public: Optional[bool] = None + """Whether the knowledge base is public or not""" + + last_indexing_job: Optional[APIIndexingJob] = None + """IndexingJob description""" + + name: Optional[str] = None + """Name of knowledge base""" + + project_id: Optional[str] = None + + region: Optional[str] = None + """Region code""" + + tags: Optional[List[str]] = None + """Tags to organize related resources""" + + updated_at: Optional[datetime] = None + """Last modified""" + + user_id: Optional[str] = None + """Id of user that created the knowledge base""" + + uuid: Optional[str] = None + """Unique id for knowledge base""" diff --git a/src/gradient/types/api_model.py b/src/gradient/types/api_model.py new file mode 100644 index 00000000..e7f99bc1 --- /dev/null +++ b/src/gradient/types/api_model.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel +from .api_agreement import APIAgreement +from .api_model_version import APIModelVersion + +__all__ = ["APIModel"] + + +class APIModel(BaseModel): + id: Optional[str] = None + """Human-readable model identifier""" + + agreement: Optional[APIAgreement] = None + """Agreement Description""" + + created_at: Optional[datetime] = None + """Creation date / time""" + + is_foundational: Optional[bool] = None + """True if it is a foundational model provided by do""" + + name: Optional[str] = None + """Display name of the model""" + + parent_uuid: Optional[str] = None + """Unique id of the model, this model is based on""" + + updated_at: Optional[datetime] = None + """Last modified""" + + upload_complete: Optional[bool] = None + """Model has been fully uploaded""" + + url: Optional[str] = None + """Download url""" + + uuid: Optional[str] = None + """Unique id""" + + version: Optional[APIModelVersion] = None + """Version Information about a Model""" diff --git a/src/gradient/types/api_model_version.py b/src/gradient/types/api_model_version.py new file mode 100644 index 00000000..f19a78c6 --- /dev/null +++ b/src/gradient/types/api_model_version.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["APIModelVersion"] + + +class APIModelVersion(BaseModel): + major: Optional[int] = None + """Major version number""" + + minor: Optional[int] = None + """Minor version number""" + + patch: Optional[int] = None + """Patch version number""" diff --git a/src/gradient/types/api_openai_api_key_info.py b/src/gradient/types/api_openai_api_key_info.py new file mode 100644 index 00000000..bcee992b --- /dev/null +++ b/src/gradient/types/api_openai_api_key_info.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from .._models import BaseModel +from .api_agent_model import APIAgentModel + +__all__ = ["APIOpenAIAPIKeyInfo"] + + +class APIOpenAIAPIKeyInfo(BaseModel): + created_at: Optional[datetime] = None + """Key creation date""" + + created_by: Optional[str] = None + """Created by user id from DO""" + + deleted_at: Optional[datetime] = None + """Key deleted date""" + + models: Optional[List[APIAgentModel]] = None + """Models supported by the openAI api key""" + + name: Optional[str] = None + """Name""" + + updated_at: Optional[datetime] = None + """Key last updated date""" + + uuid: Optional[str] = None + """Uuid""" diff --git a/src/gradient/types/api_retrieval_method.py b/src/gradient/types/api_retrieval_method.py new file mode 100644 index 00000000..9d92838e --- /dev/null +++ b/src/gradient/types/api_retrieval_method.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["APIRetrievalMethod"] + +APIRetrievalMethod: TypeAlias = Literal[ + "RETRIEVAL_METHOD_UNKNOWN", + "RETRIEVAL_METHOD_REWRITE", + "RETRIEVAL_METHOD_STEP_BACK", + "RETRIEVAL_METHOD_SUB_QUERIES", + "RETRIEVAL_METHOD_NONE", +] diff --git a/src/gradient/types/api_workspace.py b/src/gradient/types/api_workspace.py new file mode 100644 index 00000000..564fabb6 --- /dev/null +++ b/src/gradient/types/api_workspace.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from datetime import datetime + +from .._models import BaseModel +from .agents.api_evaluation_test_case import APIEvaluationTestCase + +__all__ = ["APIWorkspace"] + + +class APIWorkspace(BaseModel): + agents: Optional[List["APIAgent"]] = None + """Agents""" + + created_at: Optional[datetime] = None + """Creation date""" + + created_by: Optional[str] = None + """The id of user who created this workspace""" + + created_by_email: Optional[str] = None + """The email of the user who created this workspace""" + + deleted_at: Optional[datetime] = None + """Deleted date""" + + description: Optional[str] = None + """Description of the workspace""" + + evaluation_test_cases: Optional[List[APIEvaluationTestCase]] = None + """Evaluations""" + + name: Optional[str] = None + """Name of the workspace""" + + updated_at: Optional[datetime] = None + """Update date""" + + uuid: Optional[str] = None + """Unique id""" + + +from .api_agent import APIAgent diff --git a/src/gradient/types/chat/__init__.py b/src/gradient/types/chat/__init__.py new file mode 100644 index 00000000..9384ac14 --- /dev/null +++ b/src/gradient/types/chat/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .completion_create_response import CompletionCreateResponse as CompletionCreateResponse diff --git a/src/gradient/types/chat/completion_create_params.py b/src/gradient/types/chat/completion_create_params.py new file mode 100644 index 00000000..7874d893 --- /dev/null +++ b/src/gradient/types/chat/completion_create_params.py @@ -0,0 +1,375 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "CompletionCreateParamsBase", + "Message", + "MessageChatCompletionRequestSystemMessage", + "MessageChatCompletionRequestSystemMessageContentArrayOfContentPart", + "MessageChatCompletionRequestSystemMessageContentArrayOfContentPartUnionMember1", + "MessageChatCompletionRequestDeveloperMessage", + "MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPart", + "MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPartUnionMember1", + "MessageChatCompletionRequestUserMessage", + "MessageChatCompletionRequestUserMessageContentArrayOfContentPart", + "MessageChatCompletionRequestUserMessageContentArrayOfContentPartUnionMember1", + "MessageChatCompletionRequestAssistantMessage", + "MessageChatCompletionRequestAssistantMessageContentArrayOfContentPart", + "MessageChatCompletionRequestAssistantMessageContentArrayOfContentPartUnionMember1", + "MessageChatCompletionRequestAssistantMessageToolCall", + "MessageChatCompletionRequestAssistantMessageToolCallFunction", + "MessageChatCompletionRequestToolMessage", + "StreamOptions", + "ToolChoice", + "ToolChoiceChatCompletionNamedToolChoice", + "ToolChoiceChatCompletionNamedToolChoiceFunction", + "Tool", + "ToolFunction", + "CompletionCreateParamsNonStreaming", + "CompletionCreateParamsStreaming", +] + + +class CompletionCreateParamsBase(TypedDict, total=False): + messages: Required[Iterable[Message]] + """A list of messages comprising the conversation so far.""" + + model: Required[str] + """Model ID used to generate the response.""" + + frequency_penalty: Optional[float] + """Number between -2.0 and 2.0. + + Positive values penalize new tokens based on their existing frequency in the + text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + logit_bias: Optional[Dict[str, int]] + """Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + """ + + logprobs: Optional[bool] + """Whether to return log probabilities of the output tokens or not. + + If true, returns the log probabilities of each output token returned in the + `content` of `message`. + """ + + max_completion_tokens: Optional[int] + """ + The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. + """ + + max_tokens: Optional[int] + """The maximum number of tokens that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + """ + + metadata: Optional[Dict[str, str]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + n: Optional[int] + """How many chat completion choices to generate for each input message. + + Note that you will be charged based on the number of generated tokens across all + of the choices. Keep `n` as `1` to minimize costs. + """ + + presence_penalty: Optional[float] + """Number between -2.0 and 2.0. + + Positive values penalize new tokens based on whether they appear in the text so + far, increasing the model's likelihood to talk about new topics. + """ + + stop: Union[Optional[str], SequenceNotStr[str], None] + """Up to 4 sequences where the API will stop generating further tokens. + + The returned text will not contain the stop sequence. + """ + + stream_options: Optional[StreamOptions] + """Options for streaming response. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + tool_choice: ToolChoice + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + """ + + tools: Iterable[Tool] + """A list of tools the model may call. + + Currently, only functions are supported as a tool. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + user: str + """ + A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + """ + + +class MessageChatCompletionRequestSystemMessageContentArrayOfContentPartUnionMember1(TypedDict, total=False): + text: Required[str] + """The text content""" + + type: Required[Literal["text"]] + """The type of content part""" + + +MessageChatCompletionRequestSystemMessageContentArrayOfContentPart: TypeAlias = Union[ + str, MessageChatCompletionRequestSystemMessageContentArrayOfContentPartUnionMember1 +] + + +class MessageChatCompletionRequestSystemMessage(TypedDict, total=False): + content: Required[Union[str, SequenceNotStr[MessageChatCompletionRequestSystemMessageContentArrayOfContentPart]]] + """The contents of the system message.""" + + role: Required[Literal["system"]] + """The role of the messages author, in this case `system`.""" + + +class MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPartUnionMember1(TypedDict, total=False): + text: Required[str] + """The text content""" + + type: Required[Literal["text"]] + """The type of content part""" + + +MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPart: TypeAlias = Union[ + str, MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPartUnionMember1 +] + + +class MessageChatCompletionRequestDeveloperMessage(TypedDict, total=False): + content: Required[Union[str, SequenceNotStr[MessageChatCompletionRequestDeveloperMessageContentArrayOfContentPart]]] + """The contents of the developer message.""" + + role: Required[Literal["developer"]] + """The role of the messages author, in this case `developer`.""" + + +class MessageChatCompletionRequestUserMessageContentArrayOfContentPartUnionMember1(TypedDict, total=False): + text: Required[str] + """The text content""" + + type: Required[Literal["text"]] + """The type of content part""" + + +MessageChatCompletionRequestUserMessageContentArrayOfContentPart: TypeAlias = Union[ + str, MessageChatCompletionRequestUserMessageContentArrayOfContentPartUnionMember1 +] + + +class MessageChatCompletionRequestUserMessage(TypedDict, total=False): + content: Required[Union[str, SequenceNotStr[MessageChatCompletionRequestUserMessageContentArrayOfContentPart]]] + """The contents of the user message.""" + + role: Required[Literal["user"]] + """The role of the messages author, in this case `user`.""" + + +class MessageChatCompletionRequestAssistantMessageContentArrayOfContentPartUnionMember1(TypedDict, total=False): + text: Required[str] + """The text content""" + + type: Required[Literal["text"]] + """The type of content part""" + + +MessageChatCompletionRequestAssistantMessageContentArrayOfContentPart: TypeAlias = Union[ + str, MessageChatCompletionRequestAssistantMessageContentArrayOfContentPartUnionMember1 +] + + +class MessageChatCompletionRequestAssistantMessageToolCallFunction(TypedDict, total=False): + arguments: Required[str] + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Required[str] + """The name of the function to call.""" + + +class MessageChatCompletionRequestAssistantMessageToolCall(TypedDict, total=False): + id: Required[str] + """The ID of the tool call.""" + + function: Required[MessageChatCompletionRequestAssistantMessageToolCallFunction] + """The function that the model called.""" + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" + + +class MessageChatCompletionRequestAssistantMessage(TypedDict, total=False): + role: Required[Literal["assistant"]] + """The role of the messages author, in this case `assistant`.""" + + content: Union[str, SequenceNotStr[MessageChatCompletionRequestAssistantMessageContentArrayOfContentPart], None] + """The contents of the assistant message.""" + + tool_calls: Iterable[MessageChatCompletionRequestAssistantMessageToolCall] + """The tool calls generated by the model, such as function calls.""" + + +class MessageChatCompletionRequestToolMessage(TypedDict, total=False): + content: Required[str] + """The contents of the tool message.""" + + role: Required[Literal["tool"]] + """The role of the messages author, in this case `tool`.""" + + tool_call_id: Required[str] + """Tool call that this message is responding to.""" + + +Message: TypeAlias = Union[ + MessageChatCompletionRequestSystemMessage, + MessageChatCompletionRequestDeveloperMessage, + MessageChatCompletionRequestUserMessage, + MessageChatCompletionRequestAssistantMessage, + MessageChatCompletionRequestToolMessage, +] + + +class StreamOptions(TypedDict, total=False): + include_usage: bool + """If set, an additional chunk will be streamed before the `data: [DONE]` message. + + The `usage` field on this chunk shows the token usage statistics for the entire + request, and the `choices` field will always be an empty array. + + All other chunks will also include a `usage` field, but with a null value. + **NOTE:** If the stream is interrupted, you may not receive the final usage + chunk which contains the total token usage for the request. + """ + + +class ToolChoiceChatCompletionNamedToolChoiceFunction(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" + + +class ToolChoiceChatCompletionNamedToolChoice(TypedDict, total=False): + function: Required[ToolChoiceChatCompletionNamedToolChoiceFunction] + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" + + +ToolChoice: TypeAlias = Union[Literal["none", "auto", "required"], ToolChoiceChatCompletionNamedToolChoice] + + +class ToolFunction(TypedDict, total=False): + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + parameters: Dict[str, object] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](/docs/guides/function-calling) for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + Omitting `parameters` defines a function with an empty parameter list. + """ + + +class Tool(TypedDict, total=False): + function: Required[ToolFunction] + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" + + +class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + """ + + +class CompletionCreateParamsStreaming(CompletionCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using server-sent events. + """ + + +CompletionCreateParams = Union[CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming] diff --git a/src/gradient/types/chat/completion_create_response.py b/src/gradient/types/chat/completion_create_response.py new file mode 100644 index 00000000..151f6556 --- /dev/null +++ b/src/gradient/types/chat/completion_create_response.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.completion_usage import CompletionUsage +from ..shared.chat_completion_token_logprob import ChatCompletionTokenLogprob + +__all__ = [ + "CompletionCreateResponse", + "Choice", + "ChoiceLogprobs", + "ChoiceMessage", + "ChoiceMessageToolCall", + "ChoiceMessageToolCallFunction", +] + + +class ChoiceLogprobs(BaseModel): + content: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message content tokens with log probability information.""" + + refusal: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message refusal tokens with log probability information.""" + + +class ChoiceMessageToolCallFunction(BaseModel): + arguments: str + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: str + """The name of the function to call.""" + + +class ChoiceMessageToolCall(BaseModel): + id: str + """The ID of the tool call.""" + + function: ChoiceMessageToolCallFunction + """The function that the model called.""" + + type: Literal["function"] + """The type of the tool. Currently, only `function` is supported.""" + + +class ChoiceMessage(BaseModel): + content: Optional[str] = None + """The contents of the message.""" + + reasoning_content: Optional[str] = None + """The reasoning content generated by the model.""" + + refusal: Optional[str] = None + """The refusal message generated by the model.""" + + role: Literal["assistant"] + """The role of the author of this message.""" + + tool_calls: Optional[List[ChoiceMessageToolCall]] = None + """The tool calls generated by the model, such as function calls.""" + + +class Choice(BaseModel): + finish_reason: Literal["stop", "length", "tool_calls", "content_filter"] + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, or `length` if the maximum number of tokens specified in the request + was reached, `tool_calls` if the model called a tool. + """ + + index: int + """The index of the choice in the list of choices.""" + + logprobs: Optional[ChoiceLogprobs] = None + """Log probability information for the choice.""" + + message: ChoiceMessage + """A chat completion message generated by the model.""" + + +class CompletionCreateResponse(BaseModel): + id: str + """A unique identifier for the chat completion.""" + + choices: List[Choice] + """A list of chat completion choices. + + Can be more than one if `n` is greater than 1. + """ + + created: int + """The Unix timestamp (in seconds) of when the chat completion was created.""" + + model: str + """The model used for the chat completion.""" + + object: Literal["chat.completion"] + """The object type, which is always `chat.completion`.""" + + usage: Optional[CompletionUsage] = None + """Usage statistics for the completion request.""" diff --git a/src/gradient/types/databases/__init__.py b/src/gradient/types/databases/__init__.py new file mode 100644 index 00000000..f8ee8b14 --- /dev/null +++ b/src/gradient/types/databases/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/gradient/types/databases/schema_registry/__init__.py b/src/gradient/types/databases/schema_registry/__init__.py new file mode 100644 index 00000000..92c4e7a5 --- /dev/null +++ b/src/gradient/types/databases/schema_registry/__init__.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .config_update_params import ConfigUpdateParams as ConfigUpdateParams +from .config_update_response import ConfigUpdateResponse as ConfigUpdateResponse +from .config_retrieve_response import ConfigRetrieveResponse as ConfigRetrieveResponse +from .config_update_subject_params import ConfigUpdateSubjectParams as ConfigUpdateSubjectParams +from .config_update_subject_response import ConfigUpdateSubjectResponse as ConfigUpdateSubjectResponse +from .config_retrieve_subject_response import ConfigRetrieveSubjectResponse as ConfigRetrieveSubjectResponse diff --git a/src/gradient/types/databases/schema_registry/config_retrieve_response.py b/src/gradient/types/databases/schema_registry/config_retrieve_response.py new file mode 100644 index 00000000..583e4eec --- /dev/null +++ b/src/gradient/types/databases/schema_registry/config_retrieve_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConfigRetrieveResponse"] + + +class ConfigRetrieveResponse(BaseModel): + compatibility_level: Literal[ + "NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE" + ] + """The compatibility level of the schema registry.""" diff --git a/src/gradient/types/databases/schema_registry/config_retrieve_subject_response.py b/src/gradient/types/databases/schema_registry/config_retrieve_subject_response.py new file mode 100644 index 00000000..ec9fea68 --- /dev/null +++ b/src/gradient/types/databases/schema_registry/config_retrieve_subject_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConfigRetrieveSubjectResponse"] + + +class ConfigRetrieveSubjectResponse(BaseModel): + compatibility_level: Literal[ + "NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE" + ] + """The compatibility level of the schema registry.""" + + subject_name: str + """The name of the schema subject.""" diff --git a/src/gradient/types/databases/schema_registry/config_update_params.py b/src/gradient/types/databases/schema_registry/config_update_params.py new file mode 100644 index 00000000..b25c7e92 --- /dev/null +++ b/src/gradient/types/databases/schema_registry/config_update_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConfigUpdateParams"] + + +class ConfigUpdateParams(TypedDict, total=False): + compatibility_level: Required[ + Literal["NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE"] + ] + """The compatibility level of the schema registry.""" diff --git a/src/gradient/types/databases/schema_registry/config_update_response.py b/src/gradient/types/databases/schema_registry/config_update_response.py new file mode 100644 index 00000000..0df776af --- /dev/null +++ b/src/gradient/types/databases/schema_registry/config_update_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConfigUpdateResponse"] + + +class ConfigUpdateResponse(BaseModel): + compatibility_level: Literal[ + "NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE" + ] + """The compatibility level of the schema registry.""" diff --git a/src/gradient/types/databases/schema_registry/config_update_subject_params.py b/src/gradient/types/databases/schema_registry/config_update_subject_params.py new file mode 100644 index 00000000..b935ba80 --- /dev/null +++ b/src/gradient/types/databases/schema_registry/config_update_subject_params.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConfigUpdateSubjectParams"] + + +class ConfigUpdateSubjectParams(TypedDict, total=False): + database_cluster_uuid: Required[str] + + compatibility_level: Required[ + Literal["NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE"] + ] + """The compatibility level of the schema registry.""" diff --git a/src/gradient/types/databases/schema_registry/config_update_subject_response.py b/src/gradient/types/databases/schema_registry/config_update_subject_response.py new file mode 100644 index 00000000..3bb3cd24 --- /dev/null +++ b/src/gradient/types/databases/schema_registry/config_update_subject_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConfigUpdateSubjectResponse"] + + +class ConfigUpdateSubjectResponse(BaseModel): + compatibility_level: Literal[ + "NONE", "BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE" + ] + """The compatibility level of the schema registry.""" + + subject_name: str + """The name of the schema subject.""" diff --git a/src/gradient/types/droplet_backup_policy.py b/src/gradient/types/droplet_backup_policy.py new file mode 100644 index 00000000..63112e8f --- /dev/null +++ b/src/gradient/types/droplet_backup_policy.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["DropletBackupPolicy"] + + +class DropletBackupPolicy(BaseModel): + hour: Optional[Literal[0, 4, 8, 12, 16, 20]] = None + """The hour of the day that the backup window will start.""" + + plan: Optional[Literal["daily", "weekly"]] = None + """The backup plan used for the Droplet. + + The plan can be either `daily` or `weekly`. + """ + + retention_period_days: Optional[int] = None + """The number of days the backup will be retained.""" + + weekday: Optional[Literal["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"]] = None + """The day of the week on which the backup will occur.""" + + window_length_hours: Optional[int] = None + """The length of the backup window starting from `hour`.""" diff --git a/src/gradient/types/droplet_backup_policy_param.py b/src/gradient/types/droplet_backup_policy_param.py new file mode 100644 index 00000000..802f057f --- /dev/null +++ b/src/gradient/types/droplet_backup_policy_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["DropletBackupPolicyParam"] + + +class DropletBackupPolicyParam(TypedDict, total=False): + hour: Literal[0, 4, 8, 12, 16, 20] + """The hour of the day that the backup window will start.""" + + plan: Literal["daily", "weekly"] + """The backup plan used for the Droplet. + + The plan can be either `daily` or `weekly`. + """ + + weekday: Literal["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"] + """The day of the week on which the backup will occur.""" diff --git a/src/gradient/types/gpu_droplet_create_params.py b/src/gradient/types/gpu_droplet_create_params.py new file mode 100644 index 00000000..96403479 --- /dev/null +++ b/src/gradient/types/gpu_droplet_create_params.py @@ -0,0 +1,214 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Required, TypeAlias, TypedDict + +from .._types import SequenceNotStr +from .droplet_backup_policy_param import DropletBackupPolicyParam + +__all__ = ["GPUDropletCreateParams", "DropletSingleCreate", "DropletMultiCreate"] + + +class DropletSingleCreate(TypedDict, total=False): + image: Required[Union[str, int]] + """ + The image ID of a public or private image or the slug identifier for a public + image. This image will be the base image for your Droplet. Requires `image:read` + scope. + """ + + name: Required[str] + """The human-readable string you wish to use when displaying the Droplet name. + + The name, if set to a domain name managed in the DigitalOcean DNS management + system, will configure a PTR record for the Droplet. The name set during + creation will also determine the hostname for the Droplet in its internal + configuration. + """ + + size: Required[str] + """The slug identifier for the size that you wish to select for this Droplet.""" + + backup_policy: DropletBackupPolicyParam + """An object specifying the backup policy for the Droplet. + + If omitted and `backups` is `true`, the backup plan will default to daily. + """ + + backups: bool + """ + A boolean indicating whether automated backups should be enabled for the + Droplet. + """ + + ipv6: bool + """A boolean indicating whether to enable IPv6 on the Droplet.""" + + monitoring: bool + """A boolean indicating whether to install the DigitalOcean agent for monitoring.""" + + private_networking: bool + """This parameter has been deprecated. + + Use `vpc_uuid` instead to specify a VPC network for the Droplet. If no + `vpc_uuid` is provided, the Droplet will be placed in your account's default VPC + for the region. + """ + + region: str + """The slug identifier for the region that you wish to deploy the Droplet in. + + If the specific datacenter is not not important, a slug prefix (e.g. `nyc`) can + be used to deploy the Droplet in any of the that region's locations (`nyc1`, + `nyc2`, or `nyc3`). If the region is omitted from the create request completely, + the Droplet may deploy in any region. + """ + + ssh_keys: SequenceNotStr[Union[str, int]] + """ + An array containing the IDs or fingerprints of the SSH keys that you wish to + embed in the Droplet's root account upon creation. You must add the keys to your + team before they can be embedded on a Droplet. Requires `ssh_key:read` scope. + """ + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to apply to the Droplet after it is + created. + + Tag names can either be existing or new tags. Requires `tag:create` scope. + """ + + user_data: str + """ + A string containing 'user data' which may be used to configure the Droplet on + first boot, often a 'cloud-config' file or Bash script. It must be plain text + and may not exceed 64 KiB in size. + """ + + volumes: SequenceNotStr[str] + """ + An array of IDs for block storage volumes that will be attached to the Droplet + once created. The volumes must not already be attached to an existing Droplet. + Requires `block_storage:read` scpoe. + """ + + vpc_uuid: str + """A string specifying the UUID of the VPC to which the Droplet will be assigned. + + If excluded, the Droplet will be assigned to your account's default VPC for the + region. Requires `vpc:read` scope. + """ + + with_droplet_agent: bool + """ + A boolean indicating whether to install the DigitalOcean agent used for + providing access to the Droplet web console in the control panel. By default, + the agent is installed on new Droplets but installation errors (i.e. OS not + supported) are ignored. To prevent it from being installed, set to `false`. To + make installation errors fatal, explicitly set it to `true`. + """ + + +class DropletMultiCreate(TypedDict, total=False): + image: Required[Union[str, int]] + """ + The image ID of a public or private image or the slug identifier for a public + image. This image will be the base image for your Droplet. Requires `image:read` + scope. + """ + + names: Required[SequenceNotStr[str]] + """ + An array of human human-readable strings you wish to use when displaying the + Droplet name. Each name, if set to a domain name managed in the DigitalOcean DNS + management system, will configure a PTR record for the Droplet. Each name set + during creation will also determine the hostname for the Droplet in its internal + configuration. + """ + + size: Required[str] + """The slug identifier for the size that you wish to select for this Droplet.""" + + backup_policy: DropletBackupPolicyParam + """An object specifying the backup policy for the Droplet. + + If omitted and `backups` is `true`, the backup plan will default to daily. + """ + + backups: bool + """ + A boolean indicating whether automated backups should be enabled for the + Droplet. + """ + + ipv6: bool + """A boolean indicating whether to enable IPv6 on the Droplet.""" + + monitoring: bool + """A boolean indicating whether to install the DigitalOcean agent for monitoring.""" + + private_networking: bool + """This parameter has been deprecated. + + Use `vpc_uuid` instead to specify a VPC network for the Droplet. If no + `vpc_uuid` is provided, the Droplet will be placed in your account's default VPC + for the region. + """ + + region: str + """The slug identifier for the region that you wish to deploy the Droplet in. + + If the specific datacenter is not not important, a slug prefix (e.g. `nyc`) can + be used to deploy the Droplet in any of the that region's locations (`nyc1`, + `nyc2`, or `nyc3`). If the region is omitted from the create request completely, + the Droplet may deploy in any region. + """ + + ssh_keys: SequenceNotStr[Union[str, int]] + """ + An array containing the IDs or fingerprints of the SSH keys that you wish to + embed in the Droplet's root account upon creation. You must add the keys to your + team before they can be embedded on a Droplet. Requires `ssh_key:read` scope. + """ + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to apply to the Droplet after it is + created. + + Tag names can either be existing or new tags. Requires `tag:create` scope. + """ + + user_data: str + """ + A string containing 'user data' which may be used to configure the Droplet on + first boot, often a 'cloud-config' file or Bash script. It must be plain text + and may not exceed 64 KiB in size. + """ + + volumes: SequenceNotStr[str] + """ + An array of IDs for block storage volumes that will be attached to the Droplet + once created. The volumes must not already be attached to an existing Droplet. + Requires `block_storage:read` scpoe. + """ + + vpc_uuid: str + """A string specifying the UUID of the VPC to which the Droplet will be assigned. + + If excluded, the Droplet will be assigned to your account's default VPC for the + region. Requires `vpc:read` scope. + """ + + with_droplet_agent: bool + """ + A boolean indicating whether to install the DigitalOcean agent used for + providing access to the Droplet web console in the control panel. By default, + the agent is installed on new Droplets but installation errors (i.e. OS not + supported) are ignored. To prevent it from being installed, set to `false`. To + make installation errors fatal, explicitly set it to `true`. + """ + + +GPUDropletCreateParams: TypeAlias = Union[DropletSingleCreate, DropletMultiCreate] diff --git a/src/gradient/types/gpu_droplet_create_response.py b/src/gradient/types/gpu_droplet_create_response.py new file mode 100644 index 00000000..72fafb96 --- /dev/null +++ b/src/gradient/types/gpu_droplet_create_response.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import TypeAlias + +from .._models import BaseModel +from .shared.droplet import Droplet +from .shared.action_link import ActionLink + +__all__ = [ + "GPUDropletCreateResponse", + "SingleDropletResponse", + "SingleDropletResponseLinks", + "MultipleDropletResponse", + "MultipleDropletResponseLinks", +] + + +class SingleDropletResponseLinks(BaseModel): + actions: Optional[List[ActionLink]] = None + + +class SingleDropletResponse(BaseModel): + droplet: Droplet + + links: SingleDropletResponseLinks + + +class MultipleDropletResponseLinks(BaseModel): + actions: Optional[List[ActionLink]] = None + + +class MultipleDropletResponse(BaseModel): + droplets: List[Droplet] + + links: MultipleDropletResponseLinks + + +GPUDropletCreateResponse: TypeAlias = Union[SingleDropletResponse, MultipleDropletResponse] diff --git a/src/gradient/types/gpu_droplet_delete_by_tag_params.py b/src/gradient/types/gpu_droplet_delete_by_tag_params.py new file mode 100644 index 00000000..bc303125 --- /dev/null +++ b/src/gradient/types/gpu_droplet_delete_by_tag_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["GPUDropletDeleteByTagParams"] + + +class GPUDropletDeleteByTagParams(TypedDict, total=False): + tag_name: Required[str] + """Specifies Droplets to be deleted by tag.""" diff --git a/src/gradient/types/gpu_droplet_list_firewalls_params.py b/src/gradient/types/gpu_droplet_list_firewalls_params.py new file mode 100644 index 00000000..1f0111d8 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_firewalls_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["GPUDropletListFirewallsParams"] + + +class GPUDropletListFirewallsParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplet_list_firewalls_response.py b/src/gradient/types/gpu_droplet_list_firewalls_response.py new file mode 100644 index 00000000..617cdf98 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_firewalls_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .shared.page_links import PageLinks +from .gpu_droplets.firewall import Firewall +from .shared.meta_properties import MetaProperties + +__all__ = ["GPUDropletListFirewallsResponse"] + + +class GPUDropletListFirewallsResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + firewalls: Optional[List[Firewall]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplet_list_kernels_params.py b/src/gradient/types/gpu_droplet_list_kernels_params.py new file mode 100644 index 00000000..7aa73225 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_kernels_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["GPUDropletListKernelsParams"] + + +class GPUDropletListKernelsParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplet_list_kernels_response.py b/src/gradient/types/gpu_droplet_list_kernels_response.py new file mode 100644 index 00000000..5fa9a355 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_kernels_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .shared.kernel import Kernel +from .shared.page_links import PageLinks +from .shared.meta_properties import MetaProperties + +__all__ = ["GPUDropletListKernelsResponse"] + + +class GPUDropletListKernelsResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + kernels: Optional[List[Optional[Kernel]]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplet_list_neighbors_response.py b/src/gradient/types/gpu_droplet_list_neighbors_response.py new file mode 100644 index 00000000..cdfce3e0 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_neighbors_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .shared.droplet import Droplet + +__all__ = ["GPUDropletListNeighborsResponse"] + + +class GPUDropletListNeighborsResponse(BaseModel): + droplets: Optional[List[Droplet]] = None diff --git a/src/gradient/types/gpu_droplet_list_params.py b/src/gradient/types/gpu_droplet_list_params.py new file mode 100644 index 00000000..bf6eb793 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_params.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["GPUDropletListParams"] + + +class GPUDropletListParams(TypedDict, total=False): + name: str + """Used to filter list response by Droplet name returning only exact matches. + + It is case-insensitive and can not be combined with `tag_name`. + """ + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + tag_name: str + """Used to filter Droplets by a specific tag. + + Can not be combined with `name` or `type`. Requires `tag:read` scope. + """ + + type: Literal["droplets", "gpus"] + """When `type` is set to `gpus`, only GPU Droplets will be returned. + + By default, only non-GPU Droplets are returned. Can not be combined with + `tag_name`. + """ diff --git a/src/gradient/types/gpu_droplet_list_response.py b/src/gradient/types/gpu_droplet_list_response.py new file mode 100644 index 00000000..73e1e503 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .shared.droplet import Droplet +from .shared.page_links import PageLinks +from .shared.meta_properties import MetaProperties + +__all__ = ["GPUDropletListResponse"] + + +class GPUDropletListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + droplets: Optional[List[Droplet]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplet_list_snapshots_params.py b/src/gradient/types/gpu_droplet_list_snapshots_params.py new file mode 100644 index 00000000..66e65a36 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_snapshots_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["GPUDropletListSnapshotsParams"] + + +class GPUDropletListSnapshotsParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplet_list_snapshots_response.py b/src/gradient/types/gpu_droplet_list_snapshots_response.py new file mode 100644 index 00000000..4b34d670 --- /dev/null +++ b/src/gradient/types/gpu_droplet_list_snapshots_response.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .shared.page_links import PageLinks +from .shared.meta_properties import MetaProperties + +__all__ = ["GPUDropletListSnapshotsResponse", "Snapshot"] + + +class Snapshot(BaseModel): + id: int + """The unique identifier for the snapshot or backup.""" + + created_at: datetime + """ + A time value given in ISO8601 combined date and time format that represents when + the snapshot was created. + """ + + min_disk_size: int + """The minimum size in GB required for a volume or Droplet to use this snapshot.""" + + name: str + """A human-readable name for the snapshot.""" + + regions: List[str] + """An array of the regions that the snapshot is available in. + + The regions are represented by their identifying slug values. + """ + + size_gigabytes: float + """The billable size of the snapshot in gigabytes.""" + + type: Literal["snapshot", "backup"] + """Describes the kind of image. + + It may be one of `snapshot` or `backup`. This specifies whether an image is a + user-generated Droplet snapshot or automatically created Droplet backup. + """ + + +class GPUDropletListSnapshotsResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + links: Optional[PageLinks] = None + + snapshots: Optional[List[Snapshot]] = None diff --git a/src/gradient/types/gpu_droplet_retrieve_response.py b/src/gradient/types/gpu_droplet_retrieve_response.py new file mode 100644 index 00000000..d8cc0f20 --- /dev/null +++ b/src/gradient/types/gpu_droplet_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .shared.droplet import Droplet + +__all__ = ["GPUDropletRetrieveResponse"] + + +class GPUDropletRetrieveResponse(BaseModel): + droplet: Optional[Droplet] = None diff --git a/src/gradient/types/gpu_droplets/__init__.py b/src/gradient/types/gpu_droplets/__init__.py new file mode 100644 index 00000000..c2f1835f --- /dev/null +++ b/src/gradient/types/gpu_droplets/__init__.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .domains import Domains as Domains +from .firewall import Firewall as Firewall +from .floating_ip import FloatingIP as FloatingIP +from .lb_firewall import LbFirewall as LbFirewall +from .glb_settings import GlbSettings as GlbSettings +from .health_check import HealthCheck as HealthCheck +from .domains_param import DomainsParam as DomainsParam +from .load_balancer import LoadBalancer as LoadBalancer +from .autoscale_pool import AutoscalePool as AutoscalePool +from .firewall_param import FirewallParam as FirewallParam +from .forwarding_rule import ForwardingRule as ForwardingRule +from .sticky_sessions import StickySessions as StickySessions +from .size_list_params import SizeListParams as SizeListParams +from .image_list_params import ImageListParams as ImageListParams +from .lb_firewall_param import LbFirewallParam as LbFirewallParam +from .action_list_params import ActionListParams as ActionListParams +from .backup_list_params import BackupListParams as BackupListParams +from .glb_settings_param import GlbSettingsParam as GlbSettingsParam +from .health_check_param import HealthCheckParam as HealthCheckParam +from .size_list_response import SizeListResponse as SizeListResponse +from .volume_list_params import VolumeListParams as VolumeListParams +from .associated_resource import AssociatedResource as AssociatedResource +from .current_utilization import CurrentUtilization as CurrentUtilization +from .image_create_params import ImageCreateParams as ImageCreateParams +from .image_list_response import ImageListResponse as ImageListResponse +from .image_update_params import ImageUpdateParams as ImageUpdateParams +from .action_list_response import ActionListResponse as ActionListResponse +from .backup_list_response import BackupListResponse as BackupListResponse +from .firewall_list_params import FirewallListParams as FirewallListParams +from .snapshot_list_params import SnapshotListParams as SnapshotListParams +from .volume_create_params import VolumeCreateParams as VolumeCreateParams +from .volume_list_response import VolumeListResponse as VolumeListResponse +from .autoscale_list_params import AutoscaleListParams as AutoscaleListParams +from .forwarding_rule_param import ForwardingRuleParam as ForwardingRuleParam +from .image_create_response import ImageCreateResponse as ImageCreateResponse +from .image_update_response import ImageUpdateResponse as ImageUpdateResponse +from .sticky_sessions_param import StickySessionsParam as StickySessionsParam +from .action_initiate_params import ActionInitiateParams as ActionInitiateParams +from .firewall_create_params import FirewallCreateParams as FirewallCreateParams +from .firewall_list_response import FirewallListResponse as FirewallListResponse +from .firewall_update_params import FirewallUpdateParams as FirewallUpdateParams +from .snapshot_list_response import SnapshotListResponse as SnapshotListResponse +from .volume_create_response import VolumeCreateResponse as VolumeCreateResponse +from .autoscale_create_params import AutoscaleCreateParams as AutoscaleCreateParams +from .autoscale_list_response import AutoscaleListResponse as AutoscaleListResponse +from .autoscale_update_params import AutoscaleUpdateParams as AutoscaleUpdateParams +from .floating_ip_list_params import FloatingIPListParams as FloatingIPListParams +from .image_retrieve_response import ImageRetrieveResponse as ImageRetrieveResponse +from .action_initiate_response import ActionInitiateResponse as ActionInitiateResponse +from .action_retrieve_response import ActionRetrieveResponse as ActionRetrieveResponse +from .firewall_create_response import FirewallCreateResponse as FirewallCreateResponse +from .firewall_update_response import FirewallUpdateResponse as FirewallUpdateResponse +from .volume_retrieve_response import VolumeRetrieveResponse as VolumeRetrieveResponse +from .autoscale_create_response import AutoscaleCreateResponse as AutoscaleCreateResponse +from .autoscale_update_response import AutoscaleUpdateResponse as AutoscaleUpdateResponse +from .floating_ip_create_params import FloatingIPCreateParams as FloatingIPCreateParams +from .floating_ip_list_response import FloatingIPListResponse as FloatingIPListResponse +from .load_balancer_list_params import LoadBalancerListParams as LoadBalancerListParams +from .firewall_retrieve_response import FirewallRetrieveResponse as FirewallRetrieveResponse +from .snapshot_retrieve_response import SnapshotRetrieveResponse as SnapshotRetrieveResponse +from .action_bulk_initiate_params import ActionBulkInitiateParams as ActionBulkInitiateParams +from .autoscale_retrieve_response import AutoscaleRetrieveResponse as AutoscaleRetrieveResponse +from .backup_list_policies_params import BackupListPoliciesParams as BackupListPoliciesParams +from .floating_ip_create_response import FloatingIPCreateResponse as FloatingIPCreateResponse +from .load_balancer_create_params import LoadBalancerCreateParams as LoadBalancerCreateParams +from .load_balancer_list_response import LoadBalancerListResponse as LoadBalancerListResponse +from .load_balancer_update_params import LoadBalancerUpdateParams as LoadBalancerUpdateParams +from .autoscale_pool_static_config import AutoscalePoolStaticConfig as AutoscalePoolStaticConfig +from .volume_delete_by_name_params import VolumeDeleteByNameParams as VolumeDeleteByNameParams +from .action_bulk_initiate_response import ActionBulkInitiateResponse as ActionBulkInitiateResponse +from .autoscale_list_history_params import AutoscaleListHistoryParams as AutoscaleListHistoryParams +from .autoscale_list_members_params import AutoscaleListMembersParams as AutoscaleListMembersParams +from .autoscale_pool_dynamic_config import AutoscalePoolDynamicConfig as AutoscalePoolDynamicConfig +from .backup_list_policies_response import BackupListPoliciesResponse as BackupListPoliciesResponse +from .destroyed_associated_resource import DestroyedAssociatedResource as DestroyedAssociatedResource +from .floating_ip_retrieve_response import FloatingIPRetrieveResponse as FloatingIPRetrieveResponse +from .load_balancer_create_response import LoadBalancerCreateResponse as LoadBalancerCreateResponse +from .load_balancer_update_response import LoadBalancerUpdateResponse as LoadBalancerUpdateResponse +from .autoscale_list_history_response import AutoscaleListHistoryResponse as AutoscaleListHistoryResponse +from .autoscale_list_members_response import AutoscaleListMembersResponse as AutoscaleListMembersResponse +from .autoscale_pool_droplet_template import AutoscalePoolDropletTemplate as AutoscalePoolDropletTemplate +from .backup_retrieve_policy_response import BackupRetrievePolicyResponse as BackupRetrievePolicyResponse +from .load_balancer_retrieve_response import LoadBalancerRetrieveResponse as LoadBalancerRetrieveResponse +from .autoscale_pool_static_config_param import AutoscalePoolStaticConfigParam as AutoscalePoolStaticConfigParam +from .autoscale_pool_dynamic_config_param import AutoscalePoolDynamicConfigParam as AutoscalePoolDynamicConfigParam +from .autoscale_pool_droplet_template_param import ( + AutoscalePoolDropletTemplateParam as AutoscalePoolDropletTemplateParam, +) +from .backup_list_supported_policies_response import ( + BackupListSupportedPoliciesResponse as BackupListSupportedPoliciesResponse, +) +from .destroy_with_associated_resource_list_response import ( + DestroyWithAssociatedResourceListResponse as DestroyWithAssociatedResourceListResponse, +) +from .destroy_with_associated_resource_check_status_response import ( + DestroyWithAssociatedResourceCheckStatusResponse as DestroyWithAssociatedResourceCheckStatusResponse, +) +from .destroy_with_associated_resource_delete_selective_params import ( + DestroyWithAssociatedResourceDeleteSelectiveParams as DestroyWithAssociatedResourceDeleteSelectiveParams, +) diff --git a/src/gradient/types/gpu_droplets/account/__init__.py b/src/gradient/types/gpu_droplets/account/__init__.py new file mode 100644 index 00000000..2d8a05ae --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/__init__.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .ssh_keys import SSHKeys as SSHKeys +from .key_list_params import KeyListParams as KeyListParams +from .key_create_params import KeyCreateParams as KeyCreateParams +from .key_list_response import KeyListResponse as KeyListResponse +from .key_update_params import KeyUpdateParams as KeyUpdateParams +from .key_create_response import KeyCreateResponse as KeyCreateResponse +from .key_update_response import KeyUpdateResponse as KeyUpdateResponse +from .key_retrieve_response import KeyRetrieveResponse as KeyRetrieveResponse diff --git a/src/gradient/types/gpu_droplets/account/key_create_params.py b/src/gradient/types/gpu_droplets/account/key_create_params.py new file mode 100644 index 00000000..4e7c1cef --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/key_create_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["KeyCreateParams"] + + +class KeyCreateParams(TypedDict, total=False): + name: Required[str] + """ + A human-readable display name for this key, used to easily identify the SSH keys + when they are displayed. + """ + + public_key: Required[str] + """The entire public key string that was uploaded. + + Embedded into the root user's `authorized_keys` file if you include this key + during Droplet creation. + """ diff --git a/src/gradient/types/gpu_droplets/account/key_create_response.py b/src/gradient/types/gpu_droplets/account/key_create_response.py new file mode 100644 index 00000000..5ce63269 --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/key_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .ssh_keys import SSHKeys +from ...._models import BaseModel + +__all__ = ["KeyCreateResponse"] + + +class KeyCreateResponse(BaseModel): + ssh_key: Optional[SSHKeys] = None diff --git a/src/gradient/types/gpu_droplets/account/key_list_params.py b/src/gradient/types/gpu_droplets/account/key_list_params.py new file mode 100644 index 00000000..44a455f3 --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/key_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KeyListParams"] + + +class KeyListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/account/key_list_response.py b/src/gradient/types/gpu_droplets/account/key_list_response.py new file mode 100644 index 00000000..1151043e --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/key_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .ssh_keys import SSHKeys +from ...._models import BaseModel +from ...shared.page_links import PageLinks +from ...shared.meta_properties import MetaProperties + +__all__ = ["KeyListResponse"] + + +class KeyListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + links: Optional[PageLinks] = None + + ssh_keys: Optional[List[SSHKeys]] = None diff --git a/src/gradient/types/gpu_droplets/account/key_retrieve_response.py b/src/gradient/types/gpu_droplets/account/key_retrieve_response.py new file mode 100644 index 00000000..da6e94d1 --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/key_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .ssh_keys import SSHKeys +from ...._models import BaseModel + +__all__ = ["KeyRetrieveResponse"] + + +class KeyRetrieveResponse(BaseModel): + ssh_key: Optional[SSHKeys] = None diff --git a/src/gradient/types/gpu_droplets/account/key_update_params.py b/src/gradient/types/gpu_droplets/account/key_update_params.py new file mode 100644 index 00000000..e73d8b7b --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/key_update_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KeyUpdateParams"] + + +class KeyUpdateParams(TypedDict, total=False): + name: str + """ + A human-readable display name for this key, used to easily identify the SSH keys + when they are displayed. + """ diff --git a/src/gradient/types/gpu_droplets/account/key_update_response.py b/src/gradient/types/gpu_droplets/account/key_update_response.py new file mode 100644 index 00000000..54b81426 --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/key_update_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .ssh_keys import SSHKeys +from ...._models import BaseModel + +__all__ = ["KeyUpdateResponse"] + + +class KeyUpdateResponse(BaseModel): + ssh_key: Optional[SSHKeys] = None diff --git a/src/gradient/types/gpu_droplets/account/ssh_keys.py b/src/gradient/types/gpu_droplets/account/ssh_keys.py new file mode 100644 index 00000000..8112c18a --- /dev/null +++ b/src/gradient/types/gpu_droplets/account/ssh_keys.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["SSHKeys"] + + +class SSHKeys(BaseModel): + name: str + """ + A human-readable display name for this key, used to easily identify the SSH keys + when they are displayed. + """ + + public_key: str + """The entire public key string that was uploaded. + + Embedded into the root user's `authorized_keys` file if you include this key + during Droplet creation. + """ + + id: Optional[int] = None + """A unique identification number for this key. + + Can be used to embed a specific SSH key into a Droplet. + """ + + fingerprint: Optional[str] = None + """ + A unique identifier that differentiates this key from other keys using a format + that SSH recognizes. The fingerprint is created when the key is added to your + account. + """ diff --git a/src/gradient/types/gpu_droplets/action_bulk_initiate_params.py b/src/gradient/types/gpu_droplets/action_bulk_initiate_params.py new file mode 100644 index 00000000..a6402096 --- /dev/null +++ b/src/gradient/types/gpu_droplets/action_bulk_initiate_params.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ActionBulkInitiateParams", "DropletAction", "DropletActionSnapshot"] + + +class DropletAction(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + tag_name: str + """Used to filter Droplets by a specific tag. + + Can not be combined with `name` or `type`. Requires `tag:read` scope. + """ + + +class DropletActionSnapshot(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + tag_name: str + """Used to filter Droplets by a specific tag. + + Can not be combined with `name` or `type`. Requires `tag:read` scope. + """ + + name: str + """The name to give the new snapshot of the Droplet.""" + + +ActionBulkInitiateParams: TypeAlias = Union[DropletAction, DropletActionSnapshot] diff --git a/src/gradient/types/gpu_droplets/action_bulk_initiate_response.py b/src/gradient/types/gpu_droplets/action_bulk_initiate_response.py new file mode 100644 index 00000000..905860d7 --- /dev/null +++ b/src/gradient/types/gpu_droplets/action_bulk_initiate_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.action import Action + +__all__ = ["ActionBulkInitiateResponse"] + + +class ActionBulkInitiateResponse(BaseModel): + actions: Optional[List[Action]] = None diff --git a/src/gradient/types/gpu_droplets/action_initiate_params.py b/src/gradient/types/gpu_droplets/action_initiate_params.py new file mode 100644 index 00000000..f0ef6b1e --- /dev/null +++ b/src/gradient/types/gpu_droplets/action_initiate_params.py @@ -0,0 +1,278 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..droplet_backup_policy_param import DropletBackupPolicyParam + +__all__ = [ + "ActionInitiateParams", + "DropletAction", + "DropletActionEnableBackups", + "DropletActionChangeBackupPolicy", + "DropletActionRestore", + "DropletActionResize", + "DropletActionRebuild", + "DropletActionRename", + "DropletActionChangeKernel", + "DropletActionSnapshot", +] + + +class DropletAction(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + +class DropletActionEnableBackups(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + backup_policy: DropletBackupPolicyParam + """An object specifying the backup policy for the Droplet. + + If omitted, the backup plan will default to daily. + """ + + +class DropletActionChangeBackupPolicy(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + backup_policy: DropletBackupPolicyParam + """An object specifying the backup policy for the Droplet.""" + + +class DropletActionRestore(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + image: int + """The ID of a backup of the current Droplet instance to restore from.""" + + +class DropletActionResize(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + disk: bool + """When `true`, the Droplet's disk will be resized in addition to its RAM and CPU. + + This is a permanent change and cannot be reversed as a Droplet's disk size + cannot be decreased. + """ + + size: str + """The slug identifier for the size to which you wish to resize the Droplet.""" + + +class DropletActionRebuild(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + image: Union[str, int] + """ + The image ID of a public or private image or the slug identifier for a public + image. The Droplet will be rebuilt using this image as its base. + """ + + +class DropletActionRename(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + name: str + """The new name for the Droplet.""" + + +class DropletActionChangeKernel(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + kernel: int + """A unique number used to identify and reference a specific kernel.""" + + +class DropletActionSnapshot(TypedDict, total=False): + type: Required[ + Literal[ + "enable_backups", + "disable_backups", + "reboot", + "power_cycle", + "shutdown", + "power_off", + "power_on", + "restore", + "password_reset", + "resize", + "rebuild", + "rename", + "change_kernel", + "enable_ipv6", + "snapshot", + ] + ] + """The type of action to initiate for the Droplet.""" + + name: str + """The name to give the new snapshot of the Droplet.""" + + +ActionInitiateParams: TypeAlias = Union[ + DropletAction, + DropletActionEnableBackups, + DropletActionChangeBackupPolicy, + DropletActionRestore, + DropletActionResize, + DropletActionRebuild, + DropletActionRename, + DropletActionChangeKernel, + DropletActionSnapshot, +] diff --git a/src/gradient/types/gpu_droplets/action_initiate_response.py b/src/gradient/types/gpu_droplets/action_initiate_response.py new file mode 100644 index 00000000..087781d1 --- /dev/null +++ b/src/gradient/types/gpu_droplets/action_initiate_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..shared.action import Action + +__all__ = ["ActionInitiateResponse"] + + +class ActionInitiateResponse(BaseModel): + action: Optional[Action] = None diff --git a/src/gradient/types/gpu_droplets/action_list_params.py b/src/gradient/types/gpu_droplets/action_list_params.py new file mode 100644 index 00000000..dd873288 --- /dev/null +++ b/src/gradient/types/gpu_droplets/action_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ActionListParams"] + + +class ActionListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/action_list_response.py b/src/gradient/types/gpu_droplets/action_list_response.py new file mode 100644 index 00000000..1a20f780 --- /dev/null +++ b/src/gradient/types/gpu_droplets/action_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.action import Action +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["ActionListResponse"] + + +class ActionListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + actions: Optional[List[Action]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/action_retrieve_response.py b/src/gradient/types/gpu_droplets/action_retrieve_response.py new file mode 100644 index 00000000..3856228d --- /dev/null +++ b/src/gradient/types/gpu_droplets/action_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..shared.action import Action + +__all__ = ["ActionRetrieveResponse"] + + +class ActionRetrieveResponse(BaseModel): + action: Optional[Action] = None diff --git a/src/gradient/types/gpu_droplets/associated_resource.py b/src/gradient/types/gpu_droplets/associated_resource.py new file mode 100644 index 00000000..f72c3d32 --- /dev/null +++ b/src/gradient/types/gpu_droplets/associated_resource.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["AssociatedResource"] + + +class AssociatedResource(BaseModel): + id: Optional[str] = None + """The unique identifier for the resource associated with the Droplet.""" + + cost: Optional[str] = None + """ + The cost of the resource in USD per month if the resource is retained after the + Droplet is destroyed. + """ + + name: Optional[str] = None + """The name of the resource associated with the Droplet.""" diff --git a/src/gradient/types/gpu_droplets/autoscale_create_params.py b/src/gradient/types/gpu_droplets/autoscale_create_params.py new file mode 100644 index 00000000..0f3c05a6 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_create_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from .autoscale_pool_static_config_param import AutoscalePoolStaticConfigParam +from .autoscale_pool_dynamic_config_param import AutoscalePoolDynamicConfigParam +from .autoscale_pool_droplet_template_param import AutoscalePoolDropletTemplateParam + +__all__ = ["AutoscaleCreateParams", "Config"] + + +class AutoscaleCreateParams(TypedDict, total=False): + config: Required[Config] + """ + The scaling configuration for an autoscale pool, which is how the pool scales up + and down (either by resource utilization or static configuration). + """ + + droplet_template: Required[AutoscalePoolDropletTemplateParam] + + name: Required[str] + """The human-readable name of the autoscale pool. This field cannot be updated""" + + +Config: TypeAlias = Union[AutoscalePoolStaticConfigParam, AutoscalePoolDynamicConfigParam] diff --git a/src/gradient/types/gpu_droplets/autoscale_create_response.py b/src/gradient/types/gpu_droplets/autoscale_create_response.py new file mode 100644 index 00000000..819297e9 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .autoscale_pool import AutoscalePool + +__all__ = ["AutoscaleCreateResponse"] + + +class AutoscaleCreateResponse(BaseModel): + autoscale_pool: Optional[AutoscalePool] = None diff --git a/src/gradient/types/gpu_droplets/autoscale_list_history_params.py b/src/gradient/types/gpu_droplets/autoscale_list_history_params.py new file mode 100644 index 00000000..f837a11e --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_list_history_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AutoscaleListHistoryParams"] + + +class AutoscaleListHistoryParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/autoscale_list_history_response.py b/src/gradient/types/gpu_droplets/autoscale_list_history_response.py new file mode 100644 index 00000000..843f44d8 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_list_history_response.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["AutoscaleListHistoryResponse", "History"] + + +class History(BaseModel): + created_at: datetime + """ + The creation time of the history event in ISO8601 combined date and time format. + """ + + current_instance_count: int + """The current number of Droplets in the autoscale pool.""" + + desired_instance_count: int + """The target number of Droplets for the autoscale pool after the scaling event.""" + + history_event_id: str + """The unique identifier of the history event.""" + + reason: Literal["CONFIGURATION_CHANGE", "SCALE_UP", "SCALE_DOWN"] + """The reason for the scaling event.""" + + status: Literal["in_progress", "success", "error"] + """The status of the scaling event.""" + + updated_at: datetime + """ + The last updated time of the history event in ISO8601 combined date and time + format. + """ + + +class AutoscaleListHistoryResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + history: Optional[List[History]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/autoscale_list_members_params.py b/src/gradient/types/gpu_droplets/autoscale_list_members_params.py new file mode 100644 index 00000000..5a7f738d --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_list_members_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AutoscaleListMembersParams"] + + +class AutoscaleListMembersParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/autoscale_list_members_response.py b/src/gradient/types/gpu_droplets/autoscale_list_members_response.py new file mode 100644 index 00000000..337ac4e3 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_list_members_response.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["AutoscaleListMembersResponse", "Droplet", "DropletCurrentUtilization"] + + +class DropletCurrentUtilization(BaseModel): + cpu: Optional[float] = None + """The CPU utilization average of the individual Droplet.""" + + memory: Optional[float] = None + """The memory utilization average of the individual Droplet.""" + + +class Droplet(BaseModel): + created_at: datetime + """The creation time of the Droplet in ISO8601 combined date and time format.""" + + current_utilization: DropletCurrentUtilization + + droplet_id: int + """The unique identifier of the Droplet.""" + + health_status: str + """The health status of the Droplet.""" + + status: Literal["provisioning", "active", "deleting", "off"] + """The power status of the Droplet.""" + + updated_at: datetime + """The last updated time of the Droplet in ISO8601 combined date and time format.""" + + +class AutoscaleListMembersResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + droplets: Optional[List[Droplet]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/autoscale_list_params.py b/src/gradient/types/gpu_droplets/autoscale_list_params.py new file mode 100644 index 00000000..3a35e616 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AutoscaleListParams"] + + +class AutoscaleListParams(TypedDict, total=False): + name: str + """The name of the autoscale pool""" + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/autoscale_list_response.py b/src/gradient/types/gpu_droplets/autoscale_list_response.py new file mode 100644 index 00000000..807cb17f --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .autoscale_pool import AutoscalePool +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["AutoscaleListResponse"] + + +class AutoscaleListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + autoscale_pools: Optional[List[AutoscalePool]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/autoscale_pool.py b/src/gradient/types/gpu_droplets/autoscale_pool.py new file mode 100644 index 00000000..2964319e --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_pool.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .current_utilization import CurrentUtilization +from .autoscale_pool_static_config import AutoscalePoolStaticConfig +from .autoscale_pool_dynamic_config import AutoscalePoolDynamicConfig +from .autoscale_pool_droplet_template import AutoscalePoolDropletTemplate + +__all__ = ["AutoscalePool", "Config"] + +Config: TypeAlias = Union[AutoscalePoolStaticConfig, AutoscalePoolDynamicConfig] + + +class AutoscalePool(BaseModel): + id: str + """A unique identifier for each autoscale pool instance. + + This is automatically generated upon autoscale pool creation. + """ + + active_resources_count: int + """The number of active Droplets in the autoscale pool.""" + + config: Config + """ + The scaling configuration for an autoscale pool, which is how the pool scales up + and down (either by resource utilization or static configuration). + """ + + created_at: datetime + """ + A time value given in ISO8601 combined date and time format that represents when + the autoscale pool was created. + """ + + droplet_template: AutoscalePoolDropletTemplate + + name: str + """The human-readable name set for the autoscale pool.""" + + status: Literal["active", "deleting", "error"] + """The current status of the autoscale pool.""" + + updated_at: datetime + """ + A time value given in ISO8601 combined date and time format that represents when + the autoscale pool was last updated. + """ + + current_utilization: Optional[CurrentUtilization] = None diff --git a/src/gradient/types/gpu_droplets/autoscale_pool_droplet_template.py b/src/gradient/types/gpu_droplets/autoscale_pool_droplet_template.py new file mode 100644 index 00000000..2ab2036b --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_pool_droplet_template.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AutoscalePoolDropletTemplate"] + + +class AutoscalePoolDropletTemplate(BaseModel): + image: str + """The Droplet image to be used for all Droplets in the autoscale pool. + + You may specify the slug or the image ID. + """ + + region: Literal[ + "nyc1", "nyc2", "nyc3", "ams2", "ams3", "sfo1", "sfo2", "sfo3", "sgp1", "lon1", "fra1", "tor1", "blr1", "syd1" + ] + """The datacenter in which all of the Droplets will be created.""" + + size: str + """The Droplet size to be used for all Droplets in the autoscale pool.""" + + ssh_keys: List[str] + """The SSH keys to be installed on the Droplets in the autoscale pool. + + You can either specify the key ID or the fingerprint. Requires `ssh_key:read` + scope. + """ + + ipv6: Optional[bool] = None + """Assigns a unique IPv6 address to each of the Droplets in the autoscale pool.""" + + name: Optional[str] = None + """The name(s) to be applied to all Droplets in the autoscale pool.""" + + project_id: Optional[str] = None + """ + The project that the Droplets in the autoscale pool will belong to. Requires + `project:read` scope. + """ + + tags: Optional[List[str]] = None + """ + The tags to apply to each of the Droplets in the autoscale pool. Requires + `tag:read` scope. + """ + + user_data: Optional[str] = None + """ + A string containing user data that cloud-init consumes to configure a Droplet on + first boot. User data is often a cloud-config file or Bash script. It must be + plain text and may not exceed 64 KiB in size. + """ + + vpc_uuid: Optional[str] = None + """The VPC where the Droplets in the autoscale pool will be created. + + The VPC must be in the region where you want to create the Droplets. Requires + `vpc:read` scope. + """ + + with_droplet_agent: Optional[bool] = None + """Installs the Droplet agent. + + This must be set to true to monitor Droplets for resource utilization scaling. + """ diff --git a/src/gradient/types/gpu_droplets/autoscale_pool_droplet_template_param.py b/src/gradient/types/gpu_droplets/autoscale_pool_droplet_template_param.py new file mode 100644 index 00000000..3eb8ac89 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_pool_droplet_template_param.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["AutoscalePoolDropletTemplateParam"] + + +class AutoscalePoolDropletTemplateParam(TypedDict, total=False): + image: Required[str] + """The Droplet image to be used for all Droplets in the autoscale pool. + + You may specify the slug or the image ID. + """ + + region: Required[ + Literal[ + "nyc1", + "nyc2", + "nyc3", + "ams2", + "ams3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "lon1", + "fra1", + "tor1", + "blr1", + "syd1", + ] + ] + """The datacenter in which all of the Droplets will be created.""" + + size: Required[str] + """The Droplet size to be used for all Droplets in the autoscale pool.""" + + ssh_keys: Required[SequenceNotStr[str]] + """The SSH keys to be installed on the Droplets in the autoscale pool. + + You can either specify the key ID or the fingerprint. Requires `ssh_key:read` + scope. + """ + + ipv6: bool + """Assigns a unique IPv6 address to each of the Droplets in the autoscale pool.""" + + name: str + """The name(s) to be applied to all Droplets in the autoscale pool.""" + + project_id: str + """ + The project that the Droplets in the autoscale pool will belong to. Requires + `project:read` scope. + """ + + tags: SequenceNotStr[str] + """ + The tags to apply to each of the Droplets in the autoscale pool. Requires + `tag:read` scope. + """ + + user_data: str + """ + A string containing user data that cloud-init consumes to configure a Droplet on + first boot. User data is often a cloud-config file or Bash script. It must be + plain text and may not exceed 64 KiB in size. + """ + + vpc_uuid: str + """The VPC where the Droplets in the autoscale pool will be created. + + The VPC must be in the region where you want to create the Droplets. Requires + `vpc:read` scope. + """ + + with_droplet_agent: bool + """Installs the Droplet agent. + + This must be set to true to monitor Droplets for resource utilization scaling. + """ diff --git a/src/gradient/types/gpu_droplets/autoscale_pool_dynamic_config.py b/src/gradient/types/gpu_droplets/autoscale_pool_dynamic_config.py new file mode 100644 index 00000000..10f9781b --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_pool_dynamic_config.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["AutoscalePoolDynamicConfig"] + + +class AutoscalePoolDynamicConfig(BaseModel): + max_instances: int + """The maximum number of Droplets in an autoscale pool.""" + + min_instances: int + """The minimum number of Droplets in an autoscale pool.""" + + cooldown_minutes: Optional[int] = None + """The number of minutes to wait between scaling events in an autoscale pool. + + Defaults to 10 minutes. + """ + + target_cpu_utilization: Optional[float] = None + """Target CPU utilization as a decimal.""" + + target_memory_utilization: Optional[float] = None + """Target memory utilization as a decimal.""" diff --git a/src/gradient/types/gpu_droplets/autoscale_pool_dynamic_config_param.py b/src/gradient/types/gpu_droplets/autoscale_pool_dynamic_config_param.py new file mode 100644 index 00000000..af06e73a --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_pool_dynamic_config_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AutoscalePoolDynamicConfigParam"] + + +class AutoscalePoolDynamicConfigParam(TypedDict, total=False): + max_instances: Required[int] + """The maximum number of Droplets in an autoscale pool.""" + + min_instances: Required[int] + """The minimum number of Droplets in an autoscale pool.""" + + cooldown_minutes: int + """The number of minutes to wait between scaling events in an autoscale pool. + + Defaults to 10 minutes. + """ + + target_cpu_utilization: float + """Target CPU utilization as a decimal.""" + + target_memory_utilization: float + """Target memory utilization as a decimal.""" diff --git a/src/gradient/types/gpu_droplets/autoscale_pool_static_config.py b/src/gradient/types/gpu_droplets/autoscale_pool_static_config.py new file mode 100644 index 00000000..cc891007 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_pool_static_config.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["AutoscalePoolStaticConfig"] + + +class AutoscalePoolStaticConfig(BaseModel): + target_number_instances: int + """Fixed number of instances in an autoscale pool.""" diff --git a/src/gradient/types/gpu_droplets/autoscale_pool_static_config_param.py b/src/gradient/types/gpu_droplets/autoscale_pool_static_config_param.py new file mode 100644 index 00000000..a7510d22 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_pool_static_config_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AutoscalePoolStaticConfigParam"] + + +class AutoscalePoolStaticConfigParam(TypedDict, total=False): + target_number_instances: Required[int] + """Fixed number of instances in an autoscale pool.""" diff --git a/src/gradient/types/gpu_droplets/autoscale_retrieve_response.py b/src/gradient/types/gpu_droplets/autoscale_retrieve_response.py new file mode 100644 index 00000000..f383ed03 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .autoscale_pool import AutoscalePool + +__all__ = ["AutoscaleRetrieveResponse"] + + +class AutoscaleRetrieveResponse(BaseModel): + autoscale_pool: Optional[AutoscalePool] = None diff --git a/src/gradient/types/gpu_droplets/autoscale_update_params.py b/src/gradient/types/gpu_droplets/autoscale_update_params.py new file mode 100644 index 00000000..1b96af1e --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_update_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from .autoscale_pool_static_config_param import AutoscalePoolStaticConfigParam +from .autoscale_pool_dynamic_config_param import AutoscalePoolDynamicConfigParam +from .autoscale_pool_droplet_template_param import AutoscalePoolDropletTemplateParam + +__all__ = ["AutoscaleUpdateParams", "Config"] + + +class AutoscaleUpdateParams(TypedDict, total=False): + config: Required[Config] + """ + The scaling configuration for an autoscale pool, which is how the pool scales up + and down (either by resource utilization or static configuration). + """ + + droplet_template: Required[AutoscalePoolDropletTemplateParam] + + name: Required[str] + """The human-readable name of the autoscale pool. This field cannot be updated""" + + +Config: TypeAlias = Union[AutoscalePoolStaticConfigParam, AutoscalePoolDynamicConfigParam] diff --git a/src/gradient/types/gpu_droplets/autoscale_update_response.py b/src/gradient/types/gpu_droplets/autoscale_update_response.py new file mode 100644 index 00000000..09dde2a4 --- /dev/null +++ b/src/gradient/types/gpu_droplets/autoscale_update_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .autoscale_pool import AutoscalePool + +__all__ = ["AutoscaleUpdateResponse"] + + +class AutoscaleUpdateResponse(BaseModel): + autoscale_pool: Optional[AutoscalePool] = None diff --git a/src/gradient/types/gpu_droplets/backup_list_params.py b/src/gradient/types/gpu_droplets/backup_list_params.py new file mode 100644 index 00000000..66fe92aa --- /dev/null +++ b/src/gradient/types/gpu_droplets/backup_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BackupListParams"] + + +class BackupListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/backup_list_policies_params.py b/src/gradient/types/gpu_droplets/backup_list_policies_params.py new file mode 100644 index 00000000..0cdb0ddb --- /dev/null +++ b/src/gradient/types/gpu_droplets/backup_list_policies_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BackupListPoliciesParams"] + + +class BackupListPoliciesParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/backup_list_policies_response.py b/src/gradient/types/gpu_droplets/backup_list_policies_response.py new file mode 100644 index 00000000..73aa9458 --- /dev/null +++ b/src/gradient/types/gpu_droplets/backup_list_policies_response.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from ..._models import BaseModel +from ..shared.page_links import PageLinks +from ..droplet_backup_policy import DropletBackupPolicy +from ..shared.meta_properties import MetaProperties +from ..shared.droplet_next_backup_window import DropletNextBackupWindow + +__all__ = ["BackupListPoliciesResponse", "Policies"] + + +class Policies(BaseModel): + backup_enabled: Optional[bool] = None + """A boolean value indicating whether backups are enabled for the Droplet.""" + + backup_policy: Optional[DropletBackupPolicy] = None + """An object specifying the backup policy for the Droplet.""" + + droplet_id: Optional[int] = None + """The unique identifier for the Droplet.""" + + next_backup_window: Optional[DropletNextBackupWindow] = None + """ + An object containing keys with the start and end times of the window during + which the backup will occur. + """ + + +class BackupListPoliciesResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + links: Optional[PageLinks] = None + + policies: Optional[Dict[str, Policies]] = None + """ + A map where the keys are the Droplet IDs and the values are objects containing + the backup policy information for each Droplet. + """ diff --git a/src/gradient/types/gpu_droplets/backup_list_response.py b/src/gradient/types/gpu_droplets/backup_list_response.py new file mode 100644 index 00000000..c96d573a --- /dev/null +++ b/src/gradient/types/gpu_droplets/backup_list_response.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["BackupListResponse", "Backup"] + + +class Backup(BaseModel): + id: int + """The unique identifier for the snapshot or backup.""" + + created_at: datetime + """ + A time value given in ISO8601 combined date and time format that represents when + the snapshot was created. + """ + + min_disk_size: int + """The minimum size in GB required for a volume or Droplet to use this snapshot.""" + + name: str + """A human-readable name for the snapshot.""" + + regions: List[str] + """An array of the regions that the snapshot is available in. + + The regions are represented by their identifying slug values. + """ + + size_gigabytes: float + """The billable size of the snapshot in gigabytes.""" + + type: Literal["snapshot", "backup"] + """Describes the kind of image. + + It may be one of `snapshot` or `backup`. This specifies whether an image is a + user-generated Droplet snapshot or automatically created Droplet backup. + """ + + +class BackupListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + backups: Optional[List[Backup]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/backup_list_supported_policies_response.py b/src/gradient/types/gpu_droplets/backup_list_supported_policies_response.py new file mode 100644 index 00000000..219cfc34 --- /dev/null +++ b/src/gradient/types/gpu_droplets/backup_list_supported_policies_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["BackupListSupportedPoliciesResponse", "SupportedPolicy"] + + +class SupportedPolicy(BaseModel): + name: Optional[str] = None + """The name of the Droplet backup plan.""" + + possible_days: Optional[List[str]] = None + """The day of the week the backup will occur.""" + + possible_window_starts: Optional[List[int]] = None + """An array of integers representing the hours of the day that a backup can start.""" + + retention_period_days: Optional[int] = None + """The number of days that a backup will be kept.""" + + window_length_hours: Optional[int] = None + """The number of hours that a backup window is open.""" + + +class BackupListSupportedPoliciesResponse(BaseModel): + supported_policies: Optional[List[SupportedPolicy]] = None diff --git a/src/gradient/types/gpu_droplets/backup_retrieve_policy_response.py b/src/gradient/types/gpu_droplets/backup_retrieve_policy_response.py new file mode 100644 index 00000000..38288dea --- /dev/null +++ b/src/gradient/types/gpu_droplets/backup_retrieve_policy_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..droplet_backup_policy import DropletBackupPolicy +from ..shared.droplet_next_backup_window import DropletNextBackupWindow + +__all__ = ["BackupRetrievePolicyResponse", "Policy"] + + +class Policy(BaseModel): + backup_enabled: Optional[bool] = None + """A boolean value indicating whether backups are enabled for the Droplet.""" + + backup_policy: Optional[DropletBackupPolicy] = None + """An object specifying the backup policy for the Droplet.""" + + droplet_id: Optional[int] = None + """The unique identifier for the Droplet.""" + + next_backup_window: Optional[DropletNextBackupWindow] = None + """ + An object containing keys with the start and end times of the window during + which the backup will occur. + """ + + +class BackupRetrievePolicyResponse(BaseModel): + policy: Optional[Policy] = None diff --git a/src/gradient/types/gpu_droplets/current_utilization.py b/src/gradient/types/gpu_droplets/current_utilization.py new file mode 100644 index 00000000..f2cb0b6c --- /dev/null +++ b/src/gradient/types/gpu_droplets/current_utilization.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["CurrentUtilization"] + + +class CurrentUtilization(BaseModel): + cpu: Optional[float] = None + """The average CPU utilization of the autoscale pool.""" + + memory: Optional[float] = None + """The average memory utilization of the autoscale pool.""" diff --git a/src/gradient/types/gpu_droplets/destroy_with_associated_resource_check_status_response.py b/src/gradient/types/gpu_droplets/destroy_with_associated_resource_check_status_response.py new file mode 100644 index 00000000..f2f2ff67 --- /dev/null +++ b/src/gradient/types/gpu_droplets/destroy_with_associated_resource_check_status_response.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ..._models import BaseModel +from .destroyed_associated_resource import DestroyedAssociatedResource + +__all__ = ["DestroyWithAssociatedResourceCheckStatusResponse", "Resources"] + + +class Resources(BaseModel): + floating_ips: Optional[List[DestroyedAssociatedResource]] = None + + reserved_ips: Optional[List[DestroyedAssociatedResource]] = None + + snapshots: Optional[List[DestroyedAssociatedResource]] = None + + volume_snapshots: Optional[List[DestroyedAssociatedResource]] = None + + volumes: Optional[List[DestroyedAssociatedResource]] = None + + +class DestroyWithAssociatedResourceCheckStatusResponse(BaseModel): + completed_at: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format indicating when the + requested action was completed. + """ + + droplet: Optional[DestroyedAssociatedResource] = None + """An object containing information about a resource scheduled for deletion.""" + + failures: Optional[int] = None + """A count of the associated resources that failed to be destroyed, if any.""" + + resources: Optional[Resources] = None + """ + An object containing additional information about resource related to a Droplet + requested to be destroyed. + """ diff --git a/src/gradient/types/gpu_droplets/destroy_with_associated_resource_delete_selective_params.py b/src/gradient/types/gpu_droplets/destroy_with_associated_resource_delete_selective_params.py new file mode 100644 index 00000000..9a9730e7 --- /dev/null +++ b/src/gradient/types/gpu_droplets/destroy_with_associated_resource_delete_selective_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["DestroyWithAssociatedResourceDeleteSelectiveParams"] + + +class DestroyWithAssociatedResourceDeleteSelectiveParams(TypedDict, total=False): + floating_ips: SequenceNotStr[str] + """ + An array of unique identifiers for the floating IPs to be scheduled for + deletion. + """ + + reserved_ips: SequenceNotStr[str] + """ + An array of unique identifiers for the reserved IPs to be scheduled for + deletion. + """ + + snapshots: SequenceNotStr[str] + """An array of unique identifiers for the snapshots to be scheduled for deletion.""" + + volume_snapshots: SequenceNotStr[str] + """ + An array of unique identifiers for the volume snapshots to be scheduled for + deletion. + """ + + volumes: SequenceNotStr[str] + """An array of unique identifiers for the volumes to be scheduled for deletion.""" diff --git a/src/gradient/types/gpu_droplets/destroy_with_associated_resource_list_response.py b/src/gradient/types/gpu_droplets/destroy_with_associated_resource_list_response.py new file mode 100644 index 00000000..ef4c6c99 --- /dev/null +++ b/src/gradient/types/gpu_droplets/destroy_with_associated_resource_list_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .associated_resource import AssociatedResource + +__all__ = ["DestroyWithAssociatedResourceListResponse"] + + +class DestroyWithAssociatedResourceListResponse(BaseModel): + floating_ips: Optional[List[AssociatedResource]] = None + """ + Floating IPs that are associated with this Droplet. Requires `reserved_ip:read` + scope. + """ + + reserved_ips: Optional[List[AssociatedResource]] = None + """ + Reserved IPs that are associated with this Droplet. Requires `reserved_ip:read` + scope. + """ + + snapshots: Optional[List[AssociatedResource]] = None + """Snapshots that are associated with this Droplet. Requires `image:read` scope.""" + + volume_snapshots: Optional[List[AssociatedResource]] = None + """ + Volume Snapshots that are associated with this Droplet. Requires + `block_storage_snapshot:read` scope. + """ + + volumes: Optional[List[AssociatedResource]] = None + """ + Volumes that are associated with this Droplet. Requires `block_storage:read` + scope. + """ diff --git a/src/gradient/types/gpu_droplets/destroyed_associated_resource.py b/src/gradient/types/gpu_droplets/destroyed_associated_resource.py new file mode 100644 index 00000000..358c14e9 --- /dev/null +++ b/src/gradient/types/gpu_droplets/destroyed_associated_resource.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["DestroyedAssociatedResource"] + + +class DestroyedAssociatedResource(BaseModel): + id: Optional[str] = None + """The unique identifier for the resource scheduled for deletion.""" + + destroyed_at: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format indicating when the + resource was destroyed if the request was successful. + """ + + error_message: Optional[str] = None + """ + A string indicating that the resource was not successfully destroyed and + providing additional information. + """ + + name: Optional[str] = None + """The name of the resource scheduled for deletion.""" diff --git a/src/gradient/types/gpu_droplets/domains.py b/src/gradient/types/gpu_droplets/domains.py new file mode 100644 index 00000000..6a9400f9 --- /dev/null +++ b/src/gradient/types/gpu_droplets/domains.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["Domains"] + + +class Domains(BaseModel): + certificate_id: Optional[str] = None + """The ID of the TLS certificate used for SSL termination.""" + + is_managed: Optional[bool] = None + """A boolean value indicating if the domain is already managed by DigitalOcean. + + If true, all A and AAAA records required to enable Global load balancers will be + automatically added. + """ + + name: Optional[str] = None + """FQDN to associate with a Global load balancer.""" diff --git a/src/gradient/types/gpu_droplets/domains_param.py b/src/gradient/types/gpu_droplets/domains_param.py new file mode 100644 index 00000000..d2d21faf --- /dev/null +++ b/src/gradient/types/gpu_droplets/domains_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["DomainsParam"] + + +class DomainsParam(TypedDict, total=False): + certificate_id: str + """The ID of the TLS certificate used for SSL termination.""" + + is_managed: bool + """A boolean value indicating if the domain is already managed by DigitalOcean. + + If true, all A and AAAA records required to enable Global load balancers will be + automatically added. + """ + + name: str + """FQDN to associate with a Global load balancer.""" diff --git a/src/gradient/types/gpu_droplets/firewall.py b/src/gradient/types/gpu_droplets/firewall.py new file mode 100644 index 00000000..0eb352a1 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall.py @@ -0,0 +1,98 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.firewall_rule_target import FirewallRuleTarget + +__all__ = ["Firewall", "InboundRule", "OutboundRule", "PendingChange"] + + +class InboundRule(BaseModel): + ports: str + """ + The ports on which traffic will be allowed specified as a string containing a + single port, a range (e.g. "8000-9000"), or "0" when all ports are open for a + protocol. For ICMP rules this parameter will always return "0". + """ + + protocol: Literal["tcp", "udp", "icmp"] + """The type of traffic to be allowed. This may be one of `tcp`, `udp`, or `icmp`.""" + + sources: FirewallRuleTarget + """An object specifying locations from which inbound traffic will be accepted.""" + + +class OutboundRule(BaseModel): + destinations: FirewallRuleTarget + """An object specifying locations to which outbound traffic that will be allowed.""" + + ports: str + """ + The ports on which traffic will be allowed specified as a string containing a + single port, a range (e.g. "8000-9000"), or "0" when all ports are open for a + protocol. For ICMP rules this parameter will always return "0". + """ + + protocol: Literal["tcp", "udp", "icmp"] + """The type of traffic to be allowed. This may be one of `tcp`, `udp`, or `icmp`.""" + + +class PendingChange(BaseModel): + droplet_id: Optional[int] = None + + removing: Optional[bool] = None + + status: Optional[str] = None + + +class Firewall(BaseModel): + id: Optional[str] = None + """A unique ID that can be used to identify and reference a firewall.""" + + created_at: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format that represents when + the firewall was created. + """ + + droplet_ids: Optional[List[int]] = None + """An array containing the IDs of the Droplets assigned to the firewall. + + Requires `droplet:read` scope. + """ + + inbound_rules: Optional[List[InboundRule]] = None + + name: Optional[str] = None + """A human-readable name for a firewall. + + The name must begin with an alphanumeric character. Subsequent characters must + either be alphanumeric characters, a period (.), or a dash (-). + """ + + outbound_rules: Optional[List[OutboundRule]] = None + + pending_changes: Optional[List[PendingChange]] = None + """ + An array of objects each containing the fields "droplet_id", "removing", and + "status". It is provided to detail exactly which Droplets are having their + security policies updated. When empty, all changes have been successfully + applied. + """ + + status: Optional[Literal["waiting", "succeeded", "failed"]] = None + """A status string indicating the current state of the firewall. + + This can be "waiting", "succeeded", or "failed". + """ + + tags: Optional[List[str]] = None + """A flat array of tag names as strings to be applied to the resource. + + Tag names must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + """ diff --git a/src/gradient/types/gpu_droplets/firewall_create_params.py b/src/gradient/types/gpu_droplets/firewall_create_params.py new file mode 100644 index 00000000..b10ae98e --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .firewall_param import FirewallParam + +__all__ = ["FirewallCreateParams", "Body"] + + +class FirewallCreateParams(TypedDict, total=False): + body: Body + + +class Body(FirewallParam, total=False): + pass diff --git a/src/gradient/types/gpu_droplets/firewall_create_response.py b/src/gradient/types/gpu_droplets/firewall_create_response.py new file mode 100644 index 00000000..be30113a --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .firewall import Firewall +from ..._models import BaseModel + +__all__ = ["FirewallCreateResponse"] + + +class FirewallCreateResponse(BaseModel): + firewall: Optional[Firewall] = None diff --git a/src/gradient/types/gpu_droplets/firewall_list_params.py b/src/gradient/types/gpu_droplets/firewall_list_params.py new file mode 100644 index 00000000..155cc480 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["FirewallListParams"] + + +class FirewallListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/firewall_list_response.py b/src/gradient/types/gpu_droplets/firewall_list_response.py new file mode 100644 index 00000000..ec0af688 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .firewall import Firewall +from ..._models import BaseModel +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["FirewallListResponse"] + + +class FirewallListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + firewalls: Optional[List[Firewall]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/firewall_param.py b/src/gradient/types/gpu_droplets/firewall_param.py new file mode 100644 index 00000000..8b5a5a15 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall_param.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr +from ..shared_params.firewall_rule_target import FirewallRuleTarget + +__all__ = ["FirewallParam", "InboundRule", "OutboundRule"] + + +class InboundRule(TypedDict, total=False): + ports: Required[str] + """ + The ports on which traffic will be allowed specified as a string containing a + single port, a range (e.g. "8000-9000"), or "0" when all ports are open for a + protocol. For ICMP rules this parameter will always return "0". + """ + + protocol: Required[Literal["tcp", "udp", "icmp"]] + """The type of traffic to be allowed. This may be one of `tcp`, `udp`, or `icmp`.""" + + sources: Required[FirewallRuleTarget] + """An object specifying locations from which inbound traffic will be accepted.""" + + +class OutboundRule(TypedDict, total=False): + destinations: Required[FirewallRuleTarget] + """An object specifying locations to which outbound traffic that will be allowed.""" + + ports: Required[str] + """ + The ports on which traffic will be allowed specified as a string containing a + single port, a range (e.g. "8000-9000"), or "0" when all ports are open for a + protocol. For ICMP rules this parameter will always return "0". + """ + + protocol: Required[Literal["tcp", "udp", "icmp"]] + """The type of traffic to be allowed. This may be one of `tcp`, `udp`, or `icmp`.""" + + +class FirewallParam(TypedDict, total=False): + droplet_ids: Optional[Iterable[int]] + """An array containing the IDs of the Droplets assigned to the firewall. + + Requires `droplet:read` scope. + """ + + inbound_rules: Optional[Iterable[InboundRule]] + + name: str + """A human-readable name for a firewall. + + The name must begin with an alphanumeric character. Subsequent characters must + either be alphanumeric characters, a period (.), or a dash (-). + """ + + outbound_rules: Optional[Iterable[OutboundRule]] + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + """ diff --git a/src/gradient/types/gpu_droplets/firewall_retrieve_response.py b/src/gradient/types/gpu_droplets/firewall_retrieve_response.py new file mode 100644 index 00000000..bb29a174 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .firewall import Firewall +from ..._models import BaseModel + +__all__ = ["FirewallRetrieveResponse"] + + +class FirewallRetrieveResponse(BaseModel): + firewall: Optional[Firewall] = None diff --git a/src/gradient/types/gpu_droplets/firewall_update_params.py b/src/gradient/types/gpu_droplets/firewall_update_params.py new file mode 100644 index 00000000..c2d0691d --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall_update_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .firewall_param import FirewallParam + +__all__ = ["FirewallUpdateParams"] + + +class FirewallUpdateParams(TypedDict, total=False): + firewall: Required[FirewallParam] diff --git a/src/gradient/types/gpu_droplets/firewall_update_response.py b/src/gradient/types/gpu_droplets/firewall_update_response.py new file mode 100644 index 00000000..cb8ff702 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewall_update_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .firewall import Firewall +from ..._models import BaseModel + +__all__ = ["FirewallUpdateResponse"] + + +class FirewallUpdateResponse(BaseModel): + firewall: Optional[Firewall] = None diff --git a/src/gradient/types/gpu_droplets/firewalls/__init__.py b/src/gradient/types/gpu_droplets/firewalls/__init__.py new file mode 100644 index 00000000..6ba459d9 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewalls/__init__.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .tag_add_params import TagAddParams as TagAddParams +from .rule_add_params import RuleAddParams as RuleAddParams +from .tag_remove_params import TagRemoveParams as TagRemoveParams +from .droplet_add_params import DropletAddParams as DropletAddParams +from .rule_remove_params import RuleRemoveParams as RuleRemoveParams +from .droplet_remove_params import DropletRemoveParams as DropletRemoveParams diff --git a/src/gradient/types/gpu_droplets/firewalls/droplet_add_params.py b/src/gradient/types/gpu_droplets/firewalls/droplet_add_params.py new file mode 100644 index 00000000..35a403a5 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewalls/droplet_add_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["DropletAddParams"] + + +class DropletAddParams(TypedDict, total=False): + droplet_ids: Required[Iterable[int]] + """An array containing the IDs of the Droplets to be assigned to the firewall.""" diff --git a/src/gradient/types/gpu_droplets/firewalls/droplet_remove_params.py b/src/gradient/types/gpu_droplets/firewalls/droplet_remove_params.py new file mode 100644 index 00000000..5aea18e8 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewalls/droplet_remove_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["DropletRemoveParams"] + + +class DropletRemoveParams(TypedDict, total=False): + droplet_ids: Required[Iterable[int]] + """An array containing the IDs of the Droplets to be removed from the firewall.""" diff --git a/src/gradient/types/gpu_droplets/firewalls/rule_add_params.py b/src/gradient/types/gpu_droplets/firewalls/rule_add_params.py new file mode 100644 index 00000000..1f49e55a --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewalls/rule_add_params.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ...shared_params.firewall_rule_target import FirewallRuleTarget + +__all__ = ["RuleAddParams", "InboundRule", "OutboundRule"] + + +class RuleAddParams(TypedDict, total=False): + inbound_rules: Optional[Iterable[InboundRule]] + + outbound_rules: Optional[Iterable[OutboundRule]] + + +class InboundRule(TypedDict, total=False): + ports: Required[str] + """ + The ports on which traffic will be allowed specified as a string containing a + single port, a range (e.g. "8000-9000"), or "0" when all ports are open for a + protocol. For ICMP rules this parameter will always return "0". + """ + + protocol: Required[Literal["tcp", "udp", "icmp"]] + """The type of traffic to be allowed. This may be one of `tcp`, `udp`, or `icmp`.""" + + sources: Required[FirewallRuleTarget] + """An object specifying locations from which inbound traffic will be accepted.""" + + +class OutboundRule(TypedDict, total=False): + destinations: Required[FirewallRuleTarget] + """An object specifying locations to which outbound traffic that will be allowed.""" + + ports: Required[str] + """ + The ports on which traffic will be allowed specified as a string containing a + single port, a range (e.g. "8000-9000"), or "0" when all ports are open for a + protocol. For ICMP rules this parameter will always return "0". + """ + + protocol: Required[Literal["tcp", "udp", "icmp"]] + """The type of traffic to be allowed. This may be one of `tcp`, `udp`, or `icmp`.""" diff --git a/src/gradient/types/gpu_droplets/firewalls/rule_remove_params.py b/src/gradient/types/gpu_droplets/firewalls/rule_remove_params.py new file mode 100644 index 00000000..b6bb05df --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewalls/rule_remove_params.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ...shared_params.firewall_rule_target import FirewallRuleTarget + +__all__ = ["RuleRemoveParams", "InboundRule", "OutboundRule"] + + +class RuleRemoveParams(TypedDict, total=False): + inbound_rules: Optional[Iterable[InboundRule]] + + outbound_rules: Optional[Iterable[OutboundRule]] + + +class InboundRule(TypedDict, total=False): + ports: Required[str] + """ + The ports on which traffic will be allowed specified as a string containing a + single port, a range (e.g. "8000-9000"), or "0" when all ports are open for a + protocol. For ICMP rules this parameter will always return "0". + """ + + protocol: Required[Literal["tcp", "udp", "icmp"]] + """The type of traffic to be allowed. This may be one of `tcp`, `udp`, or `icmp`.""" + + sources: Required[FirewallRuleTarget] + """An object specifying locations from which inbound traffic will be accepted.""" + + +class OutboundRule(TypedDict, total=False): + destinations: Required[FirewallRuleTarget] + """An object specifying locations to which outbound traffic that will be allowed.""" + + ports: Required[str] + """ + The ports on which traffic will be allowed specified as a string containing a + single port, a range (e.g. "8000-9000"), or "0" when all ports are open for a + protocol. For ICMP rules this parameter will always return "0". + """ + + protocol: Required[Literal["tcp", "udp", "icmp"]] + """The type of traffic to be allowed. This may be one of `tcp`, `udp`, or `icmp`.""" diff --git a/src/gradient/types/gpu_droplets/firewalls/tag_add_params.py b/src/gradient/types/gpu_droplets/firewalls/tag_add_params.py new file mode 100644 index 00000000..c3b9696e --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewalls/tag_add_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["TagAddParams"] + + +class TagAddParams(TypedDict, total=False): + tags: Required[Optional[SequenceNotStr[str]]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + """ diff --git a/src/gradient/types/gpu_droplets/firewalls/tag_remove_params.py b/src/gradient/types/gpu_droplets/firewalls/tag_remove_params.py new file mode 100644 index 00000000..bdd848f3 --- /dev/null +++ b/src/gradient/types/gpu_droplets/firewalls/tag_remove_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["TagRemoveParams"] + + +class TagRemoveParams(TypedDict, total=False): + tags: Required[Optional[SequenceNotStr[str]]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + """ diff --git a/src/gradient/types/gpu_droplets/floating_ip.py b/src/gradient/types/gpu_droplets/floating_ip.py new file mode 100644 index 00000000..81c58753 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ip.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import TypeAlias + +from ..shared import region, droplet +from ..._models import BaseModel + +__all__ = ["FloatingIP", "Droplet", "Region"] + +Droplet: TypeAlias = Union[droplet.Droplet, Optional[object]] + + +class Region(region.Region): + pass + + +class FloatingIP(BaseModel): + droplet: Optional[Droplet] = None + """The Droplet that the floating IP has been assigned to. + + When you query a floating IP, if it is assigned to a Droplet, the entire Droplet + object will be returned. If it is not assigned, the value will be null. + + Requires `droplet:read` scope. + """ + + ip: Optional[str] = None + """The public IP address of the floating IP. It also serves as its identifier.""" + + locked: Optional[bool] = None + """ + A boolean value indicating whether or not the floating IP has pending actions + preventing new ones from being submitted. + """ + + project_id: Optional[str] = None + """The UUID of the project to which the reserved IP currently belongs. + + Requires `project:read` scope. + """ + + region: Optional[Region] = None + """The region that the floating IP is reserved to. + + When you query a floating IP, the entire region object will be returned. + """ diff --git a/src/gradient/types/gpu_droplets/floating_ip_create_params.py b/src/gradient/types/gpu_droplets/floating_ip_create_params.py new file mode 100644 index 00000000..2adadc27 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ip_create_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +__all__ = ["FloatingIPCreateParams", "AssignToDroplet", "ReserveToRegion"] + + +class AssignToDroplet(TypedDict, total=False): + droplet_id: Required[int] + """The ID of the Droplet that the floating IP will be assigned to.""" + + +class ReserveToRegion(TypedDict, total=False): + region: Required[str] + """The slug identifier for the region the floating IP will be reserved to.""" + + project_id: str + """The UUID of the project to which the floating IP will be assigned.""" + + +FloatingIPCreateParams: TypeAlias = Union[AssignToDroplet, ReserveToRegion] diff --git a/src/gradient/types/gpu_droplets/floating_ip_create_response.py b/src/gradient/types/gpu_droplets/floating_ip_create_response.py new file mode 100644 index 00000000..04668b84 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ip_create_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .floating_ip import FloatingIP +from ..shared.action_link import ActionLink + +__all__ = ["FloatingIPCreateResponse", "Links"] + + +class Links(BaseModel): + actions: Optional[List[ActionLink]] = None + + droplets: Optional[List[ActionLink]] = None + + +class FloatingIPCreateResponse(BaseModel): + floating_ip: Optional[FloatingIP] = None + + links: Optional[Links] = None diff --git a/src/gradient/types/gpu_droplets/floating_ip_list_params.py b/src/gradient/types/gpu_droplets/floating_ip_list_params.py new file mode 100644 index 00000000..2e054075 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ip_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["FloatingIPListParams"] + + +class FloatingIPListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/floating_ip_list_response.py b/src/gradient/types/gpu_droplets/floating_ip_list_response.py new file mode 100644 index 00000000..734011d2 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ip_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .floating_ip import FloatingIP +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["FloatingIPListResponse"] + + +class FloatingIPListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + floating_ips: Optional[List[FloatingIP]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/floating_ip_retrieve_response.py b/src/gradient/types/gpu_droplets/floating_ip_retrieve_response.py new file mode 100644 index 00000000..b7ec77d4 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ip_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .floating_ip import FloatingIP + +__all__ = ["FloatingIPRetrieveResponse"] + + +class FloatingIPRetrieveResponse(BaseModel): + floating_ip: Optional[FloatingIP] = None diff --git a/src/gradient/types/gpu_droplets/floating_ips/__init__.py b/src/gradient/types/gpu_droplets/floating_ips/__init__.py new file mode 100644 index 00000000..a597418e --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ips/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .action_create_params import ActionCreateParams as ActionCreateParams +from .action_list_response import ActionListResponse as ActionListResponse +from .action_create_response import ActionCreateResponse as ActionCreateResponse +from .action_retrieve_response import ActionRetrieveResponse as ActionRetrieveResponse diff --git a/src/gradient/types/gpu_droplets/floating_ips/action_create_params.py b/src/gradient/types/gpu_droplets/floating_ips/action_create_params.py new file mode 100644 index 00000000..c84f5df7 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ips/action_create_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ActionCreateParams", "FloatingIPActionUnassign", "FloatingIPActionAssign"] + + +class FloatingIPActionUnassign(TypedDict, total=False): + type: Required[Literal["assign", "unassign"]] + """The type of action to initiate for the floating IP.""" + + +class FloatingIPActionAssign(TypedDict, total=False): + droplet_id: Required[int] + """The ID of the Droplet that the floating IP will be assigned to.""" + + type: Required[Literal["assign", "unassign"]] + """The type of action to initiate for the floating IP.""" + + +ActionCreateParams: TypeAlias = Union[FloatingIPActionUnassign, FloatingIPActionAssign] diff --git a/src/gradient/types/gpu_droplets/floating_ips/action_create_response.py b/src/gradient/types/gpu_droplets/floating_ips/action_create_response.py new file mode 100644 index 00000000..90acd8c9 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ips/action_create_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...shared import action +from ...._models import BaseModel + +__all__ = ["ActionCreateResponse", "Action"] + + +class Action(action.Action): + project_id: Optional[str] = None + """The UUID of the project to which the reserved IP currently belongs.""" + + +class ActionCreateResponse(BaseModel): + action: Optional[Action] = None diff --git a/src/gradient/types/gpu_droplets/floating_ips/action_list_response.py b/src/gradient/types/gpu_droplets/floating_ips/action_list_response.py new file mode 100644 index 00000000..2f4edac5 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ips/action_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel +from ...shared.action import Action +from ...shared.page_links import PageLinks +from ...shared.meta_properties import MetaProperties + +__all__ = ["ActionListResponse"] + + +class ActionListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + actions: Optional[List[Action]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/floating_ips/action_retrieve_response.py b/src/gradient/types/gpu_droplets/floating_ips/action_retrieve_response.py new file mode 100644 index 00000000..d94554be --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ips/action_retrieve_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...shared import action +from ...._models import BaseModel + +__all__ = ["ActionRetrieveResponse", "Action"] + + +class Action(action.Action): + project_id: Optional[str] = None + """The UUID of the project to which the reserved IP currently belongs.""" + + +class ActionRetrieveResponse(BaseModel): + action: Optional[Action] = None diff --git a/src/gradient/types/gpu_droplets/forwarding_rule.py b/src/gradient/types/gpu_droplets/forwarding_rule.py new file mode 100644 index 00000000..40a310ab --- /dev/null +++ b/src/gradient/types/gpu_droplets/forwarding_rule.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ForwardingRule"] + + +class ForwardingRule(BaseModel): + entry_port: int + """ + An integer representing the port on which the load balancer instance will + listen. + """ + + entry_protocol: Literal["http", "https", "http2", "http3", "tcp", "udp"] + """The protocol used for traffic to the load balancer. + + The possible values are: `http`, `https`, `http2`, `http3`, `tcp`, or `udp`. If + you set the `entry_protocol` to `udp`, the `target_protocol` must be set to + `udp`. When using UDP, the load balancer requires that you set up a health check + with a port that uses TCP, HTTP, or HTTPS to work properly. + """ + + target_port: int + """ + An integer representing the port on the backend Droplets to which the load + balancer will send traffic. + """ + + target_protocol: Literal["http", "https", "http2", "tcp", "udp"] + """The protocol used for traffic from the load balancer to the backend Droplets. + + The possible values are: `http`, `https`, `http2`, `tcp`, or `udp`. If you set + the `target_protocol` to `udp`, the `entry_protocol` must be set to `udp`. When + using UDP, the load balancer requires that you set up a health check with a port + that uses TCP, HTTP, or HTTPS to work properly. + """ + + certificate_id: Optional[str] = None + """The ID of the TLS certificate used for SSL termination if enabled.""" + + tls_passthrough: Optional[bool] = None + """ + A boolean value indicating whether SSL encrypted traffic will be passed through + to the backend Droplets. + """ diff --git a/src/gradient/types/gpu_droplets/forwarding_rule_param.py b/src/gradient/types/gpu_droplets/forwarding_rule_param.py new file mode 100644 index 00000000..70285bf6 --- /dev/null +++ b/src/gradient/types/gpu_droplets/forwarding_rule_param.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ForwardingRuleParam"] + + +class ForwardingRuleParam(TypedDict, total=False): + entry_port: Required[int] + """ + An integer representing the port on which the load balancer instance will + listen. + """ + + entry_protocol: Required[Literal["http", "https", "http2", "http3", "tcp", "udp"]] + """The protocol used for traffic to the load balancer. + + The possible values are: `http`, `https`, `http2`, `http3`, `tcp`, or `udp`. If + you set the `entry_protocol` to `udp`, the `target_protocol` must be set to + `udp`. When using UDP, the load balancer requires that you set up a health check + with a port that uses TCP, HTTP, or HTTPS to work properly. + """ + + target_port: Required[int] + """ + An integer representing the port on the backend Droplets to which the load + balancer will send traffic. + """ + + target_protocol: Required[Literal["http", "https", "http2", "tcp", "udp"]] + """The protocol used for traffic from the load balancer to the backend Droplets. + + The possible values are: `http`, `https`, `http2`, `tcp`, or `udp`. If you set + the `target_protocol` to `udp`, the `entry_protocol` must be set to `udp`. When + using UDP, the load balancer requires that you set up a health check with a port + that uses TCP, HTTP, or HTTPS to work properly. + """ + + certificate_id: str + """The ID of the TLS certificate used for SSL termination if enabled.""" + + tls_passthrough: bool + """ + A boolean value indicating whether SSL encrypted traffic will be passed through + to the backend Droplets. + """ diff --git a/src/gradient/types/gpu_droplets/glb_settings.py b/src/gradient/types/gpu_droplets/glb_settings.py new file mode 100644 index 00000000..9aa790d8 --- /dev/null +++ b/src/gradient/types/gpu_droplets/glb_settings.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["GlbSettings", "Cdn"] + + +class Cdn(BaseModel): + is_enabled: Optional[bool] = None + """A boolean flag to enable CDN caching.""" + + +class GlbSettings(BaseModel): + cdn: Optional[Cdn] = None + """An object specifying CDN configurations for a Global load balancer.""" + + failover_threshold: Optional[int] = None + """ + An integer value as a percentage to indicate failure threshold to decide how the + regional priorities will take effect. A value of `50` would indicate that the + Global load balancer will choose a lower priority region to forward traffic to + once this failure threshold has been reached for the higher priority region. + """ + + region_priorities: Optional[Dict[str, int]] = None + """ + A map of region string to an integer priority value indicating preference for + which regional target a Global load balancer will forward traffic to. A lower + value indicates a higher priority. + """ + + target_port: Optional[int] = None + """ + An integer representing the port on the target backends which the load balancer + will forward traffic to. + """ + + target_protocol: Optional[Literal["http", "https", "http2"]] = None + """ + The protocol used for forwarding traffic from the load balancer to the target + backends. The possible values are `http`, `https` and `http2`. + """ diff --git a/src/gradient/types/gpu_droplets/glb_settings_param.py b/src/gradient/types/gpu_droplets/glb_settings_param.py new file mode 100644 index 00000000..f1b25c8b --- /dev/null +++ b/src/gradient/types/gpu_droplets/glb_settings_param.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Literal, TypedDict + +__all__ = ["GlbSettingsParam", "Cdn"] + + +class Cdn(TypedDict, total=False): + is_enabled: bool + """A boolean flag to enable CDN caching.""" + + +class GlbSettingsParam(TypedDict, total=False): + cdn: Cdn + """An object specifying CDN configurations for a Global load balancer.""" + + failover_threshold: int + """ + An integer value as a percentage to indicate failure threshold to decide how the + regional priorities will take effect. A value of `50` would indicate that the + Global load balancer will choose a lower priority region to forward traffic to + once this failure threshold has been reached for the higher priority region. + """ + + region_priorities: Dict[str, int] + """ + A map of region string to an integer priority value indicating preference for + which regional target a Global load balancer will forward traffic to. A lower + value indicates a higher priority. + """ + + target_port: int + """ + An integer representing the port on the target backends which the load balancer + will forward traffic to. + """ + + target_protocol: Literal["http", "https", "http2"] + """ + The protocol used for forwarding traffic from the load balancer to the target + backends. The possible values are `http`, `https` and `http2`. + """ diff --git a/src/gradient/types/gpu_droplets/health_check.py b/src/gradient/types/gpu_droplets/health_check.py new file mode 100644 index 00000000..db44d84e --- /dev/null +++ b/src/gradient/types/gpu_droplets/health_check.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["HealthCheck"] + + +class HealthCheck(BaseModel): + check_interval_seconds: Optional[int] = None + """The number of seconds between between two consecutive health checks.""" + + healthy_threshold: Optional[int] = None + """ + The number of times a health check must pass for a backend Droplet to be marked + "healthy" and be re-added to the pool. + """ + + path: Optional[str] = None + """ + The path on the backend Droplets to which the load balancer instance will send a + request. + """ + + port: Optional[int] = None + """ + An integer representing the port on the backend Droplets on which the health + check will attempt a connection. + """ + + protocol: Optional[Literal["http", "https", "tcp"]] = None + """The protocol used for health checks sent to the backend Droplets. + + The possible values are `http`, `https`, or `tcp`. + """ + + response_timeout_seconds: Optional[int] = None + """ + The number of seconds the load balancer instance will wait for a response until + marking a health check as failed. + """ + + unhealthy_threshold: Optional[int] = None + """ + The number of times a health check must fail for a backend Droplet to be marked + "unhealthy" and be removed from the pool. + """ diff --git a/src/gradient/types/gpu_droplets/health_check_param.py b/src/gradient/types/gpu_droplets/health_check_param.py new file mode 100644 index 00000000..e840f818 --- /dev/null +++ b/src/gradient/types/gpu_droplets/health_check_param.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["HealthCheckParam"] + + +class HealthCheckParam(TypedDict, total=False): + check_interval_seconds: int + """The number of seconds between between two consecutive health checks.""" + + healthy_threshold: int + """ + The number of times a health check must pass for a backend Droplet to be marked + "healthy" and be re-added to the pool. + """ + + path: str + """ + The path on the backend Droplets to which the load balancer instance will send a + request. + """ + + port: int + """ + An integer representing the port on the backend Droplets on which the health + check will attempt a connection. + """ + + protocol: Literal["http", "https", "tcp"] + """The protocol used for health checks sent to the backend Droplets. + + The possible values are `http`, `https`, or `tcp`. + """ + + response_timeout_seconds: int + """ + The number of seconds the load balancer instance will wait for a response until + marking a health check as failed. + """ + + unhealthy_threshold: int + """ + The number of times a health check must fail for a backend Droplet to be marked + "unhealthy" and be removed from the pool. + """ diff --git a/src/gradient/types/gpu_droplets/image_create_params.py b/src/gradient/types/gpu_droplets/image_create_params.py new file mode 100644 index 00000000..baae3bf5 --- /dev/null +++ b/src/gradient/types/gpu_droplets/image_create_params.py @@ -0,0 +1,83 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["ImageCreateParams"] + + +class ImageCreateParams(TypedDict, total=False): + description: str + """An optional free-form text field to describe an image.""" + + distribution: Literal[ + "Arch Linux", + "CentOS", + "CoreOS", + "Debian", + "Fedora", + "Fedora Atomic", + "FreeBSD", + "Gentoo", + "openSUSE", + "RancherOS", + "Rocky Linux", + "Ubuntu", + "Unknown", + ] + """The name of a custom image's distribution. + + Currently, the valid values are `Arch Linux`, `CentOS`, `CoreOS`, `Debian`, + `Fedora`, `Fedora Atomic`, `FreeBSD`, `Gentoo`, `openSUSE`, `RancherOS`, + `Rocky Linux`, `Ubuntu`, and `Unknown`. Any other value will be accepted but + ignored, and `Unknown` will be used in its place. + """ + + name: str + """The display name that has been given to an image. + + This is what is shown in the control panel and is generally a descriptive title + for the image in question. + """ + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names may be for either existing or new tags. + + Requires `tag:create` scope. + """ + + url: str + """A URL from which the custom Linux virtual machine image may be retrieved. + + The image it points to must be in the raw, qcow2, vhdx, vdi, or vmdk format. It + may be compressed using gzip or bzip2 and must be smaller than 100 GB after + being decompressed. + """ diff --git a/src/gradient/types/gpu_droplets/image_create_response.py b/src/gradient/types/gpu_droplets/image_create_response.py new file mode 100644 index 00000000..87ebbb01 --- /dev/null +++ b/src/gradient/types/gpu_droplets/image_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..shared.image import Image + +__all__ = ["ImageCreateResponse"] + + +class ImageCreateResponse(BaseModel): + image: Optional[Image] = None diff --git a/src/gradient/types/gpu_droplets/image_list_params.py b/src/gradient/types/gpu_droplets/image_list_params.py new file mode 100644 index 00000000..d8e90efa --- /dev/null +++ b/src/gradient/types/gpu_droplets/image_list_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ImageListParams"] + + +class ImageListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + private: bool + """Used to filter only user images.""" + + tag_name: str + """Used to filter images by a specific tag.""" + + type: Literal["application", "distribution"] + """ + Filters results based on image type which can be either `application` or + `distribution`. + """ diff --git a/src/gradient/types/gpu_droplets/image_list_response.py b/src/gradient/types/gpu_droplets/image_list_response.py new file mode 100644 index 00000000..d4bb5697 --- /dev/null +++ b/src/gradient/types/gpu_droplets/image_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.image import Image +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["ImageListResponse"] + + +class ImageListResponse(BaseModel): + images: List[Image] + + meta: MetaProperties + """Information about the response itself.""" + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/image_retrieve_response.py b/src/gradient/types/gpu_droplets/image_retrieve_response.py new file mode 100644 index 00000000..394dd384 --- /dev/null +++ b/src/gradient/types/gpu_droplets/image_retrieve_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel +from ..shared.image import Image + +__all__ = ["ImageRetrieveResponse"] + + +class ImageRetrieveResponse(BaseModel): + image: Image diff --git a/src/gradient/types/gpu_droplets/image_update_params.py b/src/gradient/types/gpu_droplets/image_update_params.py new file mode 100644 index 00000000..2ff851f8 --- /dev/null +++ b/src/gradient/types/gpu_droplets/image_update_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ImageUpdateParams"] + + +class ImageUpdateParams(TypedDict, total=False): + description: str + """An optional free-form text field to describe an image.""" + + distribution: Literal[ + "Arch Linux", + "CentOS", + "CoreOS", + "Debian", + "Fedora", + "Fedora Atomic", + "FreeBSD", + "Gentoo", + "openSUSE", + "RancherOS", + "Rocky Linux", + "Ubuntu", + "Unknown", + ] + """The name of a custom image's distribution. + + Currently, the valid values are `Arch Linux`, `CentOS`, `CoreOS`, `Debian`, + `Fedora`, `Fedora Atomic`, `FreeBSD`, `Gentoo`, `openSUSE`, `RancherOS`, + `Rocky Linux`, `Ubuntu`, and `Unknown`. Any other value will be accepted but + ignored, and `Unknown` will be used in its place. + """ + + name: str + """The display name that has been given to an image. + + This is what is shown in the control panel and is generally a descriptive title + for the image in question. + """ diff --git a/src/gradient/types/gpu_droplets/image_update_response.py b/src/gradient/types/gpu_droplets/image_update_response.py new file mode 100644 index 00000000..3d07f5ac --- /dev/null +++ b/src/gradient/types/gpu_droplets/image_update_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel +from ..shared.image import Image + +__all__ = ["ImageUpdateResponse"] + + +class ImageUpdateResponse(BaseModel): + image: Image diff --git a/src/gradient/types/gpu_droplets/images/__init__.py b/src/gradient/types/gpu_droplets/images/__init__.py new file mode 100644 index 00000000..7e78954c --- /dev/null +++ b/src/gradient/types/gpu_droplets/images/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .action_create_params import ActionCreateParams as ActionCreateParams +from .action_list_response import ActionListResponse as ActionListResponse diff --git a/src/gradient/types/gpu_droplets/images/action_create_params.py b/src/gradient/types/gpu_droplets/images/action_create_params.py new file mode 100644 index 00000000..a1b57d47 --- /dev/null +++ b/src/gradient/types/gpu_droplets/images/action_create_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ActionCreateParams", "ImageActionBase", "ImageActionTransfer"] + + +class ImageActionBase(TypedDict, total=False): + type: Required[Literal["convert", "transfer"]] + """The action to be taken on the image. Can be either `convert` or `transfer`.""" + + +class ImageActionTransfer(TypedDict, total=False): + region: Required[ + Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + type: Required[Literal["convert", "transfer"]] + """The action to be taken on the image. Can be either `convert` or `transfer`.""" + + +ActionCreateParams: TypeAlias = Union[ImageActionBase, ImageActionTransfer] diff --git a/src/gradient/types/gpu_droplets/images/action_list_response.py b/src/gradient/types/gpu_droplets/images/action_list_response.py new file mode 100644 index 00000000..2f4edac5 --- /dev/null +++ b/src/gradient/types/gpu_droplets/images/action_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel +from ...shared.action import Action +from ...shared.page_links import PageLinks +from ...shared.meta_properties import MetaProperties + +__all__ = ["ActionListResponse"] + + +class ActionListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + actions: Optional[List[Action]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/lb_firewall.py b/src/gradient/types/gpu_droplets/lb_firewall.py new file mode 100644 index 00000000..aea1887c --- /dev/null +++ b/src/gradient/types/gpu_droplets/lb_firewall.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["LbFirewall"] + + +class LbFirewall(BaseModel): + allow: Optional[List[str]] = None + """ + the rules for allowing traffic to the load balancer (in the form 'ip:1.2.3.4' or + 'cidr:1.2.0.0/16') + """ + + deny: Optional[List[str]] = None + """ + the rules for denying traffic to the load balancer (in the form 'ip:1.2.3.4' or + 'cidr:1.2.0.0/16') + """ diff --git a/src/gradient/types/gpu_droplets/lb_firewall_param.py b/src/gradient/types/gpu_droplets/lb_firewall_param.py new file mode 100644 index 00000000..7d54a048 --- /dev/null +++ b/src/gradient/types/gpu_droplets/lb_firewall_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["LbFirewallParam"] + + +class LbFirewallParam(TypedDict, total=False): + allow: SequenceNotStr[str] + """ + the rules for allowing traffic to the load balancer (in the form 'ip:1.2.3.4' or + 'cidr:1.2.0.0/16') + """ + + deny: SequenceNotStr[str] + """ + the rules for denying traffic to the load balancer (in the form 'ip:1.2.3.4' or + 'cidr:1.2.0.0/16') + """ diff --git a/src/gradient/types/gpu_droplets/load_balancer.py b/src/gradient/types/gpu_droplets/load_balancer.py new file mode 100644 index 00000000..d0e7597a --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancer.py @@ -0,0 +1,185 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .domains import Domains +from ..._models import BaseModel +from .lb_firewall import LbFirewall +from .glb_settings import GlbSettings +from .health_check import HealthCheck +from ..shared.region import Region +from .forwarding_rule import ForwardingRule +from .sticky_sessions import StickySessions + +__all__ = ["LoadBalancer"] + + +class LoadBalancer(BaseModel): + forwarding_rules: List[ForwardingRule] + """An array of objects specifying the forwarding rules for a load balancer.""" + + id: Optional[str] = None + """A unique ID that can be used to identify and reference a load balancer.""" + + algorithm: Optional[Literal["round_robin", "least_connections"]] = None + """This field has been deprecated. + + You can no longer specify an algorithm for load balancers. + """ + + created_at: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format that represents when + the load balancer was created. + """ + + disable_lets_encrypt_dns_records: Optional[bool] = None + """ + A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + """ + + domains: Optional[List[Domains]] = None + """ + An array of objects specifying the domain configurations for a Global load + balancer. + """ + + droplet_ids: Optional[List[int]] = None + """An array containing the IDs of the Droplets assigned to the load balancer.""" + + enable_backend_keepalive: Optional[bool] = None + """ + A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + """ + + enable_proxy_protocol: Optional[bool] = None + """A boolean value indicating whether PROXY Protocol is in use.""" + + firewall: Optional[LbFirewall] = None + """ + An object specifying allow and deny rules to control traffic to the load + balancer. + """ + + glb_settings: Optional[GlbSettings] = None + """An object specifying forwarding configurations for a Global load balancer.""" + + health_check: Optional[HealthCheck] = None + """An object specifying health check settings for the load balancer.""" + + http_idle_timeout_seconds: Optional[int] = None + """ + An integer value which configures the idle timeout for HTTP requests to the + target droplets. + """ + + ip: Optional[str] = None + """An attribute containing the public-facing IP address of the load balancer.""" + + ipv6: Optional[str] = None + """An attribute containing the public-facing IPv6 address of the load balancer.""" + + name: Optional[str] = None + """A human-readable name for a load balancer instance.""" + + network: Optional[Literal["EXTERNAL", "INTERNAL"]] = None + """A string indicating whether the load balancer should be external or internal. + + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + """ + + network_stack: Optional[Literal["IPV4", "DUALSTACK"]] = None + """ + A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + """ + + project_id: Optional[str] = None + """The ID of the project that the load balancer is associated with. + + If no ID is provided at creation, the load balancer associates with the user's + default project. If an invalid project ID is provided, the load balancer will + not be created. + """ + + redirect_http_to_https: Optional[bool] = None + """ + A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + """ + + region: Optional[Region] = None + """The region where the load balancer instance is located. + + When setting a region, the value should be the slug identifier for the region. + When you query a load balancer, an entire region object will be returned. + """ + + size: Optional[Literal["lb-small", "lb-medium", "lb-large"]] = None + """ + This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + """ + + size_unit: Optional[int] = None + """How many nodes the load balancer contains. + + Each additional node increases the load balancer's ability to manage more + connections. Load balancers can be scaled up or down, and you can change the + number of nodes after creation up to once per hour. This field is currently not + available in the AMS2, NYC2, or SFO1 regions. Use the `size` field to scale load + balancers that reside in these regions. + """ + + status: Optional[Literal["new", "active", "errored"]] = None + """A status string indicating the current state of the load balancer. + + This can be `new`, `active`, or `errored`. + """ + + sticky_sessions: Optional[StickySessions] = None + """An object specifying sticky sessions settings for the load balancer.""" + + tag: Optional[str] = None + """ + The name of a Droplet tag corresponding to Droplets assigned to the load + balancer. + """ + + target_load_balancer_ids: Optional[List[str]] = None + """ + An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + """ + + tls_cipher_policy: Optional[Literal["DEFAULT", "STRONG"]] = None + """ + A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + """ + + type: Optional[Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"]] = None + """ + A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + """ + + vpc_uuid: Optional[str] = None + """A string specifying the UUID of the VPC to which the load balancer is assigned.""" diff --git a/src/gradient/types/gpu_droplets/load_balancer_create_params.py b/src/gradient/types/gpu_droplets/load_balancer_create_params.py new file mode 100644 index 00000000..06472c78 --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancer_create_params.py @@ -0,0 +1,336 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .domains_param import DomainsParam +from .lb_firewall_param import LbFirewallParam +from .glb_settings_param import GlbSettingsParam +from .health_check_param import HealthCheckParam +from .forwarding_rule_param import ForwardingRuleParam +from .sticky_sessions_param import StickySessionsParam + +__all__ = ["LoadBalancerCreateParams", "AssignDropletsByID", "AssignDropletsByTag"] + + +class AssignDropletsByID(TypedDict, total=False): + forwarding_rules: Required[Iterable[ForwardingRuleParam]] + """An array of objects specifying the forwarding rules for a load balancer.""" + + algorithm: Literal["round_robin", "least_connections"] + """This field has been deprecated. + + You can no longer specify an algorithm for load balancers. + """ + + disable_lets_encrypt_dns_records: bool + """ + A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + """ + + domains: Iterable[DomainsParam] + """ + An array of objects specifying the domain configurations for a Global load + balancer. + """ + + droplet_ids: Iterable[int] + """An array containing the IDs of the Droplets assigned to the load balancer.""" + + enable_backend_keepalive: bool + """ + A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + """ + + enable_proxy_protocol: bool + """A boolean value indicating whether PROXY Protocol is in use.""" + + firewall: LbFirewallParam + """ + An object specifying allow and deny rules to control traffic to the load + balancer. + """ + + glb_settings: GlbSettingsParam + """An object specifying forwarding configurations for a Global load balancer.""" + + health_check: HealthCheckParam + """An object specifying health check settings for the load balancer.""" + + http_idle_timeout_seconds: int + """ + An integer value which configures the idle timeout for HTTP requests to the + target droplets. + """ + + name: str + """A human-readable name for a load balancer instance.""" + + network: Literal["EXTERNAL", "INTERNAL"] + """A string indicating whether the load balancer should be external or internal. + + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + """ + + network_stack: Literal["IPV4", "DUALSTACK"] + """ + A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + """ + + project_id: str + """The ID of the project that the load balancer is associated with. + + If no ID is provided at creation, the load balancer associates with the user's + default project. If an invalid project ID is provided, the load balancer will + not be created. + """ + + redirect_http_to_https: bool + """ + A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + """ + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + size: Literal["lb-small", "lb-medium", "lb-large"] + """ + This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + """ + + size_unit: int + """How many nodes the load balancer contains. + + Each additional node increases the load balancer's ability to manage more + connections. Load balancers can be scaled up or down, and you can change the + number of nodes after creation up to once per hour. This field is currently not + available in the AMS2, NYC2, or SFO1 regions. Use the `size` field to scale load + balancers that reside in these regions. + """ + + sticky_sessions: StickySessionsParam + """An object specifying sticky sessions settings for the load balancer.""" + + target_load_balancer_ids: SequenceNotStr[str] + """ + An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + """ + + tls_cipher_policy: Literal["DEFAULT", "STRONG"] + """ + A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + """ + + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] + """ + A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + """ + + vpc_uuid: str + """A string specifying the UUID of the VPC to which the load balancer is assigned.""" + + +class AssignDropletsByTag(TypedDict, total=False): + forwarding_rules: Required[Iterable[ForwardingRuleParam]] + """An array of objects specifying the forwarding rules for a load balancer.""" + + algorithm: Literal["round_robin", "least_connections"] + """This field has been deprecated. + + You can no longer specify an algorithm for load balancers. + """ + + disable_lets_encrypt_dns_records: bool + """ + A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + """ + + domains: Iterable[DomainsParam] + """ + An array of objects specifying the domain configurations for a Global load + balancer. + """ + + enable_backend_keepalive: bool + """ + A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + """ + + enable_proxy_protocol: bool + """A boolean value indicating whether PROXY Protocol is in use.""" + + firewall: LbFirewallParam + """ + An object specifying allow and deny rules to control traffic to the load + balancer. + """ + + glb_settings: GlbSettingsParam + """An object specifying forwarding configurations for a Global load balancer.""" + + health_check: HealthCheckParam + """An object specifying health check settings for the load balancer.""" + + http_idle_timeout_seconds: int + """ + An integer value which configures the idle timeout for HTTP requests to the + target droplets. + """ + + name: str + """A human-readable name for a load balancer instance.""" + + network: Literal["EXTERNAL", "INTERNAL"] + """A string indicating whether the load balancer should be external or internal. + + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + """ + + network_stack: Literal["IPV4", "DUALSTACK"] + """ + A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + """ + + project_id: str + """The ID of the project that the load balancer is associated with. + + If no ID is provided at creation, the load balancer associates with the user's + default project. If an invalid project ID is provided, the load balancer will + not be created. + """ + + redirect_http_to_https: bool + """ + A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + """ + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + size: Literal["lb-small", "lb-medium", "lb-large"] + """ + This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + """ + + size_unit: int + """How many nodes the load balancer contains. + + Each additional node increases the load balancer's ability to manage more + connections. Load balancers can be scaled up or down, and you can change the + number of nodes after creation up to once per hour. This field is currently not + available in the AMS2, NYC2, or SFO1 regions. Use the `size` field to scale load + balancers that reside in these regions. + """ + + sticky_sessions: StickySessionsParam + """An object specifying sticky sessions settings for the load balancer.""" + + tag: str + """ + The name of a Droplet tag corresponding to Droplets assigned to the load + balancer. + """ + + target_load_balancer_ids: SequenceNotStr[str] + """ + An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + """ + + tls_cipher_policy: Literal["DEFAULT", "STRONG"] + """ + A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + """ + + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] + """ + A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + """ + + vpc_uuid: str + """A string specifying the UUID of the VPC to which the load balancer is assigned.""" + + +LoadBalancerCreateParams: TypeAlias = Union[AssignDropletsByID, AssignDropletsByTag] diff --git a/src/gradient/types/gpu_droplets/load_balancer_create_response.py b/src/gradient/types/gpu_droplets/load_balancer_create_response.py new file mode 100644 index 00000000..ed4f2211 --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancer_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .load_balancer import LoadBalancer + +__all__ = ["LoadBalancerCreateResponse"] + + +class LoadBalancerCreateResponse(BaseModel): + load_balancer: Optional[LoadBalancer] = None diff --git a/src/gradient/types/gpu_droplets/load_balancer_list_params.py b/src/gradient/types/gpu_droplets/load_balancer_list_params.py new file mode 100644 index 00000000..d0daff3f --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancer_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["LoadBalancerListParams"] + + +class LoadBalancerListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/load_balancer_list_response.py b/src/gradient/types/gpu_droplets/load_balancer_list_response.py new file mode 100644 index 00000000..d5d0b4ac --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancer_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .load_balancer import LoadBalancer +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["LoadBalancerListResponse"] + + +class LoadBalancerListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + links: Optional[PageLinks] = None + + load_balancers: Optional[List[LoadBalancer]] = None diff --git a/src/gradient/types/gpu_droplets/load_balancer_retrieve_response.py b/src/gradient/types/gpu_droplets/load_balancer_retrieve_response.py new file mode 100644 index 00000000..779e9693 --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancer_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .load_balancer import LoadBalancer + +__all__ = ["LoadBalancerRetrieveResponse"] + + +class LoadBalancerRetrieveResponse(BaseModel): + load_balancer: Optional[LoadBalancer] = None diff --git a/src/gradient/types/gpu_droplets/load_balancer_update_params.py b/src/gradient/types/gpu_droplets/load_balancer_update_params.py new file mode 100644 index 00000000..01c2bda5 --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancer_update_params.py @@ -0,0 +1,336 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .domains_param import DomainsParam +from .lb_firewall_param import LbFirewallParam +from .glb_settings_param import GlbSettingsParam +from .health_check_param import HealthCheckParam +from .forwarding_rule_param import ForwardingRuleParam +from .sticky_sessions_param import StickySessionsParam + +__all__ = ["LoadBalancerUpdateParams", "AssignDropletsByID", "AssignDropletsByTag"] + + +class AssignDropletsByID(TypedDict, total=False): + forwarding_rules: Required[Iterable[ForwardingRuleParam]] + """An array of objects specifying the forwarding rules for a load balancer.""" + + algorithm: Literal["round_robin", "least_connections"] + """This field has been deprecated. + + You can no longer specify an algorithm for load balancers. + """ + + disable_lets_encrypt_dns_records: bool + """ + A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + """ + + domains: Iterable[DomainsParam] + """ + An array of objects specifying the domain configurations for a Global load + balancer. + """ + + droplet_ids: Iterable[int] + """An array containing the IDs of the Droplets assigned to the load balancer.""" + + enable_backend_keepalive: bool + """ + A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + """ + + enable_proxy_protocol: bool + """A boolean value indicating whether PROXY Protocol is in use.""" + + firewall: LbFirewallParam + """ + An object specifying allow and deny rules to control traffic to the load + balancer. + """ + + glb_settings: GlbSettingsParam + """An object specifying forwarding configurations for a Global load balancer.""" + + health_check: HealthCheckParam + """An object specifying health check settings for the load balancer.""" + + http_idle_timeout_seconds: int + """ + An integer value which configures the idle timeout for HTTP requests to the + target droplets. + """ + + name: str + """A human-readable name for a load balancer instance.""" + + network: Literal["EXTERNAL", "INTERNAL"] + """A string indicating whether the load balancer should be external or internal. + + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + """ + + network_stack: Literal["IPV4", "DUALSTACK"] + """ + A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + """ + + project_id: str + """The ID of the project that the load balancer is associated with. + + If no ID is provided at creation, the load balancer associates with the user's + default project. If an invalid project ID is provided, the load balancer will + not be created. + """ + + redirect_http_to_https: bool + """ + A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + """ + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + size: Literal["lb-small", "lb-medium", "lb-large"] + """ + This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + """ + + size_unit: int + """How many nodes the load balancer contains. + + Each additional node increases the load balancer's ability to manage more + connections. Load balancers can be scaled up or down, and you can change the + number of nodes after creation up to once per hour. This field is currently not + available in the AMS2, NYC2, or SFO1 regions. Use the `size` field to scale load + balancers that reside in these regions. + """ + + sticky_sessions: StickySessionsParam + """An object specifying sticky sessions settings for the load balancer.""" + + target_load_balancer_ids: SequenceNotStr[str] + """ + An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + """ + + tls_cipher_policy: Literal["DEFAULT", "STRONG"] + """ + A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + """ + + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] + """ + A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + """ + + vpc_uuid: str + """A string specifying the UUID of the VPC to which the load balancer is assigned.""" + + +class AssignDropletsByTag(TypedDict, total=False): + forwarding_rules: Required[Iterable[ForwardingRuleParam]] + """An array of objects specifying the forwarding rules for a load balancer.""" + + algorithm: Literal["round_robin", "least_connections"] + """This field has been deprecated. + + You can no longer specify an algorithm for load balancers. + """ + + disable_lets_encrypt_dns_records: bool + """ + A boolean value indicating whether to disable automatic DNS record creation for + Let's Encrypt certificates that are added to the load balancer. + """ + + domains: Iterable[DomainsParam] + """ + An array of objects specifying the domain configurations for a Global load + balancer. + """ + + enable_backend_keepalive: bool + """ + A boolean value indicating whether HTTP keepalive connections are maintained to + target Droplets. + """ + + enable_proxy_protocol: bool + """A boolean value indicating whether PROXY Protocol is in use.""" + + firewall: LbFirewallParam + """ + An object specifying allow and deny rules to control traffic to the load + balancer. + """ + + glb_settings: GlbSettingsParam + """An object specifying forwarding configurations for a Global load balancer.""" + + health_check: HealthCheckParam + """An object specifying health check settings for the load balancer.""" + + http_idle_timeout_seconds: int + """ + An integer value which configures the idle timeout for HTTP requests to the + target droplets. + """ + + name: str + """A human-readable name for a load balancer instance.""" + + network: Literal["EXTERNAL", "INTERNAL"] + """A string indicating whether the load balancer should be external or internal. + + Internal load balancers have no public IPs and are only accessible to resources + on the same VPC network. This property cannot be updated after creating the load + balancer. + """ + + network_stack: Literal["IPV4", "DUALSTACK"] + """ + A string indicating whether the load balancer will support IPv4 or both IPv4 and + IPv6 networking. This property cannot be updated after creating the load + balancer. + """ + + project_id: str + """The ID of the project that the load balancer is associated with. + + If no ID is provided at creation, the load balancer associates with the user's + default project. If an invalid project ID is provided, the load balancer will + not be created. + """ + + redirect_http_to_https: bool + """ + A boolean value indicating whether HTTP requests to the load balancer on port 80 + will be redirected to HTTPS on port 443. + """ + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + size: Literal["lb-small", "lb-medium", "lb-large"] + """ + This field has been replaced by the `size_unit` field for all regions except in + AMS2, NYC2, and SFO1. Each available load balancer size now equates to the load + balancer having a set number of nodes. + + - `lb-small` = 1 node + - `lb-medium` = 3 nodes + - `lb-large` = 6 nodes + + You can resize load balancers after creation up to once per hour. You cannot + resize a load balancer within the first hour of its creation. + """ + + size_unit: int + """How many nodes the load balancer contains. + + Each additional node increases the load balancer's ability to manage more + connections. Load balancers can be scaled up or down, and you can change the + number of nodes after creation up to once per hour. This field is currently not + available in the AMS2, NYC2, or SFO1 regions. Use the `size` field to scale load + balancers that reside in these regions. + """ + + sticky_sessions: StickySessionsParam + """An object specifying sticky sessions settings for the load balancer.""" + + tag: str + """ + The name of a Droplet tag corresponding to Droplets assigned to the load + balancer. + """ + + target_load_balancer_ids: SequenceNotStr[str] + """ + An array containing the UUIDs of the Regional load balancers to be used as + target backends for a Global load balancer. + """ + + tls_cipher_policy: Literal["DEFAULT", "STRONG"] + """ + A string indicating the policy for the TLS cipher suites used by the load + balancer. The possible values are `DEFAULT` or `STRONG`. The default value is + `DEFAULT`. + """ + + type: Literal["REGIONAL", "REGIONAL_NETWORK", "GLOBAL"] + """ + A string indicating whether the load balancer should be a standard regional HTTP + load balancer, a regional network load balancer that routes traffic at the + TCP/UDP transport layer, or a global load balancer. + """ + + vpc_uuid: str + """A string specifying the UUID of the VPC to which the load balancer is assigned.""" + + +LoadBalancerUpdateParams: TypeAlias = Union[AssignDropletsByID, AssignDropletsByTag] diff --git a/src/gradient/types/gpu_droplets/load_balancer_update_response.py b/src/gradient/types/gpu_droplets/load_balancer_update_response.py new file mode 100644 index 00000000..2b24b376 --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancer_update_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .load_balancer import LoadBalancer + +__all__ = ["LoadBalancerUpdateResponse"] + + +class LoadBalancerUpdateResponse(BaseModel): + load_balancer: Optional[LoadBalancer] = None diff --git a/src/gradient/types/gpu_droplets/load_balancers/__init__.py b/src/gradient/types/gpu_droplets/load_balancers/__init__.py new file mode 100644 index 00000000..806a71be --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancers/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .droplet_add_params import DropletAddParams as DropletAddParams +from .droplet_remove_params import DropletRemoveParams as DropletRemoveParams +from .forwarding_rule_add_params import ForwardingRuleAddParams as ForwardingRuleAddParams +from .forwarding_rule_remove_params import ForwardingRuleRemoveParams as ForwardingRuleRemoveParams diff --git a/src/gradient/types/gpu_droplets/load_balancers/droplet_add_params.py b/src/gradient/types/gpu_droplets/load_balancers/droplet_add_params.py new file mode 100644 index 00000000..ee403f5f --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancers/droplet_add_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["DropletAddParams"] + + +class DropletAddParams(TypedDict, total=False): + droplet_ids: Required[Iterable[int]] + """An array containing the IDs of the Droplets assigned to the load balancer.""" diff --git a/src/gradient/types/gpu_droplets/load_balancers/droplet_remove_params.py b/src/gradient/types/gpu_droplets/load_balancers/droplet_remove_params.py new file mode 100644 index 00000000..d48795e9 --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancers/droplet_remove_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["DropletRemoveParams"] + + +class DropletRemoveParams(TypedDict, total=False): + droplet_ids: Required[Iterable[int]] + """An array containing the IDs of the Droplets assigned to the load balancer.""" diff --git a/src/gradient/types/gpu_droplets/load_balancers/forwarding_rule_add_params.py b/src/gradient/types/gpu_droplets/load_balancers/forwarding_rule_add_params.py new file mode 100644 index 00000000..2cc6a2df --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancers/forwarding_rule_add_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +from ..forwarding_rule_param import ForwardingRuleParam + +__all__ = ["ForwardingRuleAddParams"] + + +class ForwardingRuleAddParams(TypedDict, total=False): + forwarding_rules: Required[Iterable[ForwardingRuleParam]] diff --git a/src/gradient/types/gpu_droplets/load_balancers/forwarding_rule_remove_params.py b/src/gradient/types/gpu_droplets/load_balancers/forwarding_rule_remove_params.py new file mode 100644 index 00000000..e5209543 --- /dev/null +++ b/src/gradient/types/gpu_droplets/load_balancers/forwarding_rule_remove_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +from ..forwarding_rule_param import ForwardingRuleParam + +__all__ = ["ForwardingRuleRemoveParams"] + + +class ForwardingRuleRemoveParams(TypedDict, total=False): + forwarding_rules: Required[Iterable[ForwardingRuleParam]] diff --git a/src/gradient/types/gpu_droplets/size_list_params.py b/src/gradient/types/gpu_droplets/size_list_params.py new file mode 100644 index 00000000..5df85a9c --- /dev/null +++ b/src/gradient/types/gpu_droplets/size_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SizeListParams"] + + +class SizeListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/size_list_response.py b/src/gradient/types/gpu_droplets/size_list_response.py new file mode 100644 index 00000000..c0c600b4 --- /dev/null +++ b/src/gradient/types/gpu_droplets/size_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.size import Size +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["SizeListResponse"] + + +class SizeListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + sizes: List[Size] + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/snapshot_list_params.py b/src/gradient/types/gpu_droplets/snapshot_list_params.py new file mode 100644 index 00000000..6d1b6f5b --- /dev/null +++ b/src/gradient/types/gpu_droplets/snapshot_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SnapshotListParams"] + + +class SnapshotListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + resource_type: Literal["droplet", "volume"] + """Used to filter snapshots by a resource type.""" diff --git a/src/gradient/types/gpu_droplets/snapshot_list_response.py b/src/gradient/types/gpu_droplets/snapshot_list_response.py new file mode 100644 index 00000000..29b6ec3b --- /dev/null +++ b/src/gradient/types/gpu_droplets/snapshot_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.snapshots import Snapshots +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["SnapshotListResponse"] + + +class SnapshotListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + links: Optional[PageLinks] = None + + snapshots: Optional[List[Snapshots]] = None diff --git a/src/gradient/types/gpu_droplets/snapshot_retrieve_response.py b/src/gradient/types/gpu_droplets/snapshot_retrieve_response.py new file mode 100644 index 00000000..38d84c7a --- /dev/null +++ b/src/gradient/types/gpu_droplets/snapshot_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..shared.snapshots import Snapshots + +__all__ = ["SnapshotRetrieveResponse"] + + +class SnapshotRetrieveResponse(BaseModel): + snapshot: Optional[Snapshots] = None diff --git a/src/gradient/types/gpu_droplets/sticky_sessions.py b/src/gradient/types/gpu_droplets/sticky_sessions.py new file mode 100644 index 00000000..78debc07 --- /dev/null +++ b/src/gradient/types/gpu_droplets/sticky_sessions.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["StickySessions"] + + +class StickySessions(BaseModel): + cookie_name: Optional[str] = None + """The name of the cookie sent to the client. + + This attribute is only returned when using `cookies` for the sticky sessions + type. + """ + + cookie_ttl_seconds: Optional[int] = None + """The number of seconds until the cookie set by the load balancer expires. + + This attribute is only returned when using `cookies` for the sticky sessions + type. + """ + + type: Optional[Literal["cookies", "none"]] = None + """ + An attribute indicating how and if requests from a client will be persistently + served by the same backend Droplet. The possible values are `cookies` or `none`. + """ diff --git a/src/gradient/types/gpu_droplets/sticky_sessions_param.py b/src/gradient/types/gpu_droplets/sticky_sessions_param.py new file mode 100644 index 00000000..acea4a4a --- /dev/null +++ b/src/gradient/types/gpu_droplets/sticky_sessions_param.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["StickySessionsParam"] + + +class StickySessionsParam(TypedDict, total=False): + cookie_name: str + """The name of the cookie sent to the client. + + This attribute is only returned when using `cookies` for the sticky sessions + type. + """ + + cookie_ttl_seconds: int + """The number of seconds until the cookie set by the load balancer expires. + + This attribute is only returned when using `cookies` for the sticky sessions + type. + """ + + type: Literal["cookies", "none"] + """ + An attribute indicating how and if requests from a client will be persistently + served by the same backend Droplet. The possible values are `cookies` or `none`. + """ diff --git a/src/gradient/types/gpu_droplets/volume_create_params.py b/src/gradient/types/gpu_droplets/volume_create_params.py new file mode 100644 index 00000000..c58f7f9d --- /dev/null +++ b/src/gradient/types/gpu_droplets/volume_create_params.py @@ -0,0 +1,155 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["VolumeCreateParams", "VolumesExt4", "VolumesXfs"] + + +class VolumesExt4(TypedDict, total=False): + name: Required[str] + """A human-readable name for the block storage volume. + + Must be lowercase and be composed only of numbers, letters and "-", up to a + limit of 64 characters. The name must begin with a letter. + """ + + region: Required[ + Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + size_gigabytes: Required[int] + """The size of the block storage volume in GiB (1024^3). + + This field does not apply when creating a volume from a snapshot. + """ + + description: str + """An optional free-form text field to describe a block storage volume.""" + + filesystem_label: str + """The label applied to the filesystem. + + Labels for ext4 type filesystems may contain 16 characters while labels for xfs + type filesystems are limited to 12 characters. May only be used in conjunction + with filesystem_type. + """ + + filesystem_type: str + """The name of the filesystem type to be used on the volume. + + When provided, the volume will automatically be formatted to the specified + filesystem type. Currently, the available options are `ext4` and `xfs`. + Pre-formatted volumes are automatically mounted when attached to Ubuntu, Debian, + Fedora, Fedora Atomic, and CentOS Droplets created on or after April 26, 2018. + Attaching pre-formatted volumes to other Droplets is not recommended. + """ + + snapshot_id: str + """The unique identifier for the volume snapshot from which to create the volume.""" + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names may be for either existing or new tags. + + Requires `tag:create` scope. + """ + + +class VolumesXfs(TypedDict, total=False): + name: Required[str] + """A human-readable name for the block storage volume. + + Must be lowercase and be composed only of numbers, letters and "-", up to a + limit of 64 characters. The name must begin with a letter. + """ + + region: Required[ + Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + size_gigabytes: Required[int] + """The size of the block storage volume in GiB (1024^3). + + This field does not apply when creating a volume from a snapshot. + """ + + description: str + """An optional free-form text field to describe a block storage volume.""" + + filesystem_label: str + """The label applied to the filesystem. + + Labels for ext4 type filesystems may contain 16 characters while labels for xfs + type filesystems are limited to 12 characters. May only be used in conjunction + with filesystem_type. + """ + + filesystem_type: str + """The name of the filesystem type to be used on the volume. + + When provided, the volume will automatically be formatted to the specified + filesystem type. Currently, the available options are `ext4` and `xfs`. + Pre-formatted volumes are automatically mounted when attached to Ubuntu, Debian, + Fedora, Fedora Atomic, and CentOS Droplets created on or after April 26, 2018. + Attaching pre-formatted volumes to other Droplets is not recommended. + """ + + snapshot_id: str + """The unique identifier for the volume snapshot from which to create the volume.""" + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names may be for either existing or new tags. + + Requires `tag:create` scope. + """ + + +VolumeCreateParams: TypeAlias = Union[VolumesExt4, VolumesXfs] diff --git a/src/gradient/types/gpu_droplets/volume_create_response.py b/src/gradient/types/gpu_droplets/volume_create_response.py new file mode 100644 index 00000000..1bca9965 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volume_create_response.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.region import Region + +__all__ = ["VolumeCreateResponse", "Volume"] + + +class Volume(BaseModel): + id: Optional[str] = None + """The unique identifier for the block storage volume.""" + + created_at: Optional[str] = None + """ + A time value given in ISO8601 combined date and time format that represents when + the block storage volume was created. + """ + + description: Optional[str] = None + """An optional free-form text field to describe a block storage volume.""" + + droplet_ids: Optional[List[int]] = None + """An array containing the IDs of the Droplets the volume is attached to. + + Note that at this time, a volume can only be attached to a single Droplet. + """ + + filesystem_label: Optional[str] = None + """The label currently applied to the filesystem.""" + + filesystem_type: Optional[str] = None + """The type of filesystem currently in-use on the volume.""" + + name: Optional[str] = None + """A human-readable name for the block storage volume. + + Must be lowercase and be composed only of numbers, letters and "-", up to a + limit of 64 characters. The name must begin with a letter. + """ + + region: Optional[Region] = None + """The region that the block storage volume is located in. + + When setting a region, the value should be the slug identifier for the region. + When you query a block storage volume, the entire region object will be + returned. + """ + + size_gigabytes: Optional[int] = None + """The size of the block storage volume in GiB (1024^3). + + This field does not apply when creating a volume from a snapshot. + """ + + tags: Optional[List[str]] = None + """A flat array of tag names as strings applied to the resource. + + Requires `tag:read` scope. + """ + + +class VolumeCreateResponse(BaseModel): + volume: Optional[Volume] = None diff --git a/src/gradient/types/gpu_droplets/volume_delete_by_name_params.py b/src/gradient/types/gpu_droplets/volume_delete_by_name_params.py new file mode 100644 index 00000000..26d173f0 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volume_delete_by_name_params.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VolumeDeleteByNameParams"] + + +class VolumeDeleteByNameParams(TypedDict, total=False): + name: str + """The block storage volume's name.""" + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """The slug identifier for the region where the resource is available.""" diff --git a/src/gradient/types/gpu_droplets/volume_list_params.py b/src/gradient/types/gpu_droplets/volume_list_params.py new file mode 100644 index 00000000..b4549651 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volume_list_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VolumeListParams"] + + +class VolumeListParams(TypedDict, total=False): + name: str + """The block storage volume's name.""" + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """The slug identifier for the region where the resource is available.""" diff --git a/src/gradient/types/gpu_droplets/volume_list_response.py b/src/gradient/types/gpu_droplets/volume_list_response.py new file mode 100644 index 00000000..69ff421a --- /dev/null +++ b/src/gradient/types/gpu_droplets/volume_list_response.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.region import Region +from ..shared.page_links import PageLinks +from ..shared.meta_properties import MetaProperties + +__all__ = ["VolumeListResponse", "Volume"] + + +class Volume(BaseModel): + id: Optional[str] = None + """The unique identifier for the block storage volume.""" + + created_at: Optional[str] = None + """ + A time value given in ISO8601 combined date and time format that represents when + the block storage volume was created. + """ + + description: Optional[str] = None + """An optional free-form text field to describe a block storage volume.""" + + droplet_ids: Optional[List[int]] = None + """An array containing the IDs of the Droplets the volume is attached to. + + Note that at this time, a volume can only be attached to a single Droplet. + """ + + filesystem_label: Optional[str] = None + """The label currently applied to the filesystem.""" + + filesystem_type: Optional[str] = None + """The type of filesystem currently in-use on the volume.""" + + name: Optional[str] = None + """A human-readable name for the block storage volume. + + Must be lowercase and be composed only of numbers, letters and "-", up to a + limit of 64 characters. The name must begin with a letter. + """ + + region: Optional[Region] = None + """The region that the block storage volume is located in. + + When setting a region, the value should be the slug identifier for the region. + When you query a block storage volume, the entire region object will be + returned. + """ + + size_gigabytes: Optional[int] = None + """The size of the block storage volume in GiB (1024^3). + + This field does not apply when creating a volume from a snapshot. + """ + + tags: Optional[List[str]] = None + """A flat array of tag names as strings applied to the resource. + + Requires `tag:read` scope. + """ + + +class VolumeListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + volumes: List[Volume] + """Array of volumes.""" + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/volume_retrieve_response.py b/src/gradient/types/gpu_droplets/volume_retrieve_response.py new file mode 100644 index 00000000..3efe8de7 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volume_retrieve_response.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.region import Region + +__all__ = ["VolumeRetrieveResponse", "Volume"] + + +class Volume(BaseModel): + id: Optional[str] = None + """The unique identifier for the block storage volume.""" + + created_at: Optional[str] = None + """ + A time value given in ISO8601 combined date and time format that represents when + the block storage volume was created. + """ + + description: Optional[str] = None + """An optional free-form text field to describe a block storage volume.""" + + droplet_ids: Optional[List[int]] = None + """An array containing the IDs of the Droplets the volume is attached to. + + Note that at this time, a volume can only be attached to a single Droplet. + """ + + filesystem_label: Optional[str] = None + """The label currently applied to the filesystem.""" + + filesystem_type: Optional[str] = None + """The type of filesystem currently in-use on the volume.""" + + name: Optional[str] = None + """A human-readable name for the block storage volume. + + Must be lowercase and be composed only of numbers, letters and "-", up to a + limit of 64 characters. The name must begin with a letter. + """ + + region: Optional[Region] = None + """The region that the block storage volume is located in. + + When setting a region, the value should be the slug identifier for the region. + When you query a block storage volume, the entire region object will be + returned. + """ + + size_gigabytes: Optional[int] = None + """The size of the block storage volume in GiB (1024^3). + + This field does not apply when creating a volume from a snapshot. + """ + + tags: Optional[List[str]] = None + """A flat array of tag names as strings applied to the resource. + + Requires `tag:read` scope. + """ + + +class VolumeRetrieveResponse(BaseModel): + volume: Optional[Volume] = None diff --git a/src/gradient/types/gpu_droplets/volumes/__init__.py b/src/gradient/types/gpu_droplets/volumes/__init__.py new file mode 100644 index 00000000..68d3d1e9 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/__init__.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .volume_action import VolumeAction as VolumeAction +from .action_list_params import ActionListParams as ActionListParams +from .action_list_response import ActionListResponse as ActionListResponse +from .snapshot_list_params import SnapshotListParams as SnapshotListParams +from .action_retrieve_params import ActionRetrieveParams as ActionRetrieveParams +from .snapshot_create_params import SnapshotCreateParams as SnapshotCreateParams +from .snapshot_list_response import SnapshotListResponse as SnapshotListResponse +from .action_retrieve_response import ActionRetrieveResponse as ActionRetrieveResponse +from .snapshot_create_response import SnapshotCreateResponse as SnapshotCreateResponse +from .snapshot_retrieve_response import SnapshotRetrieveResponse as SnapshotRetrieveResponse +from .action_initiate_by_id_params import ActionInitiateByIDParams as ActionInitiateByIDParams +from .action_initiate_by_id_response import ActionInitiateByIDResponse as ActionInitiateByIDResponse +from .action_initiate_by_name_params import ActionInitiateByNameParams as ActionInitiateByNameParams +from .action_initiate_by_name_response import ActionInitiateByNameResponse as ActionInitiateByNameResponse diff --git a/src/gradient/types/gpu_droplets/volumes/action_initiate_by_id_params.py b/src/gradient/types/gpu_droplets/volumes/action_initiate_by_id_params.py new file mode 100644 index 00000000..bf1869af --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/action_initiate_by_id_params.py @@ -0,0 +1,135 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["ActionInitiateByIDParams", "VolumeActionPostAttach", "VolumeActionPostDetach", "VolumeActionPostResize"] + + +class VolumeActionPostAttach(TypedDict, total=False): + droplet_id: Required[int] + """ + The unique identifier for the Droplet the volume will be attached or detached + from. + """ + + type: Required[Literal["attach", "detach", "resize"]] + """The volume action to initiate.""" + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names may be for either existing or new tags. + + Requires `tag:create` scope. + """ + + +class VolumeActionPostDetach(TypedDict, total=False): + droplet_id: Required[int] + """ + The unique identifier for the Droplet the volume will be attached or detached + from. + """ + + type: Required[Literal["attach", "detach", "resize"]] + """The volume action to initiate.""" + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + +class VolumeActionPostResize(TypedDict, total=False): + size_gigabytes: Required[int] + """The new size of the block storage volume in GiB (1024^3).""" + + type: Required[Literal["attach", "detach", "resize"]] + """The volume action to initiate.""" + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + +ActionInitiateByIDParams: TypeAlias = Union[VolumeActionPostAttach, VolumeActionPostDetach, VolumeActionPostResize] diff --git a/src/gradient/types/gpu_droplets/volumes/action_initiate_by_id_response.py b/src/gradient/types/gpu_droplets/volumes/action_initiate_by_id_response.py new file mode 100644 index 00000000..d8460f22 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/action_initiate_by_id_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from .volume_action import VolumeAction + +__all__ = ["ActionInitiateByIDResponse"] + + +class ActionInitiateByIDResponse(BaseModel): + action: Optional[VolumeAction] = None diff --git a/src/gradient/types/gpu_droplets/volumes/action_initiate_by_name_params.py b/src/gradient/types/gpu_droplets/volumes/action_initiate_by_name_params.py new file mode 100644 index 00000000..f37d6d9a --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/action_initiate_by_name_params.py @@ -0,0 +1,99 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["ActionInitiateByNameParams", "VolumeActionPostAttach", "VolumeActionPostDetach"] + + +class VolumeActionPostAttach(TypedDict, total=False): + droplet_id: Required[int] + """ + The unique identifier for the Droplet the volume will be attached or detached + from. + """ + + type: Required[Literal["attach", "detach", "resize"]] + """The volume action to initiate.""" + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names may be for either existing or new tags. + + Requires `tag:create` scope. + """ + + +class VolumeActionPostDetach(TypedDict, total=False): + droplet_id: Required[int] + """ + The unique identifier for the Droplet the volume will be attached or detached + from. + """ + + type: Required[Literal["attach", "detach", "resize"]] + """The volume action to initiate.""" + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" + + region: Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + """ + The slug identifier for the region where the resource will initially be + available. + """ + + +ActionInitiateByNameParams: TypeAlias = Union[VolumeActionPostAttach, VolumeActionPostDetach] diff --git a/src/gradient/types/gpu_droplets/volumes/action_initiate_by_name_response.py b/src/gradient/types/gpu_droplets/volumes/action_initiate_by_name_response.py new file mode 100644 index 00000000..9a935bdf --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/action_initiate_by_name_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from .volume_action import VolumeAction + +__all__ = ["ActionInitiateByNameResponse"] + + +class ActionInitiateByNameResponse(BaseModel): + action: Optional[VolumeAction] = None diff --git a/src/gradient/types/gpu_droplets/volumes/action_list_params.py b/src/gradient/types/gpu_droplets/volumes/action_list_params.py new file mode 100644 index 00000000..dd873288 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/action_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ActionListParams"] + + +class ActionListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/volumes/action_list_response.py b/src/gradient/types/gpu_droplets/volumes/action_list_response.py new file mode 100644 index 00000000..35964633 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/action_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel +from .volume_action import VolumeAction +from ...shared.page_links import PageLinks +from ...shared.meta_properties import MetaProperties + +__all__ = ["ActionListResponse"] + + +class ActionListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + actions: Optional[List[VolumeAction]] = None + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/gpu_droplets/volumes/action_retrieve_params.py b/src/gradient/types/gpu_droplets/volumes/action_retrieve_params.py new file mode 100644 index 00000000..93ab443f --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/action_retrieve_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ActionRetrieveParams"] + + +class ActionRetrieveParams(TypedDict, total=False): + volume_id: Required[str] + + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/volumes/action_retrieve_response.py b/src/gradient/types/gpu_droplets/volumes/action_retrieve_response.py new file mode 100644 index 00000000..cd47f37e --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/action_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from .volume_action import VolumeAction + +__all__ = ["ActionRetrieveResponse"] + + +class ActionRetrieveResponse(BaseModel): + action: Optional[VolumeAction] = None diff --git a/src/gradient/types/gpu_droplets/volumes/snapshot_create_params.py b/src/gradient/types/gpu_droplets/volumes/snapshot_create_params.py new file mode 100644 index 00000000..890dd302 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/snapshot_create_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["SnapshotCreateParams"] + + +class SnapshotCreateParams(TypedDict, total=False): + name: Required[str] + """A human-readable name for the volume snapshot.""" + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names may be for either existing or new tags. + + Requires `tag:create` scope. + """ diff --git a/src/gradient/types/gpu_droplets/volumes/snapshot_create_response.py b/src/gradient/types/gpu_droplets/volumes/snapshot_create_response.py new file mode 100644 index 00000000..41701795 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/snapshot_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...shared.snapshots import Snapshots + +__all__ = ["SnapshotCreateResponse"] + + +class SnapshotCreateResponse(BaseModel): + snapshot: Optional[Snapshots] = None diff --git a/src/gradient/types/gpu_droplets/volumes/snapshot_list_params.py b/src/gradient/types/gpu_droplets/volumes/snapshot_list_params.py new file mode 100644 index 00000000..65221a79 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/snapshot_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SnapshotListParams"] + + +class SnapshotListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/gpu_droplets/volumes/snapshot_list_response.py b/src/gradient/types/gpu_droplets/volumes/snapshot_list_response.py new file mode 100644 index 00000000..25d91ed2 --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/snapshot_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel +from ...shared.snapshots import Snapshots +from ...shared.page_links import PageLinks +from ...shared.meta_properties import MetaProperties + +__all__ = ["SnapshotListResponse"] + + +class SnapshotListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + links: Optional[PageLinks] = None + + snapshots: Optional[List[Snapshots]] = None diff --git a/src/gradient/types/gpu_droplets/volumes/snapshot_retrieve_response.py b/src/gradient/types/gpu_droplets/volumes/snapshot_retrieve_response.py new file mode 100644 index 00000000..3defa47d --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/snapshot_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...shared.snapshots import Snapshots + +__all__ = ["SnapshotRetrieveResponse"] + + +class SnapshotRetrieveResponse(BaseModel): + snapshot: Optional[Snapshots] = None diff --git a/src/gradient/types/gpu_droplets/volumes/volume_action.py b/src/gradient/types/gpu_droplets/volumes/volume_action.py new file mode 100644 index 00000000..e1c01f6c --- /dev/null +++ b/src/gradient/types/gpu_droplets/volumes/volume_action.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...shared.action import Action + +__all__ = ["VolumeAction"] + + +class VolumeAction(Action): + resource_id: Optional[int] = None # type: ignore + + type: Optional[str] = None # type: ignore + """This is the type of action that the object represents. + + For example, this could be "attach_volume" to represent the state of a volume + attach action. + """ diff --git a/src/gradient/types/image_generate_params.py b/src/gradient/types/image_generate_params.py new file mode 100644 index 00000000..42e6144a --- /dev/null +++ b/src/gradient/types/image_generate_params.py @@ -0,0 +1,100 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ImageGenerateParamsBase", "ImageGenerateParamsNonStreaming", "ImageGenerateParamsStreaming"] + + +class ImageGenerateParamsBase(TypedDict, total=False): + prompt: Required[str] + """A text description of the desired image(s). + + GPT-IMAGE-1 supports up to 32,000 characters and provides automatic prompt + optimization for best results. + """ + + background: Optional[str] + """The background setting for the image generation. + + GPT-IMAGE-1 supports: transparent, opaque, auto. + """ + + model: str + """The model to use for image generation. + + GPT-IMAGE-1 is the latest model offering the best quality with automatic + optimization and enhanced capabilities. + """ + + moderation: Optional[str] + """The moderation setting for the image generation. + + GPT-IMAGE-1 supports: low, auto. + """ + + n: Optional[int] + """The number of images to generate. GPT-IMAGE-1 only supports n=1.""" + + output_compression: Optional[int] + """The output compression for the image generation. GPT-IMAGE-1 supports: 0-100.""" + + output_format: Optional[str] + """The output format for the image generation. + + GPT-IMAGE-1 supports: png, webp, jpeg. + """ + + partial_images: Optional[int] + """The number of partial image chunks to return during streaming generation. + + This parameter is optional with a default of 0. When stream=true, this must be + greater than 0 to receive progressive updates of the image as it's being + generated. Higher values provide more frequent updates but may increase response + overhead. + """ + + quality: Optional[str] + """The quality of the image that will be generated. + + GPT-IMAGE-1 supports: auto (automatically select best quality), high, medium, + low. + """ + + size: Optional[str] + """The size of the generated images. + + GPT-IMAGE-1 supports: auto (automatically select best size), 1536x1024 + (landscape), 1024x1536 (portrait). + """ + + user: Optional[str] + """ + A unique identifier representing your end-user, which can help DigitalOcean to + monitor and detect abuse. + """ + + +class ImageGenerateParamsNonStreaming(ImageGenerateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, partial image data will be streamed as the image is being + generated. When streaming, the response will be sent as server-sent events with + partial image chunks. When stream is true, partial_images must be greater + than 0. + """ + + +class ImageGenerateParamsStreaming(ImageGenerateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, partial image data will be streamed as the image is being + generated. When streaming, the response will be sent as server-sent events with + partial image chunks. When stream is true, partial_images must be greater + than 0. + """ + + +ImageGenerateParams = Union[ImageGenerateParamsNonStreaming, ImageGenerateParamsStreaming] diff --git a/src/gradient/types/image_generate_response.py b/src/gradient/types/image_generate_response.py new file mode 100644 index 00000000..5f97697c --- /dev/null +++ b/src/gradient/types/image_generate_response.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel + +__all__ = ["ImageGenerateResponse", "Data", "Usage", "UsageInputTokensDetails"] + + +class Data(BaseModel): + b64_json: str + """The base64-encoded JSON of the generated image. + + GPT-IMAGE-1 returns images in b64_json format only. + """ + + revised_prompt: Optional[str] = None + """The optimized prompt that was used to generate the image. + + GPT-IMAGE-1 automatically optimizes prompts for best results. + """ + + +class UsageInputTokensDetails(BaseModel): + text_tokens: Optional[int] = None + """Number of text tokens in the input""" + + +class Usage(BaseModel): + input_tokens: int + """Number of tokens in the input prompt""" + + total_tokens: int + """Total number of tokens used (input + output)""" + + input_tokens_details: Optional[UsageInputTokensDetails] = None + """Detailed breakdown of input tokens""" + + output_tokens: Optional[int] = None + """Number of tokens in the generated output""" + + +class ImageGenerateResponse(BaseModel): + created: int + """The Unix timestamp (in seconds) of when the images were created""" + + data: List[Data] + """The list of generated images""" + + background: Optional[str] = None + """The background setting used for the image generation""" + + output_format: Optional[str] = None + """The output format of the generated image""" + + quality: Optional[str] = None + """The quality setting used for the image generation""" + + size: Optional[str] = None + """The size of the generated image""" + + usage: Optional[Usage] = None + """Usage statistics for the image generation request""" diff --git a/src/gradient/types/inference/__init__.py b/src/gradient/types/inference/__init__.py new file mode 100644 index 00000000..c3cbcd6d --- /dev/null +++ b/src/gradient/types/inference/__init__.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .api_key_list_params import APIKeyListParams as APIKeyListParams +from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams +from .api_key_list_response import APIKeyListResponse as APIKeyListResponse +from .api_key_update_params import APIKeyUpdateParams as APIKeyUpdateParams +from .api_model_api_key_info import APIModelAPIKeyInfo as APIModelAPIKeyInfo +from .api_key_create_response import APIKeyCreateResponse as APIKeyCreateResponse +from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse +from .api_key_update_response import APIKeyUpdateResponse as APIKeyUpdateResponse +from .api_key_update_regenerate_response import APIKeyUpdateRegenerateResponse as APIKeyUpdateRegenerateResponse diff --git a/src/gradient/types/inference/api_key_create_params.py b/src/gradient/types/inference/api_key_create_params.py new file mode 100644 index 00000000..10edfbbe --- /dev/null +++ b/src/gradient/types/inference/api_key_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["APIKeyCreateParams"] + + +class APIKeyCreateParams(TypedDict, total=False): + name: str + """A human friendly name to identify the key""" diff --git a/src/gradient/types/inference/api_key_create_response.py b/src/gradient/types/inference/api_key_create_response.py new file mode 100644 index 00000000..f2469e43 --- /dev/null +++ b/src/gradient/types/inference/api_key_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_model_api_key_info import APIModelAPIKeyInfo + +__all__ = ["APIKeyCreateResponse"] + + +class APIKeyCreateResponse(BaseModel): + api_key_info: Optional[APIModelAPIKeyInfo] = None + """Model API Key Info""" diff --git a/src/gradient/types/inference/api_key_delete_response.py b/src/gradient/types/inference/api_key_delete_response.py new file mode 100644 index 00000000..89102258 --- /dev/null +++ b/src/gradient/types/inference/api_key_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_model_api_key_info import APIModelAPIKeyInfo + +__all__ = ["APIKeyDeleteResponse"] + + +class APIKeyDeleteResponse(BaseModel): + api_key_info: Optional[APIModelAPIKeyInfo] = None + """Model API Key Info""" diff --git a/src/gradient/types/inference/api_key_list_params.py b/src/gradient/types/inference/api_key_list_params.py new file mode 100644 index 00000000..1f8f96b7 --- /dev/null +++ b/src/gradient/types/inference/api_key_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["APIKeyListParams"] + + +class APIKeyListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/inference/api_key_list_response.py b/src/gradient/types/inference/api_key_list_response.py new file mode 100644 index 00000000..7c474873 --- /dev/null +++ b/src/gradient/types/inference/api_key_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.api_meta import APIMeta +from ..shared.api_links import APILinks +from .api_model_api_key_info import APIModelAPIKeyInfo + +__all__ = ["APIKeyListResponse"] + + +class APIKeyListResponse(BaseModel): + api_key_infos: Optional[List[APIModelAPIKeyInfo]] = None + """Api key infos""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/inference/api_key_update_params.py b/src/gradient/types/inference/api_key_update_params.py new file mode 100644 index 00000000..7f79240a --- /dev/null +++ b/src/gradient/types/inference/api_key_update_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["APIKeyUpdateParams"] + + +class APIKeyUpdateParams(TypedDict, total=False): + body_api_key_uuid: Annotated[str, PropertyInfo(alias="api_key_uuid")] + """API key ID""" + + name: str + """Name""" diff --git a/src/gradient/types/inference/api_key_update_regenerate_response.py b/src/gradient/types/inference/api_key_update_regenerate_response.py new file mode 100644 index 00000000..c7ce5f0a --- /dev/null +++ b/src/gradient/types/inference/api_key_update_regenerate_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_model_api_key_info import APIModelAPIKeyInfo + +__all__ = ["APIKeyUpdateRegenerateResponse"] + + +class APIKeyUpdateRegenerateResponse(BaseModel): + api_key_info: Optional[APIModelAPIKeyInfo] = None + """Model API Key Info""" diff --git a/src/gradient/types/inference/api_key_update_response.py b/src/gradient/types/inference/api_key_update_response.py new file mode 100644 index 00000000..1b7f92ef --- /dev/null +++ b/src/gradient/types/inference/api_key_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_model_api_key_info import APIModelAPIKeyInfo + +__all__ = ["APIKeyUpdateResponse"] + + +class APIKeyUpdateResponse(BaseModel): + api_key_info: Optional[APIModelAPIKeyInfo] = None + """Model API Key Info""" diff --git a/src/gradient/types/inference/api_model_api_key_info.py b/src/gradient/types/inference/api_model_api_key_info.py new file mode 100644 index 00000000..3da1c70a --- /dev/null +++ b/src/gradient/types/inference/api_model_api_key_info.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["APIModelAPIKeyInfo"] + + +class APIModelAPIKeyInfo(BaseModel): + created_at: Optional[datetime] = None + """Creation date""" + + created_by: Optional[str] = None + """Created by""" + + deleted_at: Optional[datetime] = None + """Deleted date""" + + name: Optional[str] = None + """Name""" + + secret_key: Optional[str] = None + + uuid: Optional[str] = None + """Uuid""" diff --git a/src/gradient/types/knowledge_base_create_params.py b/src/gradient/types/knowledge_base_create_params.py new file mode 100644 index 00000000..4dc42098 --- /dev/null +++ b/src/gradient/types/knowledge_base_create_params.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import TypedDict + +from .._types import SequenceNotStr +from .knowledge_bases.aws_data_source_param import AwsDataSourceParam +from .knowledge_bases.api_spaces_data_source_param import APISpacesDataSourceParam +from .knowledge_bases.api_file_upload_data_source_param import APIFileUploadDataSourceParam +from .knowledge_bases.api_web_crawler_data_source_param import APIWebCrawlerDataSourceParam + +__all__ = ["KnowledgeBaseCreateParams", "Datasource", "DatasourceDropboxDataSource", "DatasourceGoogleDriveDataSource"] + + +class KnowledgeBaseCreateParams(TypedDict, total=False): + database_id: str + """ + Identifier of the DigitalOcean OpenSearch database this knowledge base will use, + optional. If not provided, we create a new database for the knowledge base in + the same region as the knowledge base. + """ + + datasources: Iterable[Datasource] + """The data sources to use for this knowledge base. + + See + [Organize Data Sources](https://docs.digitalocean.com/products/genai-platform/concepts/best-practices/#spaces-buckets) + for more information on data sources best practices. + """ + + embedding_model_uuid: str + """ + Identifier for the + [embedding model](https://docs.digitalocean.com/products/genai-platform/details/models/#embedding-models). + """ + + name: str + """Name of the knowledge base.""" + + project_id: str + """Identifier of the DigitalOcean project this knowledge base will belong to.""" + + region: str + """The datacenter region to deploy the knowledge base in.""" + + tags: SequenceNotStr[str] + """Tags to organize your knowledge base.""" + + vpc_uuid: str + """The VPC to deploy the knowledge base database in""" + + +class DatasourceDropboxDataSource(TypedDict, total=False): + folder: str + + refresh_token: str + """Refresh token. + + you can obrain a refresh token by following the oauth2 flow. see + /v2/gen-ai/oauth2/dropbox/tokens for reference. + """ + + +class DatasourceGoogleDriveDataSource(TypedDict, total=False): + folder_id: str + + refresh_token: str + """Refresh token. + + you can obrain a refresh token by following the oauth2 flow. see + /v2/gen-ai/oauth2/google/tokens for reference. + """ + + +class Datasource(TypedDict, total=False): + aws_data_source: AwsDataSourceParam + """AWS S3 Data Source""" + + bucket_name: str + """Deprecated, moved to data_source_details""" + + bucket_region: str + """Deprecated, moved to data_source_details""" + + dropbox_data_source: DatasourceDropboxDataSource + """Dropbox Data Source""" + + file_upload_data_source: APIFileUploadDataSourceParam + """File to upload as data source for knowledge base.""" + + google_drive_data_source: DatasourceGoogleDriveDataSource + """Google Drive Data Source""" + + item_path: str + + spaces_data_source: APISpacesDataSourceParam + """Spaces Bucket Data Source""" + + web_crawler_data_source: APIWebCrawlerDataSourceParam + """WebCrawlerDataSource""" diff --git a/src/gradient/types/knowledge_base_create_response.py b/src/gradient/types/knowledge_base_create_response.py new file mode 100644 index 00000000..6d846fa5 --- /dev/null +++ b/src/gradient/types/knowledge_base_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .api_knowledge_base import APIKnowledgeBase + +__all__ = ["KnowledgeBaseCreateResponse"] + + +class KnowledgeBaseCreateResponse(BaseModel): + knowledge_base: Optional[APIKnowledgeBase] = None + """Knowledgebase Description""" diff --git a/src/gradient/types/knowledge_base_delete_response.py b/src/gradient/types/knowledge_base_delete_response.py new file mode 100644 index 00000000..b0605a20 --- /dev/null +++ b/src/gradient/types/knowledge_base_delete_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["KnowledgeBaseDeleteResponse"] + + +class KnowledgeBaseDeleteResponse(BaseModel): + uuid: Optional[str] = None + """The id of the deleted knowledge base""" diff --git a/src/gradient/types/knowledge_base_list_indexing_jobs_response.py b/src/gradient/types/knowledge_base_list_indexing_jobs_response.py new file mode 100644 index 00000000..d88f83fc --- /dev/null +++ b/src/gradient/types/knowledge_base_list_indexing_jobs_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .shared.api_meta import APIMeta +from .shared.api_links import APILinks +from .knowledge_bases.api_indexing_job import APIIndexingJob + +__all__ = ["KnowledgeBaseListIndexingJobsResponse"] + + +class KnowledgeBaseListIndexingJobsResponse(BaseModel): + jobs: Optional[List[APIIndexingJob]] = None + """The indexing jobs""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/knowledge_base_list_params.py b/src/gradient/types/knowledge_base_list_params.py new file mode 100644 index 00000000..b2c0eb31 --- /dev/null +++ b/src/gradient/types/knowledge_base_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["KnowledgeBaseListParams"] + + +class KnowledgeBaseListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/knowledge_base_list_response.py b/src/gradient/types/knowledge_base_list_response.py new file mode 100644 index 00000000..08227316 --- /dev/null +++ b/src/gradient/types/knowledge_base_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .shared.api_meta import APIMeta +from .shared.api_links import APILinks +from .api_knowledge_base import APIKnowledgeBase + +__all__ = ["KnowledgeBaseListResponse"] + + +class KnowledgeBaseListResponse(BaseModel): + knowledge_bases: Optional[List[APIKnowledgeBase]] = None + """The knowledge bases""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/knowledge_base_retrieve_response.py b/src/gradient/types/knowledge_base_retrieve_response.py new file mode 100644 index 00000000..55994f70 --- /dev/null +++ b/src/gradient/types/knowledge_base_retrieve_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .api_knowledge_base import APIKnowledgeBase + +__all__ = ["KnowledgeBaseRetrieveResponse"] + + +class KnowledgeBaseRetrieveResponse(BaseModel): + database_status: Optional[ + Literal[ + "CREATING", + "ONLINE", + "POWEROFF", + "REBUILDING", + "REBALANCING", + "DECOMMISSIONED", + "FORKING", + "MIGRATING", + "RESIZING", + "RESTORING", + "POWERING_ON", + "UNHEALTHY", + ] + ] = None + + knowledge_base: Optional[APIKnowledgeBase] = None + """Knowledgebase Description""" diff --git a/src/gradient/types/knowledge_base_update_params.py b/src/gradient/types/knowledge_base_update_params.py new file mode 100644 index 00000000..cfb52016 --- /dev/null +++ b/src/gradient/types/knowledge_base_update_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["KnowledgeBaseUpdateParams"] + + +class KnowledgeBaseUpdateParams(TypedDict, total=False): + database_id: str + """The id of the DigitalOcean database this knowledge base will use, optiona.""" + + embedding_model_uuid: str + """Identifier for the foundation model.""" + + name: str + """Knowledge base name""" + + project_id: str + """The id of the DigitalOcean project this knowledge base will belong to""" + + tags: SequenceNotStr[str] + """Tags to organize your knowledge base.""" + + body_uuid: Annotated[str, PropertyInfo(alias="uuid")] + """Knowledge base id""" diff --git a/src/gradient/types/knowledge_base_update_response.py b/src/gradient/types/knowledge_base_update_response.py new file mode 100644 index 00000000..0840622c --- /dev/null +++ b/src/gradient/types/knowledge_base_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .api_knowledge_base import APIKnowledgeBase + +__all__ = ["KnowledgeBaseUpdateResponse"] + + +class KnowledgeBaseUpdateResponse(BaseModel): + knowledge_base: Optional[APIKnowledgeBase] = None + """Knowledgebase Description""" diff --git a/src/gradient/types/knowledge_bases/__init__.py b/src/gradient/types/knowledge_bases/__init__.py new file mode 100644 index 00000000..a8ce2cc7 --- /dev/null +++ b/src/gradient/types/knowledge_bases/__init__.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .api_indexing_job import APIIndexingJob as APIIndexingJob +from .aws_data_source_param import AwsDataSourceParam as AwsDataSourceParam +from .api_spaces_data_source import APISpacesDataSource as APISpacesDataSource +from .api_indexed_data_source import APIIndexedDataSource as APIIndexedDataSource +from .data_source_list_params import DataSourceListParams as DataSourceListParams +from .indexing_job_list_params import IndexingJobListParams as IndexingJobListParams +from .data_source_create_params import DataSourceCreateParams as DataSourceCreateParams +from .data_source_list_response import DataSourceListResponse as DataSourceListResponse +from .indexing_job_create_params import IndexingJobCreateParams as IndexingJobCreateParams +from .indexing_job_list_response import IndexingJobListResponse as IndexingJobListResponse +from .api_file_upload_data_source import APIFileUploadDataSource as APIFileUploadDataSource +from .api_web_crawler_data_source import APIWebCrawlerDataSource as APIWebCrawlerDataSource +from .data_source_create_response import DataSourceCreateResponse as DataSourceCreateResponse +from .data_source_delete_response import DataSourceDeleteResponse as DataSourceDeleteResponse +from .api_spaces_data_source_param import APISpacesDataSourceParam as APISpacesDataSourceParam +from .indexing_job_create_response import IndexingJobCreateResponse as IndexingJobCreateResponse +from .api_knowledge_base_data_source import APIKnowledgeBaseDataSource as APIKnowledgeBaseDataSource +from .indexing_job_retrieve_response import IndexingJobRetrieveResponse as IndexingJobRetrieveResponse +from .api_file_upload_data_source_param import APIFileUploadDataSourceParam as APIFileUploadDataSourceParam +from .api_web_crawler_data_source_param import APIWebCrawlerDataSourceParam as APIWebCrawlerDataSourceParam +from .indexing_job_update_cancel_params import IndexingJobUpdateCancelParams as IndexingJobUpdateCancelParams +from .indexing_job_update_cancel_response import IndexingJobUpdateCancelResponse as IndexingJobUpdateCancelResponse +from .data_source_create_presigned_urls_params import ( + DataSourceCreatePresignedURLsParams as DataSourceCreatePresignedURLsParams, +) +from .indexing_job_retrieve_signed_url_response import ( + IndexingJobRetrieveSignedURLResponse as IndexingJobRetrieveSignedURLResponse, +) +from .data_source_create_presigned_urls_response import ( + DataSourceCreatePresignedURLsResponse as DataSourceCreatePresignedURLsResponse, +) +from .indexing_job_retrieve_data_sources_response import ( + IndexingJobRetrieveDataSourcesResponse as IndexingJobRetrieveDataSourcesResponse, +) diff --git a/src/gradient/types/knowledge_bases/api_file_upload_data_source.py b/src/gradient/types/knowledge_bases/api_file_upload_data_source.py new file mode 100644 index 00000000..a1c23e09 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_file_upload_data_source.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["APIFileUploadDataSource"] + + +class APIFileUploadDataSource(BaseModel): + original_file_name: Optional[str] = None + """The original file name""" + + size_in_bytes: Optional[str] = None + """The size of the file in bytes""" + + stored_object_key: Optional[str] = None + """The object key the file was stored as""" diff --git a/src/gradient/types/knowledge_bases/api_file_upload_data_source_param.py b/src/gradient/types/knowledge_bases/api_file_upload_data_source_param.py new file mode 100644 index 00000000..562f8a34 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_file_upload_data_source_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["APIFileUploadDataSourceParam"] + + +class APIFileUploadDataSourceParam(TypedDict, total=False): + original_file_name: str + """The original file name""" + + size_in_bytes: str + """The size of the file in bytes""" + + stored_object_key: str + """The object key the file was stored as""" diff --git a/src/gradient/types/knowledge_bases/api_indexed_data_source.py b/src/gradient/types/knowledge_bases/api_indexed_data_source.py new file mode 100644 index 00000000..3f011582 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_indexed_data_source.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["APIIndexedDataSource"] + + +class APIIndexedDataSource(BaseModel): + completed_at: Optional[datetime] = None + """Timestamp when data source completed indexing""" + + data_source_uuid: Optional[str] = None + """Uuid of the indexed data source""" + + error_details: Optional[str] = None + """A detailed error description""" + + error_msg: Optional[str] = None + """A string code provinding a hint which part of the system experienced an error""" + + failed_item_count: Optional[str] = None + """Total count of files that have failed""" + + indexed_file_count: Optional[str] = None + """Total count of files that have been indexed""" + + indexed_item_count: Optional[str] = None + """Total count of files that have been indexed""" + + removed_item_count: Optional[str] = None + """Total count of files that have been removed""" + + skipped_item_count: Optional[str] = None + """Total count of files that have been skipped""" + + started_at: Optional[datetime] = None + """Timestamp when data source started indexing""" + + status: Optional[ + Literal[ + "DATA_SOURCE_STATUS_UNKNOWN", + "DATA_SOURCE_STATUS_IN_PROGRESS", + "DATA_SOURCE_STATUS_UPDATED", + "DATA_SOURCE_STATUS_PARTIALLY_UPDATED", + "DATA_SOURCE_STATUS_NOT_UPDATED", + "DATA_SOURCE_STATUS_FAILED", + "DATA_SOURCE_STATUS_CANCELLED", + ] + ] = None + + total_bytes: Optional[str] = None + """Total size of files in data source in bytes""" + + total_bytes_indexed: Optional[str] = None + """Total size of files in data source in bytes that have been indexed""" + + total_file_count: Optional[str] = None + """Total file count in the data source""" diff --git a/src/gradient/types/knowledge_bases/api_indexing_job.py b/src/gradient/types/knowledge_bases/api_indexing_job.py new file mode 100644 index 00000000..93124cf8 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_indexing_job.py @@ -0,0 +1,84 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel +from .api_indexed_data_source import APIIndexedDataSource + +__all__ = ["APIIndexingJob"] + + +class APIIndexingJob(BaseModel): + completed_datasources: Optional[int] = None + """Number of datasources indexed completed""" + + created_at: Optional[datetime] = None + """Creation date / time""" + + data_source_jobs: Optional[List[APIIndexedDataSource]] = None + """Details on Data Sources included in the Indexing Job""" + + data_source_uuids: Optional[List[str]] = None + + finished_at: Optional[datetime] = None + + is_report_available: Optional[bool] = None + """Boolean value to determine if the indexing job details are available""" + + knowledge_base_uuid: Optional[str] = None + """Knowledge base id""" + + phase: Optional[ + Literal[ + "BATCH_JOB_PHASE_UNKNOWN", + "BATCH_JOB_PHASE_PENDING", + "BATCH_JOB_PHASE_RUNNING", + "BATCH_JOB_PHASE_SUCCEEDED", + "BATCH_JOB_PHASE_FAILED", + "BATCH_JOB_PHASE_ERROR", + "BATCH_JOB_PHASE_CANCELLED", + ] + ] = None + + started_at: Optional[datetime] = None + + status: Optional[ + Literal[ + "INDEX_JOB_STATUS_UNKNOWN", + "INDEX_JOB_STATUS_PARTIAL", + "INDEX_JOB_STATUS_IN_PROGRESS", + "INDEX_JOB_STATUS_COMPLETED", + "INDEX_JOB_STATUS_FAILED", + "INDEX_JOB_STATUS_NO_CHANGES", + "INDEX_JOB_STATUS_PENDING", + ] + ] = None + + tokens: Optional[int] = None + """Number of tokens [This field is deprecated]""" + + total_datasources: Optional[int] = None + """Number of datasources being indexed""" + + total_items_failed: Optional[str] = None + """Total Items Failed""" + + total_items_indexed: Optional[str] = None + """Total Items Indexed""" + + total_items_removed: Optional[str] = None + """Total Items Removed""" + + total_items_skipped: Optional[str] = None + """Total Items Skipped""" + + total_tokens: Optional[str] = None + """Total Tokens Consumed By the Indexing Job""" + + updated_at: Optional[datetime] = None + """Last modified""" + + uuid: Optional[str] = None + """Unique id""" diff --git a/src/gradient/types/knowledge_bases/api_knowledge_base_data_source.py b/src/gradient/types/knowledge_bases/api_knowledge_base_data_source.py new file mode 100644 index 00000000..223797c7 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_knowledge_base_data_source.py @@ -0,0 +1,77 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from ..._models import BaseModel +from .api_indexing_job import APIIndexingJob +from .api_spaces_data_source import APISpacesDataSource +from .api_indexed_data_source import APIIndexedDataSource +from .api_file_upload_data_source import APIFileUploadDataSource +from .api_web_crawler_data_source import APIWebCrawlerDataSource + +__all__ = ["APIKnowledgeBaseDataSource", "AwsDataSource", "DropboxDataSource", "GoogleDriveDataSource"] + + +class AwsDataSource(BaseModel): + bucket_name: Optional[str] = None + """Spaces bucket name""" + + item_path: Optional[str] = None + + region: Optional[str] = None + """Region of bucket""" + + +class DropboxDataSource(BaseModel): + folder: Optional[str] = None + + +class GoogleDriveDataSource(BaseModel): + folder_id: Optional[str] = None + + folder_name: Optional[str] = None + """Name of the selected folder if available""" + + +class APIKnowledgeBaseDataSource(BaseModel): + aws_data_source: Optional[AwsDataSource] = None + """AWS S3 Data Source for Display""" + + bucket_name: Optional[str] = None + """Name of storage bucket - Deprecated, moved to data_source_details""" + + created_at: Optional[datetime] = None + """Creation date / time""" + + dropbox_data_source: Optional[DropboxDataSource] = None + """Dropbox Data Source for Display""" + + file_upload_data_source: Optional[APIFileUploadDataSource] = None + """File to upload as data source for knowledge base.""" + + google_drive_data_source: Optional[GoogleDriveDataSource] = None + """Google Drive Data Source for Display""" + + item_path: Optional[str] = None + """Path of folder or object in bucket - Deprecated, moved to data_source_details""" + + last_datasource_indexing_job: Optional[APIIndexedDataSource] = None + + last_indexing_job: Optional[APIIndexingJob] = None + """IndexingJob description""" + + region: Optional[str] = None + """Region code - Deprecated, moved to data_source_details""" + + spaces_data_source: Optional[APISpacesDataSource] = None + """Spaces Bucket Data Source""" + + updated_at: Optional[datetime] = None + """Last modified""" + + uuid: Optional[str] = None + """Unique id of knowledge base""" + + web_crawler_data_source: Optional[APIWebCrawlerDataSource] = None + """WebCrawlerDataSource""" diff --git a/src/gradient/types/knowledge_bases/api_spaces_data_source.py b/src/gradient/types/knowledge_bases/api_spaces_data_source.py new file mode 100644 index 00000000..02aa479a --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_spaces_data_source.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["APISpacesDataSource"] + + +class APISpacesDataSource(BaseModel): + bucket_name: Optional[str] = None + """Spaces bucket name""" + + item_path: Optional[str] = None + + region: Optional[str] = None + """Region of bucket""" diff --git a/src/gradient/types/knowledge_bases/api_spaces_data_source_param.py b/src/gradient/types/knowledge_bases/api_spaces_data_source_param.py new file mode 100644 index 00000000..5eaeb0ad --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_spaces_data_source_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["APISpacesDataSourceParam"] + + +class APISpacesDataSourceParam(TypedDict, total=False): + bucket_name: str + """Spaces bucket name""" + + item_path: str + + region: str + """Region of bucket""" diff --git a/src/gradient/types/knowledge_bases/api_web_crawler_data_source.py b/src/gradient/types/knowledge_bases/api_web_crawler_data_source.py new file mode 100644 index 00000000..63c9111a --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_web_crawler_data_source.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["APIWebCrawlerDataSource"] + + +class APIWebCrawlerDataSource(BaseModel): + base_url: Optional[str] = None + """The base url to crawl.""" + + crawling_option: Optional[Literal["UNKNOWN", "SCOPED", "PATH", "DOMAIN", "SUBDOMAINS"]] = None + """Options for specifying how URLs found on pages should be handled. + + - UNKNOWN: Default unknown value + - SCOPED: Only include the base URL. + - PATH: Crawl the base URL and linked pages within the URL path. + - DOMAIN: Crawl the base URL and linked pages within the same domain. + - SUBDOMAINS: Crawl the base URL and linked pages for any subdomain. + """ + + embed_media: Optional[bool] = None + """Whether to ingest and index media (images, etc.) on web pages.""" + + exclude_tags: Optional[List[str]] = None + """Declaring which tags to exclude in web pages while webcrawling""" diff --git a/src/gradient/types/knowledge_bases/api_web_crawler_data_source_param.py b/src/gradient/types/knowledge_bases/api_web_crawler_data_source_param.py new file mode 100644 index 00000000..17988e73 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_web_crawler_data_source_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["APIWebCrawlerDataSourceParam"] + + +class APIWebCrawlerDataSourceParam(TypedDict, total=False): + base_url: str + """The base url to crawl.""" + + crawling_option: Literal["UNKNOWN", "SCOPED", "PATH", "DOMAIN", "SUBDOMAINS"] + """Options for specifying how URLs found on pages should be handled. + + - UNKNOWN: Default unknown value + - SCOPED: Only include the base URL. + - PATH: Crawl the base URL and linked pages within the URL path. + - DOMAIN: Crawl the base URL and linked pages within the same domain. + - SUBDOMAINS: Crawl the base URL and linked pages for any subdomain. + """ + + embed_media: bool + """Whether to ingest and index media (images, etc.) on web pages.""" + + exclude_tags: SequenceNotStr[str] + """Declaring which tags to exclude in web pages while webcrawling""" diff --git a/src/gradient/types/knowledge_bases/aws_data_source_param.py b/src/gradient/types/knowledge_bases/aws_data_source_param.py new file mode 100644 index 00000000..912e3e29 --- /dev/null +++ b/src/gradient/types/knowledge_bases/aws_data_source_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AwsDataSourceParam"] + + +class AwsDataSourceParam(TypedDict, total=False): + bucket_name: str + """Spaces bucket name""" + + item_path: str + + key_id: str + """The AWS Key ID""" + + region: str + """Region of bucket""" + + secret_key: str + """The AWS Secret Key""" diff --git a/src/gradient/types/knowledge_bases/data_source_create_params.py b/src/gradient/types/knowledge_bases/data_source_create_params.py new file mode 100644 index 00000000..ac3aa93c --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_create_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo +from .aws_data_source_param import AwsDataSourceParam +from .api_spaces_data_source_param import APISpacesDataSourceParam +from .api_web_crawler_data_source_param import APIWebCrawlerDataSourceParam + +__all__ = ["DataSourceCreateParams"] + + +class DataSourceCreateParams(TypedDict, total=False): + aws_data_source: AwsDataSourceParam + """AWS S3 Data Source""" + + body_knowledge_base_uuid: Annotated[str, PropertyInfo(alias="knowledge_base_uuid")] + """Knowledge base id""" + + spaces_data_source: APISpacesDataSourceParam + """Spaces Bucket Data Source""" + + web_crawler_data_source: APIWebCrawlerDataSourceParam + """WebCrawlerDataSource""" diff --git a/src/gradient/types/knowledge_bases/data_source_create_presigned_urls_params.py b/src/gradient/types/knowledge_bases/data_source_create_presigned_urls_params.py new file mode 100644 index 00000000..253cbce7 --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_create_presigned_urls_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import TypedDict + +__all__ = ["DataSourceCreatePresignedURLsParams", "File"] + + +class DataSourceCreatePresignedURLsParams(TypedDict, total=False): + files: Iterable[File] + """A list of files to generate presigned URLs for.""" + + +class File(TypedDict, total=False): + file_name: str + """Local filename""" + + file_size: str + """The size of the file in bytes.""" diff --git a/src/gradient/types/knowledge_bases/data_source_create_presigned_urls_response.py b/src/gradient/types/knowledge_bases/data_source_create_presigned_urls_response.py new file mode 100644 index 00000000..c3d172d7 --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_create_presigned_urls_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["DataSourceCreatePresignedURLsResponse", "Upload"] + + +class Upload(BaseModel): + expires_at: Optional[datetime] = None + """The time the url expires at.""" + + object_key: Optional[str] = None + """The unique object key to store the file as.""" + + original_file_name: Optional[str] = None + """The original file name.""" + + presigned_url: Optional[str] = None + """The actual presigned URL the client can use to upload the file directly.""" + + +class DataSourceCreatePresignedURLsResponse(BaseModel): + request_id: Optional[str] = None + """The ID generated for the request for Presigned URLs.""" + + uploads: Optional[List[Upload]] = None + """A list of generated presigned URLs and object keys, one per file.""" diff --git a/src/gradient/types/knowledge_bases/data_source_create_response.py b/src/gradient/types/knowledge_bases/data_source_create_response.py new file mode 100644 index 00000000..76ec88e2 --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_knowledge_base_data_source import APIKnowledgeBaseDataSource + +__all__ = ["DataSourceCreateResponse"] + + +class DataSourceCreateResponse(BaseModel): + knowledge_base_data_source: Optional[APIKnowledgeBaseDataSource] = None + """Data Source configuration for Knowledge Bases""" diff --git a/src/gradient/types/knowledge_bases/data_source_delete_response.py b/src/gradient/types/knowledge_bases/data_source_delete_response.py new file mode 100644 index 00000000..eaad72ff --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["DataSourceDeleteResponse"] + + +class DataSourceDeleteResponse(BaseModel): + data_source_uuid: Optional[str] = None + """Data source id""" + + knowledge_base_uuid: Optional[str] = None + """Knowledge base id""" diff --git a/src/gradient/types/knowledge_bases/data_source_list_params.py b/src/gradient/types/knowledge_bases/data_source_list_params.py new file mode 100644 index 00000000..089eb291 --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["DataSourceListParams"] + + +class DataSourceListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/knowledge_bases/data_source_list_response.py b/src/gradient/types/knowledge_bases/data_source_list_response.py new file mode 100644 index 00000000..f05a49bc --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.api_meta import APIMeta +from ..shared.api_links import APILinks +from .api_knowledge_base_data_source import APIKnowledgeBaseDataSource + +__all__ = ["DataSourceListResponse"] + + +class DataSourceListResponse(BaseModel): + knowledge_base_data_sources: Optional[List[APIKnowledgeBaseDataSource]] = None + """The data sources""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/knowledge_bases/indexing_job_create_params.py b/src/gradient/types/knowledge_bases/indexing_job_create_params.py new file mode 100644 index 00000000..ebd8632b --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_create_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["IndexingJobCreateParams"] + + +class IndexingJobCreateParams(TypedDict, total=False): + data_source_uuids: SequenceNotStr[str] + """ + List of data source ids to index, if none are provided, all data sources will be + indexed + """ + + knowledge_base_uuid: str + """Knowledge base id""" diff --git a/src/gradient/types/knowledge_bases/indexing_job_create_response.py b/src/gradient/types/knowledge_bases/indexing_job_create_response.py new file mode 100644 index 00000000..685f40ef --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_indexing_job import APIIndexingJob + +__all__ = ["IndexingJobCreateResponse"] + + +class IndexingJobCreateResponse(BaseModel): + job: Optional[APIIndexingJob] = None + """IndexingJob description""" diff --git a/src/gradient/types/knowledge_bases/indexing_job_list_params.py b/src/gradient/types/knowledge_bases/indexing_job_list_params.py new file mode 100644 index 00000000..c9ac560e --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["IndexingJobListParams"] + + +class IndexingJobListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/knowledge_bases/indexing_job_list_response.py b/src/gradient/types/knowledge_bases/indexing_job_list_response.py new file mode 100644 index 00000000..371f51bb --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from ..shared.api_meta import APIMeta +from .api_indexing_job import APIIndexingJob +from ..shared.api_links import APILinks + +__all__ = ["IndexingJobListResponse"] + + +class IndexingJobListResponse(BaseModel): + jobs: Optional[List[APIIndexingJob]] = None + """The indexing jobs""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/knowledge_bases/indexing_job_retrieve_data_sources_response.py b/src/gradient/types/knowledge_bases/indexing_job_retrieve_data_sources_response.py new file mode 100644 index 00000000..dd0e317e --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_retrieve_data_sources_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .api_indexed_data_source import APIIndexedDataSource + +__all__ = ["IndexingJobRetrieveDataSourcesResponse"] + + +class IndexingJobRetrieveDataSourcesResponse(BaseModel): + indexed_data_sources: Optional[List[APIIndexedDataSource]] = None diff --git a/src/gradient/types/knowledge_bases/indexing_job_retrieve_response.py b/src/gradient/types/knowledge_bases/indexing_job_retrieve_response.py new file mode 100644 index 00000000..2d6be855 --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_retrieve_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_indexing_job import APIIndexingJob + +__all__ = ["IndexingJobRetrieveResponse"] + + +class IndexingJobRetrieveResponse(BaseModel): + job: Optional[APIIndexingJob] = None + """IndexingJob description""" diff --git a/src/gradient/types/knowledge_bases/indexing_job_retrieve_signed_url_response.py b/src/gradient/types/knowledge_bases/indexing_job_retrieve_signed_url_response.py new file mode 100644 index 00000000..2ef60e45 --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_retrieve_signed_url_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["IndexingJobRetrieveSignedURLResponse"] + + +class IndexingJobRetrieveSignedURLResponse(BaseModel): + signed_url: Optional[str] = None + """The signed url for downloading the indexing job details""" diff --git a/src/gradient/types/knowledge_bases/indexing_job_update_cancel_params.py b/src/gradient/types/knowledge_bases/indexing_job_update_cancel_params.py new file mode 100644 index 00000000..9359a42a --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_update_cancel_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["IndexingJobUpdateCancelParams"] + + +class IndexingJobUpdateCancelParams(TypedDict, total=False): + body_uuid: Annotated[str, PropertyInfo(alias="uuid")] + """A unique identifier for an indexing job.""" diff --git a/src/gradient/types/knowledge_bases/indexing_job_update_cancel_response.py b/src/gradient/types/knowledge_bases/indexing_job_update_cancel_response.py new file mode 100644 index 00000000..9fd41764 --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_update_cancel_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .api_indexing_job import APIIndexingJob + +__all__ = ["IndexingJobUpdateCancelResponse"] + + +class IndexingJobUpdateCancelResponse(BaseModel): + job: Optional[APIIndexingJob] = None + """IndexingJob description""" diff --git a/src/gradient/types/model_list_params.py b/src/gradient/types/model_list_params.py new file mode 100644 index 00000000..a2fa066a --- /dev/null +++ b/src/gradient/types/model_list_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = ["ModelListParams"] + + +class ModelListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" + + public_only: bool + """Only include models that are publicly available.""" + + usecases: List[ + Literal[ + "MODEL_USECASE_UNKNOWN", + "MODEL_USECASE_AGENT", + "MODEL_USECASE_FINETUNED", + "MODEL_USECASE_KNOWLEDGEBASE", + "MODEL_USECASE_GUARDRAIL", + "MODEL_USECASE_REASONING", + "MODEL_USECASE_SERVERLESS", + ] + ] + """Include only models defined for the listed usecases. + + - MODEL_USECASE_UNKNOWN: The use case of the model is unknown + - MODEL_USECASE_AGENT: The model maybe used in an agent + - MODEL_USECASE_FINETUNED: The model maybe used for fine tuning + - MODEL_USECASE_KNOWLEDGEBASE: The model maybe used for knowledge bases + (embedding models) + - MODEL_USECASE_GUARDRAIL: The model maybe used for guardrails + - MODEL_USECASE_REASONING: The model usecase for reasoning + - MODEL_USECASE_SERVERLESS: The model usecase for serverless inference + """ diff --git a/src/gradient/types/model_list_response.py b/src/gradient/types/model_list_response.py new file mode 100644 index 00000000..12d95437 --- /dev/null +++ b/src/gradient/types/model_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .api_model import APIModel +from .shared.api_meta import APIMeta +from .shared.api_links import APILinks + +__all__ = ["ModelListResponse"] + + +class ModelListResponse(BaseModel): + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" + + models: Optional[List[APIModel]] = None + """The models""" diff --git a/src/gradient/types/models/__init__.py b/src/gradient/types/models/__init__.py new file mode 100644 index 00000000..f8ee8b14 --- /dev/null +++ b/src/gradient/types/models/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/gradient/types/models/providers/__init__.py b/src/gradient/types/models/providers/__init__.py new file mode 100644 index 00000000..74366e70 --- /dev/null +++ b/src/gradient/types/models/providers/__init__.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .openai_list_params import OpenAIListParams as OpenAIListParams +from .openai_create_params import OpenAICreateParams as OpenAICreateParams +from .openai_list_response import OpenAIListResponse as OpenAIListResponse +from .openai_update_params import OpenAIUpdateParams as OpenAIUpdateParams +from .anthropic_list_params import AnthropicListParams as AnthropicListParams +from .openai_create_response import OpenAICreateResponse as OpenAICreateResponse +from .openai_delete_response import OpenAIDeleteResponse as OpenAIDeleteResponse +from .openai_update_response import OpenAIUpdateResponse as OpenAIUpdateResponse +from .anthropic_create_params import AnthropicCreateParams as AnthropicCreateParams +from .anthropic_list_response import AnthropicListResponse as AnthropicListResponse +from .anthropic_update_params import AnthropicUpdateParams as AnthropicUpdateParams +from .openai_retrieve_response import OpenAIRetrieveResponse as OpenAIRetrieveResponse +from .anthropic_create_response import AnthropicCreateResponse as AnthropicCreateResponse +from .anthropic_delete_response import AnthropicDeleteResponse as AnthropicDeleteResponse +from .anthropic_update_response import AnthropicUpdateResponse as AnthropicUpdateResponse +from .anthropic_retrieve_response import AnthropicRetrieveResponse as AnthropicRetrieveResponse +from .anthropic_list_agents_params import AnthropicListAgentsParams as AnthropicListAgentsParams +from .openai_retrieve_agents_params import OpenAIRetrieveAgentsParams as OpenAIRetrieveAgentsParams +from .anthropic_list_agents_response import AnthropicListAgentsResponse as AnthropicListAgentsResponse +from .openai_retrieve_agents_response import OpenAIRetrieveAgentsResponse as OpenAIRetrieveAgentsResponse diff --git a/src/gradient/types/models/providers/anthropic_create_params.py b/src/gradient/types/models/providers/anthropic_create_params.py new file mode 100644 index 00000000..c9fd6e85 --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AnthropicCreateParams"] + + +class AnthropicCreateParams(TypedDict, total=False): + api_key: str + """Anthropic API key""" + + name: str + """Name of the key""" diff --git a/src/gradient/types/models/providers/anthropic_create_response.py b/src/gradient/types/models/providers/anthropic_create_response.py new file mode 100644 index 00000000..0fbe50bc --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["AnthropicCreateResponse"] + + +class AnthropicCreateResponse(BaseModel): + api_key_info: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" diff --git a/src/gradient/types/models/providers/anthropic_delete_response.py b/src/gradient/types/models/providers/anthropic_delete_response.py new file mode 100644 index 00000000..b4fdd978 --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["AnthropicDeleteResponse"] + + +class AnthropicDeleteResponse(BaseModel): + api_key_info: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" diff --git a/src/gradient/types/models/providers/anthropic_list_agents_params.py b/src/gradient/types/models/providers/anthropic_list_agents_params.py new file mode 100644 index 00000000..b3308b69 --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_list_agents_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AnthropicListAgentsParams"] + + +class AnthropicListAgentsParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/models/providers/anthropic_list_agents_response.py b/src/gradient/types/models/providers/anthropic_list_agents_response.py new file mode 100644 index 00000000..a1525275 --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_list_agents_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +from ...._models import BaseModel +from ...shared.api_meta import APIMeta +from ...shared.api_links import APILinks + +__all__ = ["AnthropicListAgentsResponse"] + + +class AnthropicListAgentsResponse(BaseModel): + agents: Optional[List["APIAgent"]] = None + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" + + +from ...api_agent import APIAgent diff --git a/src/gradient/types/models/providers/anthropic_list_params.py b/src/gradient/types/models/providers/anthropic_list_params.py new file mode 100644 index 00000000..ae1cca58 --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AnthropicListParams"] + + +class AnthropicListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/models/providers/anthropic_list_response.py b/src/gradient/types/models/providers/anthropic_list_response.py new file mode 100644 index 00000000..24d6547a --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel +from ...shared.api_meta import APIMeta +from ...shared.api_links import APILinks +from ...api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["AnthropicListResponse"] + + +class AnthropicListResponse(BaseModel): + api_key_infos: Optional[List[APIAnthropicAPIKeyInfo]] = None + """Api key infos""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/models/providers/anthropic_retrieve_response.py b/src/gradient/types/models/providers/anthropic_retrieve_response.py new file mode 100644 index 00000000..61324b7d --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_retrieve_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["AnthropicRetrieveResponse"] + + +class AnthropicRetrieveResponse(BaseModel): + api_key_info: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" diff --git a/src/gradient/types/models/providers/anthropic_update_params.py b/src/gradient/types/models/providers/anthropic_update_params.py new file mode 100644 index 00000000..865dc29c --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["AnthropicUpdateParams"] + + +class AnthropicUpdateParams(TypedDict, total=False): + api_key: str + """Anthropic API key""" + + body_api_key_uuid: Annotated[str, PropertyInfo(alias="api_key_uuid")] + """API key ID""" + + name: str + """Name of the key""" diff --git a/src/gradient/types/models/providers/anthropic_update_response.py b/src/gradient/types/models/providers/anthropic_update_response.py new file mode 100644 index 00000000..3a6daaea --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["AnthropicUpdateResponse"] + + +class AnthropicUpdateResponse(BaseModel): + api_key_info: Optional[APIAnthropicAPIKeyInfo] = None + """Anthropic API Key Info""" diff --git a/src/gradient/types/models/providers/openai_create_params.py b/src/gradient/types/models/providers/openai_create_params.py new file mode 100644 index 00000000..8ed7f571 --- /dev/null +++ b/src/gradient/types/models/providers/openai_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["OpenAICreateParams"] + + +class OpenAICreateParams(TypedDict, total=False): + api_key: str + """OpenAI API key""" + + name: str + """Name of the key""" diff --git a/src/gradient/types/models/providers/openai_create_response.py b/src/gradient/types/models/providers/openai_create_response.py new file mode 100644 index 00000000..b2e94766 --- /dev/null +++ b/src/gradient/types/models/providers/openai_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["OpenAICreateResponse"] + + +class OpenAICreateResponse(BaseModel): + api_key_info: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" diff --git a/src/gradient/types/models/providers/openai_delete_response.py b/src/gradient/types/models/providers/openai_delete_response.py new file mode 100644 index 00000000..e59c89fe --- /dev/null +++ b/src/gradient/types/models/providers/openai_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["OpenAIDeleteResponse"] + + +class OpenAIDeleteResponse(BaseModel): + api_key_info: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" diff --git a/src/gradient/types/models/providers/openai_list_params.py b/src/gradient/types/models/providers/openai_list_params.py new file mode 100644 index 00000000..5677eeaf --- /dev/null +++ b/src/gradient/types/models/providers/openai_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["OpenAIListParams"] + + +class OpenAIListParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/models/providers/openai_list_response.py b/src/gradient/types/models/providers/openai_list_response.py new file mode 100644 index 00000000..698cd11e --- /dev/null +++ b/src/gradient/types/models/providers/openai_list_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel +from ...shared.api_meta import APIMeta +from ...shared.api_links import APILinks +from ...api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["OpenAIListResponse"] + + +class OpenAIListResponse(BaseModel): + api_key_infos: Optional[List[APIOpenAIAPIKeyInfo]] = None + """Api key infos""" + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" diff --git a/src/gradient/types/models/providers/openai_retrieve_agents_params.py b/src/gradient/types/models/providers/openai_retrieve_agents_params.py new file mode 100644 index 00000000..2db6d7a1 --- /dev/null +++ b/src/gradient/types/models/providers/openai_retrieve_agents_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["OpenAIRetrieveAgentsParams"] + + +class OpenAIRetrieveAgentsParams(TypedDict, total=False): + page: int + """Page number.""" + + per_page: int + """Items per page.""" diff --git a/src/gradient/types/models/providers/openai_retrieve_agents_response.py b/src/gradient/types/models/providers/openai_retrieve_agents_response.py new file mode 100644 index 00000000..717a56cd --- /dev/null +++ b/src/gradient/types/models/providers/openai_retrieve_agents_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +from ...._models import BaseModel +from ...shared.api_meta import APIMeta +from ...shared.api_links import APILinks + +__all__ = ["OpenAIRetrieveAgentsResponse"] + + +class OpenAIRetrieveAgentsResponse(BaseModel): + agents: Optional[List["APIAgent"]] = None + + links: Optional[APILinks] = None + """Links to other pages""" + + meta: Optional[APIMeta] = None + """Meta information about the data set""" + + +from ...api_agent import APIAgent diff --git a/src/gradient/types/models/providers/openai_retrieve_response.py b/src/gradient/types/models/providers/openai_retrieve_response.py new file mode 100644 index 00000000..0f382073 --- /dev/null +++ b/src/gradient/types/models/providers/openai_retrieve_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["OpenAIRetrieveResponse"] + + +class OpenAIRetrieveResponse(BaseModel): + api_key_info: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" diff --git a/src/gradient/types/models/providers/openai_update_params.py b/src/gradient/types/models/providers/openai_update_params.py new file mode 100644 index 00000000..9b99495e --- /dev/null +++ b/src/gradient/types/models/providers/openai_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["OpenAIUpdateParams"] + + +class OpenAIUpdateParams(TypedDict, total=False): + api_key: str + """OpenAI API key""" + + body_api_key_uuid: Annotated[str, PropertyInfo(alias="api_key_uuid")] + """API key ID""" + + name: str + """Name of the key""" diff --git a/src/gradient/types/models/providers/openai_update_response.py b/src/gradient/types/models/providers/openai_update_response.py new file mode 100644 index 00000000..ec7a1c94 --- /dev/null +++ b/src/gradient/types/models/providers/openai_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from ...api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["OpenAIUpdateResponse"] + + +class OpenAIUpdateResponse(BaseModel): + api_key_info: Optional[APIOpenAIAPIKeyInfo] = None + """OpenAI API Key Info""" diff --git a/src/gradient/types/nf_create_params.py b/src/gradient/types/nf_create_params.py new file mode 100644 index 00000000..327beb2e --- /dev/null +++ b/src/gradient/types/nf_create_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["NfCreateParams"] + + +class NfCreateParams(TypedDict, total=False): + name: Required[str] + """The human-readable name of the share.""" + + region: Required[str] + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" + + size_gib: Required[int] + """The desired/provisioned size of the share in GiB (Gibibytes). Must be >= 50.""" + + vpc_ids: Required[SequenceNotStr[str]] + """List of VPC IDs that should be able to access the share.""" diff --git a/src/gradient/types/nf_create_response.py b/src/gradient/types/nf_create_response.py new file mode 100644 index 00000000..5016d776 --- /dev/null +++ b/src/gradient/types/nf_create_response.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["NfCreateResponse", "Share"] + + +class Share(BaseModel): + id: str + """The unique identifier of the NFS share.""" + + created_at: datetime + """Timestamp for when the NFS share was created.""" + + name: str + """The human-readable name of the share.""" + + region: str + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" + + size_gib: int + """The desired/provisioned size of the share in GiB (Gibibytes). Must be >= 50.""" + + status: Literal["CREATING", "ACTIVE", "FAILED", "DELETED"] + """The current status of the share.""" + + host: Optional[str] = None + """The host IP of the NFS server that will be accessible from the associated VPC""" + + mount_path: Optional[str] = None + """ + Path at which the share will be available, to be mounted at a target of the + user's choice within the client + """ + + vpc_ids: Optional[List[str]] = None + """List of VPC IDs that should be able to access the share.""" + + +class NfCreateResponse(BaseModel): + share: Optional[Share] = None diff --git a/src/gradient/types/nf_delete_params.py b/src/gradient/types/nf_delete_params.py new file mode 100644 index 00000000..a11474e5 --- /dev/null +++ b/src/gradient/types/nf_delete_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["NfDeleteParams"] + + +class NfDeleteParams(TypedDict, total=False): + region: Required[str] + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" diff --git a/src/gradient/types/nf_initiate_action_params.py b/src/gradient/types/nf_initiate_action_params.py new file mode 100644 index 00000000..a187f56d --- /dev/null +++ b/src/gradient/types/nf_initiate_action_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "NfInitiateActionParams", + "NfsActionResize", + "NfsActionResizeParams", + "NfsActionSnapshot", + "NfsActionSnapshotParams", +] + + +class NfsActionResize(TypedDict, total=False): + region: Required[str] + """The DigitalOcean region slug (e.g. atl1, nyc2) where the NFS snapshot resides.""" + + type: Required[Literal["resize", "snapshot"]] + """The type of action to initiate for the NFS share (such as resize or snapshot).""" + + params: NfsActionResizeParams + + +class NfsActionResizeParams(TypedDict, total=False): + size_gib: Required[int] + """The new size for the NFS share.""" + + +class NfsActionSnapshot(TypedDict, total=False): + region: Required[str] + """The DigitalOcean region slug (e.g. atl1, nyc2) where the NFS snapshot resides.""" + + type: Required[Literal["resize", "snapshot"]] + """The type of action to initiate for the NFS share (such as resize or snapshot).""" + + params: NfsActionSnapshotParams + + +class NfsActionSnapshotParams(TypedDict, total=False): + name: Required[str] + """Snapshot name of the NFS share""" + + +NfInitiateActionParams: TypeAlias = Union[NfsActionResize, NfsActionSnapshot] diff --git a/src/gradient/types/nf_initiate_action_response.py b/src/gradient/types/nf_initiate_action_response.py new file mode 100644 index 00000000..9f38a4b2 --- /dev/null +++ b/src/gradient/types/nf_initiate_action_response.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["NfInitiateActionResponse", "Action"] + + +class Action(BaseModel): + region_slug: str + """The DigitalOcean region slug where the resource is located.""" + + resource_id: str + """The unique identifier of the resource on which the action is being performed.""" + + resource_type: Literal["network_file_share", "network_file_share_snapshot"] + """The type of resource on which the action is being performed.""" + + started_at: datetime + """The timestamp when the action was started.""" + + status: Literal["in-progress", "completed", "errored"] + """The current status of the action.""" + + type: str + """The type of action being performed.""" + + +class NfInitiateActionResponse(BaseModel): + action: Action + """The action that was submitted.""" diff --git a/src/gradient/types/nf_list_params.py b/src/gradient/types/nf_list_params.py new file mode 100644 index 00000000..bc53c284 --- /dev/null +++ b/src/gradient/types/nf_list_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["NfListParams"] + + +class NfListParams(TypedDict, total=False): + region: Required[str] + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" diff --git a/src/gradient/types/nf_list_response.py b/src/gradient/types/nf_list_response.py new file mode 100644 index 00000000..c5af118b --- /dev/null +++ b/src/gradient/types/nf_list_response.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["NfListResponse", "Share"] + + +class Share(BaseModel): + id: str + """The unique identifier of the NFS share.""" + + created_at: datetime + """Timestamp for when the NFS share was created.""" + + name: str + """The human-readable name of the share.""" + + region: str + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" + + size_gib: int + """The desired/provisioned size of the share in GiB (Gibibytes). Must be >= 50.""" + + status: Literal["CREATING", "ACTIVE", "FAILED", "DELETED"] + """The current status of the share.""" + + host: Optional[str] = None + """The host IP of the NFS server that will be accessible from the associated VPC""" + + mount_path: Optional[str] = None + """ + Path at which the share will be available, to be mounted at a target of the + user's choice within the client + """ + + vpc_ids: Optional[List[str]] = None + """List of VPC IDs that should be able to access the share.""" + + +class NfListResponse(BaseModel): + shares: Optional[List[Share]] = None diff --git a/src/gradient/types/nf_retrieve_params.py b/src/gradient/types/nf_retrieve_params.py new file mode 100644 index 00000000..292053d9 --- /dev/null +++ b/src/gradient/types/nf_retrieve_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["NfRetrieveParams"] + + +class NfRetrieveParams(TypedDict, total=False): + region: Required[str] + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" diff --git a/src/gradient/types/nf_retrieve_response.py b/src/gradient/types/nf_retrieve_response.py new file mode 100644 index 00000000..897f07f0 --- /dev/null +++ b/src/gradient/types/nf_retrieve_response.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["NfRetrieveResponse", "Share"] + + +class Share(BaseModel): + id: str + """The unique identifier of the NFS share.""" + + created_at: datetime + """Timestamp for when the NFS share was created.""" + + name: str + """The human-readable name of the share.""" + + region: str + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" + + size_gib: int + """The desired/provisioned size of the share in GiB (Gibibytes). Must be >= 50.""" + + status: Literal["CREATING", "ACTIVE", "FAILED", "DELETED"] + """The current status of the share.""" + + host: Optional[str] = None + """The host IP of the NFS server that will be accessible from the associated VPC""" + + mount_path: Optional[str] = None + """ + Path at which the share will be available, to be mounted at a target of the + user's choice within the client + """ + + vpc_ids: Optional[List[str]] = None + """List of VPC IDs that should be able to access the share.""" + + +class NfRetrieveResponse(BaseModel): + share: Optional[Share] = None diff --git a/src/gradient/types/nfs/__init__.py b/src/gradient/types/nfs/__init__.py new file mode 100644 index 00000000..41777980 --- /dev/null +++ b/src/gradient/types/nfs/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .snapshot_list_params import SnapshotListParams as SnapshotListParams +from .snapshot_delete_params import SnapshotDeleteParams as SnapshotDeleteParams +from .snapshot_list_response import SnapshotListResponse as SnapshotListResponse +from .snapshot_retrieve_params import SnapshotRetrieveParams as SnapshotRetrieveParams +from .snapshot_retrieve_response import SnapshotRetrieveResponse as SnapshotRetrieveResponse diff --git a/src/gradient/types/nfs/snapshot_delete_params.py b/src/gradient/types/nfs/snapshot_delete_params.py new file mode 100644 index 00000000..1b26149e --- /dev/null +++ b/src/gradient/types/nfs/snapshot_delete_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SnapshotDeleteParams"] + + +class SnapshotDeleteParams(TypedDict, total=False): + region: Required[str] + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" diff --git a/src/gradient/types/nfs/snapshot_list_params.py b/src/gradient/types/nfs/snapshot_list_params.py new file mode 100644 index 00000000..8c4c6946 --- /dev/null +++ b/src/gradient/types/nfs/snapshot_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SnapshotListParams"] + + +class SnapshotListParams(TypedDict, total=False): + region: Required[str] + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" + + share_id: str + """The unique ID of an NFS share. + + If provided, only snapshots of this specific share will be returned. + """ diff --git a/src/gradient/types/nfs/snapshot_list_response.py b/src/gradient/types/nfs/snapshot_list_response.py new file mode 100644 index 00000000..8a6864dc --- /dev/null +++ b/src/gradient/types/nfs/snapshot_list_response.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SnapshotListResponse", "Snapshot"] + + +class Snapshot(BaseModel): + id: str + """The unique identifier of the snapshot.""" + + created_at: datetime + """The timestamp when the snapshot was created.""" + + name: str + """The human-readable name of the snapshot.""" + + region: str + """The DigitalOcean region slug where the snapshot is located.""" + + share_id: str + """The unique identifier of the share from which this snapshot was created.""" + + size_gib: int + """The size of the snapshot in GiB.""" + + status: Literal["UNKNOWN", "CREATING", "ACTIVE", "FAILED", "DELETED"] + """The current status of the snapshot.""" + + +class SnapshotListResponse(BaseModel): + snapshots: Optional[List[Snapshot]] = None diff --git a/src/gradient/types/nfs/snapshot_retrieve_params.py b/src/gradient/types/nfs/snapshot_retrieve_params.py new file mode 100644 index 00000000..d1e1f8e8 --- /dev/null +++ b/src/gradient/types/nfs/snapshot_retrieve_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SnapshotRetrieveParams"] + + +class SnapshotRetrieveParams(TypedDict, total=False): + region: Required[str] + """The DigitalOcean region slug (e.g., nyc2, atl1) where the NFS share resides.""" diff --git a/src/gradient/types/nfs/snapshot_retrieve_response.py b/src/gradient/types/nfs/snapshot_retrieve_response.py new file mode 100644 index 00000000..2d54d523 --- /dev/null +++ b/src/gradient/types/nfs/snapshot_retrieve_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SnapshotRetrieveResponse", "Snapshot"] + + +class Snapshot(BaseModel): + id: str + """The unique identifier of the snapshot.""" + + created_at: datetime + """The timestamp when the snapshot was created.""" + + name: str + """The human-readable name of the snapshot.""" + + region: str + """The DigitalOcean region slug where the snapshot is located.""" + + share_id: str + """The unique identifier of the share from which this snapshot was created.""" + + size_gib: int + """The size of the snapshot in GiB.""" + + status: Literal["UNKNOWN", "CREATING", "ACTIVE", "FAILED", "DELETED"] + """The current status of the snapshot.""" + + +class SnapshotRetrieveResponse(BaseModel): + snapshot: Optional[Snapshot] = None + """Represents an NFS snapshot.""" diff --git a/src/gradient/types/region_list_params.py b/src/gradient/types/region_list_params.py new file mode 100644 index 00000000..4fef37b3 --- /dev/null +++ b/src/gradient/types/region_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["RegionListParams"] + + +class RegionListParams(TypedDict, total=False): + page: int + """Which 'page' of paginated results to return.""" + + per_page: int + """Number of items returned per page""" diff --git a/src/gradient/types/region_list_response.py b/src/gradient/types/region_list_response.py new file mode 100644 index 00000000..f1bf4c69 --- /dev/null +++ b/src/gradient/types/region_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .shared.region import Region +from .shared.page_links import PageLinks +from .shared.meta_properties import MetaProperties + +__all__ = ["RegionListResponse"] + + +class RegionListResponse(BaseModel): + meta: MetaProperties + """Information about the response itself.""" + + regions: List[Region] + + links: Optional[PageLinks] = None diff --git a/src/gradient/types/shared/__init__.py b/src/gradient/types/shared/__init__.py new file mode 100644 index 00000000..4fb2986a --- /dev/null +++ b/src/gradient/types/shared/__init__.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .size import Size as Size +from .image import Image as Image +from .action import Action as Action +from .kernel import Kernel as Kernel +from .region import Region as Region +from .droplet import Droplet as Droplet +from .api_meta import APIMeta as APIMeta +from .gpu_info import GPUInfo as GPUInfo +from .api_links import APILinks as APILinks +from .disk_info import DiskInfo as DiskInfo +from .snapshots import Snapshots as Snapshots +from .network_v4 import NetworkV4 as NetworkV4 +from .network_v6 import NetworkV6 as NetworkV6 +from .page_links import PageLinks as PageLinks +from .action_link import ActionLink as ActionLink +from .vpc_peering import VpcPeering as VpcPeering +from .subscription import Subscription as Subscription +from .forward_links import ForwardLinks as ForwardLinks +from .backward_links import BackwardLinks as BackwardLinks +from .meta_properties import MetaProperties as MetaProperties +from .completion_usage import CompletionUsage as CompletionUsage +from .garbage_collection import GarbageCollection as GarbageCollection +from .firewall_rule_target import FirewallRuleTarget as FirewallRuleTarget +from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk +from .image_gen_stream_event import ImageGenStreamEvent as ImageGenStreamEvent +from .subscription_tier_base import SubscriptionTierBase as SubscriptionTierBase +from .image_gen_completed_event import ImageGenCompletedEvent as ImageGenCompletedEvent +from .droplet_next_backup_window import DropletNextBackupWindow as DropletNextBackupWindow +from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob +from .image_gen_partial_image_event import ImageGenPartialImageEvent as ImageGenPartialImageEvent diff --git a/src/gradient/types/shared/action.py b/src/gradient/types/shared/action.py new file mode 100644 index 00000000..2b9fbf4e --- /dev/null +++ b/src/gradient/types/shared/action.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from .region import Region +from ..._models import BaseModel + +__all__ = ["Action"] + + +class Action(BaseModel): + id: Optional[int] = None + """A unique numeric ID that can be used to identify and reference an action.""" + + completed_at: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format that represents when + the action was completed. + """ + + region: Optional[Region] = None + + region_slug: Optional[str] = None + """A human-readable string that is used as a unique identifier for each region.""" + + resource_id: Optional[int] = None + """A unique identifier for the resource that the action is associated with.""" + + resource_type: Optional[str] = None + """The type of resource that the action is associated with.""" + + started_at: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format that represents when + the action was initiated. + """ + + status: Optional[Literal["in-progress", "completed", "errored"]] = None + """The current status of the action. + + This can be "in-progress", "completed", or "errored". + """ + + type: Optional[str] = None + """This is the type of action that the object represents. + + For example, this could be "transfer" to represent the state of an image + transfer action. + """ diff --git a/src/gradient/types/shared/action_link.py b/src/gradient/types/shared/action_link.py new file mode 100644 index 00000000..78aec9ff --- /dev/null +++ b/src/gradient/types/shared/action_link.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["ActionLink"] + + +class ActionLink(BaseModel): + id: Optional[int] = None + """A unique numeric ID that can be used to identify and reference an action.""" + + href: Optional[str] = None + """A URL that can be used to access the action.""" + + rel: Optional[str] = None + """A string specifying the type of the related action.""" diff --git a/src/gradient/types/shared/api_links.py b/src/gradient/types/shared/api_links.py new file mode 100644 index 00000000..24b19cfe --- /dev/null +++ b/src/gradient/types/shared/api_links.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["APILinks", "Pages"] + + +class Pages(BaseModel): + first: Optional[str] = None + """First page""" + + last: Optional[str] = None + """Last page""" + + next: Optional[str] = None + """Next page""" + + previous: Optional[str] = None + """Previous page""" + + +class APILinks(BaseModel): + pages: Optional[Pages] = None + """Information about how to reach other pages""" diff --git a/src/gradient/types/shared/api_meta.py b/src/gradient/types/shared/api_meta.py new file mode 100644 index 00000000..dc267527 --- /dev/null +++ b/src/gradient/types/shared/api_meta.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["APIMeta"] + + +class APIMeta(BaseModel): + page: Optional[int] = None + """The current page""" + + pages: Optional[int] = None + """Total number of pages""" + + total: Optional[int] = None + """Total amount of items over all pages""" diff --git a/src/gradient/types/shared/backward_links.py b/src/gradient/types/shared/backward_links.py new file mode 100644 index 00000000..502fefef --- /dev/null +++ b/src/gradient/types/shared/backward_links.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["BackwardLinks"] + + +class BackwardLinks(BaseModel): + first: Optional[str] = None + """URI of the first page of the results.""" + + prev: Optional[str] = None + """URI of the previous page of the results.""" diff --git a/src/gradient/types/shared/chat_completion_chunk.py b/src/gradient/types/shared/chat_completion_chunk.py new file mode 100644 index 00000000..ff705bf4 --- /dev/null +++ b/src/gradient/types/shared/chat_completion_chunk.py @@ -0,0 +1,121 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .completion_usage import CompletionUsage +from .chat_completion_token_logprob import ChatCompletionTokenLogprob + +__all__ = [ + "ChatCompletionChunk", + "Choice", + "ChoiceDelta", + "ChoiceDeltaToolCall", + "ChoiceDeltaToolCallFunction", + "ChoiceLogprobs", +] + + +class ChoiceDeltaToolCallFunction(BaseModel): + arguments: Optional[str] = None + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Optional[str] = None + """The name of the function to call.""" + + +class ChoiceDeltaToolCall(BaseModel): + index: int + + id: Optional[str] = None + """The ID of the tool call.""" + + function: Optional[ChoiceDeltaToolCallFunction] = None + """A chunk of a function that the model called.""" + + type: Optional[Literal["function"]] = None + """The type of the tool. Currently, only `function` is supported.""" + + +class ChoiceDelta(BaseModel): + content: Optional[str] = None + """The contents of the chunk message.""" + + reasoning_content: Optional[str] = None + """The reasoning content generated by the model.""" + + refusal: Optional[str] = None + """The refusal message generated by the model.""" + + role: Optional[Literal["developer", "user", "assistant"]] = None + """The role of the author of this message.""" + + tool_calls: Optional[List[ChoiceDeltaToolCall]] = None + + +class ChoiceLogprobs(BaseModel): + content: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message content tokens with log probability information.""" + + refusal: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message refusal tokens with log probability information.""" + + +class Choice(BaseModel): + delta: ChoiceDelta + """A chat completion delta generated by streamed model responses.""" + + finish_reason: Optional[Literal["stop", "length", "tool_calls", "content_filter"]] = None + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, or `length` if the maximum number of tokens specified in the request + was reached, `tool_calls` if the model called a tool. + """ + + index: int + """The index of the choice in the list of choices.""" + + logprobs: Optional[ChoiceLogprobs] = None + """Log probability information for the choice.""" + + +class ChatCompletionChunk(BaseModel): + id: str + """A unique identifier for the chat completion. Each chunk has the same ID.""" + + choices: List[Choice] + """A list of chat completion choices. + + Can contain more than one elements if `n` is greater than 1. Can also be empty + for the last chunk if you set `stream_options: {"include_usage": true}`. + """ + + created: int + """The Unix timestamp (in seconds) of when the chat completion was created. + + Each chunk has the same timestamp. + """ + + model: str + """The model to generate the completion.""" + + object: Literal["chat.completion.chunk"] + """The object type, which is always `chat.completion.chunk`.""" + + usage: Optional[CompletionUsage] = None + """ + An optional field that will only be present when you set + `stream_options: {"include_usage": true}` in your request. When present, it + contains a null value **except for the last chunk** which contains the token + usage statistics for the entire request. + + **NOTE:** If the stream is interrupted or cancelled, you may not receive the + final usage chunk which contains the total token usage for the request. + """ diff --git a/src/gradient/types/shared/chat_completion_token_logprob.py b/src/gradient/types/shared/chat_completion_token_logprob.py new file mode 100644 index 00000000..c69e2589 --- /dev/null +++ b/src/gradient/types/shared/chat_completion_token_logprob.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["ChatCompletionTokenLogprob", "TopLogprob"] + + +class TopLogprob(BaseModel): + token: str + """The token.""" + + bytes: Optional[List[int]] = None + """A list of integers representing the UTF-8 bytes representation of the token. + + Useful in instances where characters are represented by multiple tokens and + their byte representations must be combined to generate the correct text + representation. Can be `null` if there is no bytes representation for the token. + """ + + logprob: float + """The log probability of this token, if it is within the top 20 most likely + tokens. + + Otherwise, the value `-9999.0` is used to signify that the token is very + unlikely. + """ + + +class ChatCompletionTokenLogprob(BaseModel): + token: str + """The token.""" + + bytes: Optional[List[int]] = None + """A list of integers representing the UTF-8 bytes representation of the token. + + Useful in instances where characters are represented by multiple tokens and + their byte representations must be combined to generate the correct text + representation. Can be `null` if there is no bytes representation for the token. + """ + + logprob: float + """The log probability of this token, if it is within the top 20 most likely + tokens. + + Otherwise, the value `-9999.0` is used to signify that the token is very + unlikely. + """ + + top_logprobs: List[TopLogprob] + """List of the most likely tokens and their log probability, at this token + position. + + In rare cases, there may be fewer than the number of requested `top_logprobs` + returned. + """ diff --git a/src/gradient/types/shared/completion_usage.py b/src/gradient/types/shared/completion_usage.py new file mode 100644 index 00000000..a2012eef --- /dev/null +++ b/src/gradient/types/shared/completion_usage.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["CompletionUsage"] + + +class CompletionUsage(BaseModel): + completion_tokens: int + """Number of tokens in the generated completion.""" + + prompt_tokens: int + """Number of tokens in the prompt.""" + + total_tokens: int + """Total number of tokens used in the request (prompt + completion).""" diff --git a/src/gradient/types/shared/disk_info.py b/src/gradient/types/shared/disk_info.py new file mode 100644 index 00000000..3c5c4911 --- /dev/null +++ b/src/gradient/types/shared/disk_info.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["DiskInfo", "Size"] + + +class Size(BaseModel): + amount: Optional[int] = None + """The amount of space allocated to the disk.""" + + unit: Optional[str] = None + """The unit of measure for the disk size.""" + + +class DiskInfo(BaseModel): + size: Optional[Size] = None + + type: Optional[Literal["local", "scratch"]] = None + """The type of disk. + + All Droplets contain a `local` disk. Additionally, GPU Droplets can also have a + `scratch` disk for non-persistent data. + """ diff --git a/src/gradient/types/shared/droplet.py b/src/gradient/types/shared/droplet.py new file mode 100644 index 00000000..9d2bb17c --- /dev/null +++ b/src/gradient/types/shared/droplet.py @@ -0,0 +1,143 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .size import Size +from .image import Image +from .kernel import Kernel +from .region import Region +from .gpu_info import GPUInfo +from ..._models import BaseModel +from .disk_info import DiskInfo +from .network_v4 import NetworkV4 +from .network_v6 import NetworkV6 +from .droplet_next_backup_window import DropletNextBackupWindow + +__all__ = ["Droplet", "Networks"] + + +class Networks(BaseModel): + v4: Optional[List[NetworkV4]] = None + + v6: Optional[List[NetworkV6]] = None + + +class Droplet(BaseModel): + id: int + """A unique identifier for each Droplet instance. + + This is automatically generated upon Droplet creation. + """ + + backup_ids: List[int] + """ + An array of backup IDs of any backups that have been taken of the Droplet + instance. Droplet backups are enabled at the time of the instance creation. + Requires `image:read` scope. + """ + + created_at: datetime + """ + A time value given in ISO8601 combined date and time format that represents when + the Droplet was created. + """ + + disk: int + """The size of the Droplet's disk in gigabytes.""" + + features: List[str] + """An array of features enabled on this Droplet.""" + + image: Image + """The Droplet's image. Requires `image:read` scope.""" + + locked: bool + """ + A boolean value indicating whether the Droplet has been locked, preventing + actions by users. + """ + + memory: int + """Memory of the Droplet in megabytes.""" + + name: str + """The human-readable name set for the Droplet instance.""" + + networks: Networks + """The details of the network that are configured for the Droplet instance. + + This is an object that contains keys for IPv4 and IPv6. The value of each of + these is an array that contains objects describing an individual IP resource + allocated to the Droplet. These will define attributes like the IP address, + netmask, and gateway of the specific network depending on the type of network it + is. + """ + + next_backup_window: Optional[DropletNextBackupWindow] = None + """ + The details of the Droplet's backups feature, if backups are configured for the + Droplet. This object contains keys for the start and end times of the window + during which the backup will start. + """ + + region: Region + + size: Size + + size_slug: str + """The unique slug identifier for the size of this Droplet.""" + + snapshot_ids: List[int] + """ + An array of snapshot IDs of any snapshots created from the Droplet instance. + Requires `image:read` scope. + """ + + status: Literal["new", "active", "off", "archive"] + """A status string indicating the state of the Droplet instance. + + This may be "new", "active", "off", or "archive". + """ + + tags: List[str] + """An array of Tags the Droplet has been tagged with. Requires `tag:read` scope.""" + + vcpus: int + """The number of virtual CPUs.""" + + volume_ids: List[str] + """ + A flat array including the unique identifier for each Block Storage volume + attached to the Droplet. Requires `block_storage:read` scope. + """ + + disk_info: Optional[List[DiskInfo]] = None + """ + An array of objects containing information about the disks available to the + Droplet. + """ + + gpu_info: Optional[GPUInfo] = None + """ + An object containing information about the GPU capabilities of Droplets created + with this size. + """ + + kernel: Optional[Kernel] = None + """ + **Note**: All Droplets created after March 2017 use internal kernels by default. + These Droplets will have this attribute set to `null`. + + The current + [kernel](https://docs.digitalocean.com/products/droplets/how-to/kernel/) for + Droplets with externally managed kernels. This will initially be set to the + kernel of the base image when the Droplet is created. + """ + + vpc_uuid: Optional[str] = None + """ + A string specifying the UUID of the VPC to which the Droplet is assigned. + Requires `vpc:read` scope. + """ diff --git a/src/gradient/types/shared/droplet_next_backup_window.py b/src/gradient/types/shared/droplet_next_backup_window.py new file mode 100644 index 00000000..81d07be6 --- /dev/null +++ b/src/gradient/types/shared/droplet_next_backup_window.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["DropletNextBackupWindow"] + + +class DropletNextBackupWindow(BaseModel): + end: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format specifying the end + of the Droplet's backup window. + """ + + start: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format specifying the start + of the Droplet's backup window. + """ diff --git a/src/gradient/types/shared/firewall_rule_target.py b/src/gradient/types/shared/firewall_rule_target.py new file mode 100644 index 00000000..11f61065 --- /dev/null +++ b/src/gradient/types/shared/firewall_rule_target.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["FirewallRuleTarget"] + + +class FirewallRuleTarget(BaseModel): + addresses: Optional[List[str]] = None + """ + An array of strings containing the IPv4 addresses, IPv6 addresses, IPv4 CIDRs, + and/or IPv6 CIDRs to which the firewall will allow traffic. + """ + + droplet_ids: Optional[List[int]] = None + """ + An array containing the IDs of the Droplets to which the firewall will allow + traffic. + """ + + kubernetes_ids: Optional[List[str]] = None + """ + An array containing the IDs of the Kubernetes clusters to which the firewall + will allow traffic. + """ + + load_balancer_uids: Optional[List[str]] = None + """ + An array containing the IDs of the load balancers to which the firewall will + allow traffic. + """ + + tags: Optional[List[str]] = None + """A flat array of tag names as strings to be applied to the resource. + + Tag names must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + """ diff --git a/src/gradient/types/shared/forward_links.py b/src/gradient/types/shared/forward_links.py new file mode 100644 index 00000000..30d46985 --- /dev/null +++ b/src/gradient/types/shared/forward_links.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["ForwardLinks"] + + +class ForwardLinks(BaseModel): + last: Optional[str] = None + """URI of the last page of the results.""" + + next: Optional[str] = None + """URI of the next page of the results.""" diff --git a/src/gradient/types/shared/garbage_collection.py b/src/gradient/types/shared/garbage_collection.py new file mode 100644 index 00000000..f1f7f4cd --- /dev/null +++ b/src/gradient/types/shared/garbage_collection.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["GarbageCollection"] + + +class GarbageCollection(BaseModel): + blobs_deleted: Optional[int] = None + """The number of blobs deleted as a result of this garbage collection.""" + + created_at: Optional[datetime] = None + """The time the garbage collection was created.""" + + freed_bytes: Optional[int] = None + """The number of bytes freed as a result of this garbage collection.""" + + registry_name: Optional[str] = None + """The name of the container registry.""" + + status: Optional[ + Literal[ + "requested", + "waiting for write JWTs to expire", + "scanning manifests", + "deleting unreferenced blobs", + "cancelling", + "failed", + "succeeded", + "cancelled", + ] + ] = None + """The current status of this garbage collection.""" + + updated_at: Optional[datetime] = None + """The time the garbage collection was last updated.""" + + uuid: Optional[str] = None + """A string specifying the UUID of the garbage collection.""" diff --git a/src/gradient/types/shared/gpu_info.py b/src/gradient/types/shared/gpu_info.py new file mode 100644 index 00000000..a285dd23 --- /dev/null +++ b/src/gradient/types/shared/gpu_info.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["GPUInfo", "Vram"] + + +class Vram(BaseModel): + amount: Optional[int] = None + """The amount of VRAM allocated to the GPU.""" + + unit: Optional[str] = None + """The unit of measure for the VRAM.""" + + +class GPUInfo(BaseModel): + count: Optional[int] = None + """The number of GPUs allocated to the Droplet.""" + + model: Optional[str] = None + """The model of the GPU.""" + + vram: Optional[Vram] = None diff --git a/src/gradient/types/shared/image.py b/src/gradient/types/shared/image.py new file mode 100644 index 00000000..d8a7acde --- /dev/null +++ b/src/gradient/types/shared/image.py @@ -0,0 +1,131 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Image"] + + +class Image(BaseModel): + id: Optional[int] = None + """A unique number that can be used to identify and reference a specific image.""" + + created_at: Optional[datetime] = None + """ + A time value given in ISO8601 combined date and time format that represents when + the image was created. + """ + + description: Optional[str] = None + """An optional free-form text field to describe an image.""" + + distribution: Optional[ + Literal[ + "Arch Linux", + "CentOS", + "CoreOS", + "Debian", + "Fedora", + "Fedora Atomic", + "FreeBSD", + "Gentoo", + "openSUSE", + "RancherOS", + "Rocky Linux", + "Ubuntu", + "Unknown", + ] + ] = None + """The name of a custom image's distribution. + + Currently, the valid values are `Arch Linux`, `CentOS`, `CoreOS`, `Debian`, + `Fedora`, `Fedora Atomic`, `FreeBSD`, `Gentoo`, `openSUSE`, `RancherOS`, + `Rocky Linux`, `Ubuntu`, and `Unknown`. Any other value will be accepted but + ignored, and `Unknown` will be used in its place. + """ + + error_message: Optional[str] = None + """ + A string containing information about errors that may occur when importing a + custom image. + """ + + min_disk_size: Optional[int] = None + """The minimum disk size in GB required for a Droplet to use this image.""" + + name: Optional[str] = None + """The display name that has been given to an image. + + This is what is shown in the control panel and is generally a descriptive title + for the image in question. + """ + + public: Optional[bool] = None + """ + This is a boolean value that indicates whether the image in question is public + or not. An image that is public is available to all accounts. A non-public image + is only accessible from your account. + """ + + regions: Optional[ + List[ + Literal[ + "ams1", + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + "syd1", + ] + ] + ] = None + """This attribute is an array of the regions that the image is available in. + + The regions are represented by their identifying slug values. + """ + + size_gigabytes: Optional[float] = None + """The size of the image in gigabytes.""" + + slug: Optional[str] = None + """ + A uniquely identifying string that is associated with each of the + DigitalOcean-provided public images. These can be used to reference a public + image as an alternative to the numeric id. + """ + + status: Optional[Literal["NEW", "available", "pending", "deleted", "retired"]] = None + """A status string indicating the state of a custom image. + + This may be `NEW`, `available`, `pending`, `deleted`, or `retired`. + """ + + tags: Optional[List[str]] = None + """A flat array of tag names as strings to be applied to the resource. + + Tag names may be for either existing or new tags. + + Requires `tag:create` scope. + """ + + type: Optional[Literal["base", "snapshot", "backup", "custom", "admin"]] = None + """Describes the kind of image. + + It may be one of `base`, `snapshot`, `backup`, `custom`, or `admin`. + Respectively, this specifies whether an image is a DigitalOcean base OS image, + user-generated Droplet snapshot, automatically created Droplet backup, + user-provided virtual machine image, or an image used for DigitalOcean managed + resources (e.g. DOKS worker nodes). + """ diff --git a/src/gradient/types/shared/image_gen_completed_event.py b/src/gradient/types/shared/image_gen_completed_event.py new file mode 100644 index 00000000..cbb282e5 --- /dev/null +++ b/src/gradient/types/shared/image_gen_completed_event.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ImageGenCompletedEvent", "Usage", "UsageInputTokensDetails"] + + +class UsageInputTokensDetails(BaseModel): + image_tokens: int + """The number of image tokens in the input prompt.""" + + text_tokens: int + """The number of text tokens in the input prompt.""" + + +class Usage(BaseModel): + input_tokens: int + """The number of tokens (images and text) in the input prompt.""" + + input_tokens_details: UsageInputTokensDetails + """The input tokens detailed information for the image generation.""" + + output_tokens: int + """The number of image tokens in the output image.""" + + total_tokens: int + """The total number of tokens (images and text) used for the image generation.""" + + +class ImageGenCompletedEvent(BaseModel): + b64_json: str + """Base64-encoded image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the generated image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the generated image.""" + + quality: Literal["low", "medium", "high", "auto"] + """The quality setting for the generated image.""" + + size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"] + """The size of the generated image.""" + + type: Literal["image_generation.completed"] + """The type of the event. Always `image_generation.completed`.""" + + usage: Usage + """For `gpt-image-1` only, the token usage information for the image generation.""" diff --git a/src/gradient/types/shared/image_gen_partial_image_event.py b/src/gradient/types/shared/image_gen_partial_image_event.py new file mode 100644 index 00000000..4cc704b2 --- /dev/null +++ b/src/gradient/types/shared/image_gen_partial_image_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ImageGenPartialImageEvent"] + + +class ImageGenPartialImageEvent(BaseModel): + b64_json: str + """Base64-encoded partial image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the requested image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the requested image.""" + + partial_image_index: int + """0-based index for the partial image (streaming).""" + + quality: Literal["low", "medium", "high", "auto"] + """The quality setting for the requested image.""" + + size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"] + """The size of the requested image.""" + + type: Literal["image_generation.partial_image"] + """The type of the event. Always `image_generation.partial_image`.""" diff --git a/src/gradient/types/shared/image_gen_stream_event.py b/src/gradient/types/shared/image_gen_stream_event.py new file mode 100644 index 00000000..30e9571e --- /dev/null +++ b/src/gradient/types/shared/image_gen_stream_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .image_gen_completed_event import ImageGenCompletedEvent +from .image_gen_partial_image_event import ImageGenPartialImageEvent + +__all__ = ["ImageGenStreamEvent"] + +ImageGenStreamEvent: TypeAlias = Annotated[ + Union[ImageGenPartialImageEvent, ImageGenCompletedEvent], PropertyInfo(discriminator="type") +] diff --git a/src/gradient/types/shared/kernel.py b/src/gradient/types/shared/kernel.py new file mode 100644 index 00000000..78a63427 --- /dev/null +++ b/src/gradient/types/shared/kernel.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["Kernel"] + + +class Kernel(BaseModel): + id: Optional[int] = None + """A unique number used to identify and reference a specific kernel.""" + + name: Optional[str] = None + """The display name of the kernel. + + This is shown in the web UI and is generally a descriptive title for the kernel + in question. + """ + + version: Optional[str] = None + """ + A standard kernel version string representing the version, patch, and release + information. + """ diff --git a/src/gradient/types/shared/meta_properties.py b/src/gradient/types/shared/meta_properties.py new file mode 100644 index 00000000..a78a64d6 --- /dev/null +++ b/src/gradient/types/shared/meta_properties.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["MetaProperties"] + + +class MetaProperties(BaseModel): + total: Optional[int] = None + """Number of objects returned by the request.""" diff --git a/src/gradient/types/shared/network_v4.py b/src/gradient/types/shared/network_v4.py new file mode 100644 index 00000000..bbf8490a --- /dev/null +++ b/src/gradient/types/shared/network_v4.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["NetworkV4"] + + +class NetworkV4(BaseModel): + gateway: Optional[str] = None + """The gateway of the specified IPv4 network interface. + + For private interfaces, a gateway is not provided. This is denoted by returning + `nil` as its value. + """ + + ip_address: Optional[str] = None + """The IP address of the IPv4 network interface.""" + + netmask: Optional[str] = None + """The netmask of the IPv4 network interface.""" + + type: Optional[Literal["public", "private"]] = None + """The type of the IPv4 network interface.""" diff --git a/src/gradient/types/shared/network_v6.py b/src/gradient/types/shared/network_v6.py new file mode 100644 index 00000000..a3eb6b42 --- /dev/null +++ b/src/gradient/types/shared/network_v6.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["NetworkV6"] + + +class NetworkV6(BaseModel): + gateway: Optional[str] = None + """The gateway of the specified IPv6 network interface.""" + + ip_address: Optional[str] = None + """The IP address of the IPv6 network interface.""" + + netmask: Optional[int] = None + """The netmask of the IPv6 network interface.""" + + type: Optional[Literal["public"]] = None + """The type of the IPv6 network interface. + + **Note**: IPv6 private networking is not currently supported. + """ diff --git a/src/gradient/types/shared/page_links.py b/src/gradient/types/shared/page_links.py new file mode 100644 index 00000000..bfceabef --- /dev/null +++ b/src/gradient/types/shared/page_links.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import TypeAlias + +from ..._models import BaseModel +from .forward_links import ForwardLinks +from .backward_links import BackwardLinks + +__all__ = ["PageLinks", "Pages"] + +Pages: TypeAlias = Union[ForwardLinks, BackwardLinks, object] + + +class PageLinks(BaseModel): + pages: Optional[Pages] = None diff --git a/src/gradient/types/shared/region.py b/src/gradient/types/shared/region.py new file mode 100644 index 00000000..d2fe7c51 --- /dev/null +++ b/src/gradient/types/shared/region.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["Region"] + + +class Region(BaseModel): + available: bool + """ + This is a boolean value that represents whether new Droplets can be created in + this region. + """ + + features: List[str] + """ + This attribute is set to an array which contains features available in this + region + """ + + name: str + """The display name of the region. + + This will be a full name that is used in the control panel and other interfaces. + """ + + sizes: List[str] + """ + This attribute is set to an array which contains the identifying slugs for the + sizes available in this region. sizes:read is required to view. + """ + + slug: str + """A human-readable string that is used as a unique identifier for each region.""" diff --git a/src/gradient/types/shared/size.py b/src/gradient/types/shared/size.py new file mode 100644 index 00000000..73abb7dd --- /dev/null +++ b/src/gradient/types/shared/size.py @@ -0,0 +1,79 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .gpu_info import GPUInfo +from ..._models import BaseModel +from .disk_info import DiskInfo + +__all__ = ["Size"] + + +class Size(BaseModel): + available: bool + """ + This is a boolean value that represents whether new Droplets can be created with + this size. + """ + + description: str + """A string describing the class of Droplets created from this size. + + For example: Basic, General Purpose, CPU-Optimized, Memory-Optimized, or + Storage-Optimized. + """ + + disk: int + """The amount of disk space set aside for Droplets of this size. + + The value is represented in gigabytes. + """ + + memory: int + """The amount of RAM allocated to Droplets created of this size. + + The value is represented in megabytes. + """ + + price_hourly: float + """This describes the price of the Droplet size as measured hourly. + + The value is measured in US dollars. + """ + + price_monthly: float + """ + This attribute describes the monthly cost of this Droplet size if the Droplet is + kept for an entire month. The value is measured in US dollars. + """ + + regions: List[str] + """ + An array containing the region slugs where this size is available for Droplet + creates. + """ + + slug: str + """A human-readable string that is used to uniquely identify each size.""" + + transfer: float + """ + The amount of transfer bandwidth that is available for Droplets created in this + size. This only counts traffic on the public interface. The value is given in + terabytes. + """ + + vcpus: int + """The number of CPUs allocated to Droplets of this size.""" + + disk_info: Optional[List[DiskInfo]] = None + """ + An array of objects containing information about the disks available to Droplets + created with this size. + """ + + gpu_info: Optional[GPUInfo] = None + """ + An object containing information about the GPU capabilities of Droplets created + with this size. + """ diff --git a/src/gradient/types/shared/snapshots.py b/src/gradient/types/shared/snapshots.py new file mode 100644 index 00000000..940b58c8 --- /dev/null +++ b/src/gradient/types/shared/snapshots.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Snapshots"] + + +class Snapshots(BaseModel): + id: str + """The unique identifier for the snapshot.""" + + created_at: datetime + """ + A time value given in ISO8601 combined date and time format that represents when + the snapshot was created. + """ + + min_disk_size: int + """The minimum size in GB required for a volume or Droplet to use this snapshot.""" + + name: str + """A human-readable name for the snapshot.""" + + regions: List[str] + """An array of the regions that the snapshot is available in. + + The regions are represented by their identifying slug values. + """ + + resource_id: str + """The unique identifier for the resource that the snapshot originated from.""" + + resource_type: Literal["droplet", "volume"] + """The type of resource that the snapshot originated from.""" + + size_gigabytes: float + """The billable size of the snapshot in gigabytes.""" + + tags: Optional[List[str]] = None + """An array of Tags the snapshot has been tagged with. + + Requires `tag:read` scope. + """ diff --git a/src/gradient/types/shared/subscription.py b/src/gradient/types/shared/subscription.py new file mode 100644 index 00000000..4d77a9b8 --- /dev/null +++ b/src/gradient/types/shared/subscription.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from ..._models import BaseModel +from .subscription_tier_base import SubscriptionTierBase + +__all__ = ["Subscription"] + + +class Subscription(BaseModel): + created_at: Optional[datetime] = None + """The time at which the subscription was created.""" + + tier: Optional[SubscriptionTierBase] = None + + updated_at: Optional[datetime] = None + """The time at which the subscription was last updated.""" diff --git a/src/gradient/types/shared/subscription_tier_base.py b/src/gradient/types/shared/subscription_tier_base.py new file mode 100644 index 00000000..65e1a316 --- /dev/null +++ b/src/gradient/types/shared/subscription_tier_base.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["SubscriptionTierBase"] + + +class SubscriptionTierBase(BaseModel): + allow_storage_overage: Optional[bool] = None + """ + A boolean indicating whether the subscription tier supports additional storage + above what is included in the base plan at an additional cost per GiB used. + """ + + included_bandwidth_bytes: Optional[int] = None + """ + The amount of outbound data transfer included in the subscription tier in bytes. + """ + + included_repositories: Optional[int] = None + """The number of repositories included in the subscription tier. + + `0` indicates that the subscription tier includes unlimited repositories. + """ + + included_storage_bytes: Optional[int] = None + """The amount of storage included in the subscription tier in bytes.""" + + monthly_price_in_cents: Optional[int] = None + """The monthly cost of the subscription tier in cents.""" + + name: Optional[str] = None + """The name of the subscription tier.""" + + slug: Optional[str] = None + """The slug identifier of the subscription tier.""" + + storage_overage_price_in_cents: Optional[int] = None + """ + The price paid in cents per GiB for additional storage beyond what is included + in the subscription plan. + """ diff --git a/src/gradient/types/shared/vpc_peering.py b/src/gradient/types/shared/vpc_peering.py new file mode 100644 index 00000000..ef674e23 --- /dev/null +++ b/src/gradient/types/shared/vpc_peering.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["VpcPeering"] + + +class VpcPeering(BaseModel): + id: Optional[str] = None + """A unique ID that can be used to identify and reference the VPC peering.""" + + created_at: Optional[datetime] = None + """A time value given in ISO8601 combined date and time format.""" + + name: Optional[str] = None + """The name of the VPC peering. + + Must be unique within the team and may only contain alphanumeric characters and + dashes. + """ + + status: Optional[Literal["PROVISIONING", "ACTIVE", "DELETING"]] = None + """The current status of the VPC peering.""" + + vpc_ids: Optional[List[str]] = None + """An array of the two peered VPCs IDs.""" diff --git a/src/gradient/types/shared_params/__init__.py b/src/gradient/types/shared_params/__init__.py new file mode 100644 index 00000000..ccdec8fd --- /dev/null +++ b/src/gradient/types/shared_params/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .firewall_rule_target import FirewallRuleTarget as FirewallRuleTarget diff --git a/src/gradient/types/shared_params/firewall_rule_target.py b/src/gradient/types/shared_params/firewall_rule_target.py new file mode 100644 index 00000000..7f317f6c --- /dev/null +++ b/src/gradient/types/shared_params/firewall_rule_target.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["FirewallRuleTarget"] + + +class FirewallRuleTarget(TypedDict, total=False): + addresses: SequenceNotStr[str] + """ + An array of strings containing the IPv4 addresses, IPv6 addresses, IPv4 CIDRs, + and/or IPv6 CIDRs to which the firewall will allow traffic. + """ + + droplet_ids: Iterable[int] + """ + An array containing the IDs of the Droplets to which the firewall will allow + traffic. + """ + + kubernetes_ids: SequenceNotStr[str] + """ + An array containing the IDs of the Kubernetes clusters to which the firewall + will allow traffic. + """ + + load_balancer_uids: SequenceNotStr[str] + """ + An array containing the IDs of the load balancers to which the firewall will + allow traffic. + """ + + tags: Optional[SequenceNotStr[str]] + """A flat array of tag names as strings to be applied to the resource. + + Tag names must exist in order to be referenced in a request. + + Requires `tag:create` and `tag:read` scopes. + """ diff --git a/src/gradientai/lib/.keep b/src/gradientai/lib/.keep new file mode 100644 index 00000000..5e2c99fd --- /dev/null +++ b/src/gradientai/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/__init__.py b/tests/api_resources/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/agents/__init__.py b/tests/api_resources/agents/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/agents/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/agents/chat/__init__.py b/tests/api_resources/agents/chat/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/agents/chat/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/agents/chat/test_completions.py b/tests/api_resources/agents/chat/test_completions.py new file mode 100644 index 00000000..2824ed3d --- /dev/null +++ b/tests/api_resources/agents/chat/test_completions.py @@ -0,0 +1,396 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents.chat import CompletionCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCompletions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_1(self, client: Gradient) -> None: + completion = client.agents.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_1(self, client: Gradient) -> None: + completion = client.agents.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=True, + max_completion_tokens=256, + max_tokens=0, + metadata={"foo": "string"}, + n=1, + presence_penalty=-2, + stop="\n", + stream=False, + stream_options={"include_usage": True}, + temperature=1, + tool_choice="none", + tools=[ + { + "function": { + "name": "name", + "description": "description", + "parameters": {"foo": "bar"}, + }, + "type": "function", + } + ], + top_logprobs=0, + top_p=1, + user="user-1234", + ) + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_1(self, client: Gradient) -> None: + response = client.agents.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_1(self, client: Gradient) -> None: + with client.agents.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = response.parse() + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_2(self, client: Gradient) -> None: + completion_stream = client.agents.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) + completion_stream.response.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_2(self, client: Gradient) -> None: + completion_stream = client.agents.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=True, + max_completion_tokens=256, + max_tokens=0, + metadata={"foo": "string"}, + n=1, + presence_penalty=-2, + stop="\n", + stream_options={"include_usage": True}, + temperature=1, + tool_choice="none", + tools=[ + { + "function": { + "name": "name", + "description": "description", + "parameters": {"foo": "bar"}, + }, + "type": "function", + } + ], + top_logprobs=0, + top_p=1, + user="user-1234", + ) + completion_stream.response.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_2(self, client: Gradient) -> None: + response = client.agents.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_2(self, client: Gradient) -> None: + with client.agents.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + +class TestAsyncCompletions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncGradient) -> None: + completion = await async_client.agents.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + completion = await async_client.agents.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=True, + max_completion_tokens=256, + max_tokens=0, + metadata={"foo": "string"}, + n=1, + presence_penalty=-2, + stop="\n", + stream=False, + stream_options={"include_usage": True}, + temperature=1, + tool_choice="none", + tools=[ + { + "function": { + "name": "name", + "description": "description", + "parameters": {"foo": "bar"}, + }, + "type": "function", + } + ], + top_logprobs=0, + top_p=1, + user="user-1234", + ) + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = await response.parse() + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.agents.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = await response.parse() + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncGradient) -> None: + completion_stream = await async_client.agents.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) + await completion_stream.response.aclose() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + completion_stream = await async_client.agents.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=True, + max_completion_tokens=256, + max_tokens=0, + metadata={"foo": "string"}, + n=1, + presence_penalty=-2, + stop="\n", + stream_options={"include_usage": True}, + temperature=1, + tool_choice="none", + tools=[ + { + "function": { + "name": "name", + "description": "description", + "parameters": {"foo": "bar"}, + }, + "type": "function", + } + ], + top_logprobs=0, + top_p=1, + user="user-1234", + ) + await completion_stream.response.aclose() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.agents.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/agents/evaluation_metrics/__init__.py b/tests/api_resources/agents/evaluation_metrics/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/agents/evaluation_metrics/anthropic/__init__.py b/tests/api_resources/agents/evaluation_metrics/anthropic/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/anthropic/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/agents/evaluation_metrics/anthropic/test_keys.py b/tests/api_resources/agents/evaluation_metrics/anthropic/test_keys.py new file mode 100644 index 00000000..5028698c --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/anthropic/test_keys.py @@ -0,0 +1,557 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents.evaluation_metrics.anthropic import ( + KeyListResponse, + KeyCreateResponse, + KeyDeleteResponse, + KeyUpdateResponse, + KeyRetrieveResponse, + KeyListAgentsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestKeys: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.create() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.create( + api_key='"sk-ant-12345678901234567890123456789012"', + name='"Production Key"', + ) + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.anthropic.keys.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.anthropic.keys.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.agents.evaluation_metrics.anthropic.keys.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + api_key='"sk-ant-12345678901234567890123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.anthropic.keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + client.agents.evaluation_metrics.anthropic.keys.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.list() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.list( + page=0, + per_page=0, + ) + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.anthropic.keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.anthropic.keys.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.agents.evaluation_metrics.anthropic.keys.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_agents(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_agents_with_all_params(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.anthropic.keys.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_agents(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.anthropic.keys.with_raw_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_agents(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_agents(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.agents.evaluation_metrics.anthropic.keys.with_raw_response.list_agents( + uuid="", + ) + + +class TestAsyncKeys: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.create() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.create( + api_key='"sk-ant-12345678901234567890123456789012"', + name='"Production Key"', + ) + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + api_key='"sk-ant-12345678901234567890123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.list() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.list( + page=0, + per_page=0, + ) + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_agents(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_agents_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.anthropic.keys.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_agents(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_agents(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.anthropic.keys.with_streaming_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_agents(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.agents.evaluation_metrics.anthropic.keys.with_raw_response.list_agents( + uuid="", + ) diff --git a/tests/api_resources/agents/evaluation_metrics/oauth2/__init__.py b/tests/api_resources/agents/evaluation_metrics/oauth2/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/oauth2/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/agents/evaluation_metrics/oauth2/test_dropbox.py b/tests/api_resources/agents/evaluation_metrics/oauth2/test_dropbox.py new file mode 100644 index 00000000..417bb3b1 --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/oauth2/test_dropbox.py @@ -0,0 +1,100 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents.evaluation_metrics.oauth2 import DropboxCreateTokensResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDropbox: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_tokens(self, client: Gradient) -> None: + dropbox = client.agents.evaluation_metrics.oauth2.dropbox.create_tokens() + assert_matches_type(DropboxCreateTokensResponse, dropbox, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_tokens_with_all_params(self, client: Gradient) -> None: + dropbox = client.agents.evaluation_metrics.oauth2.dropbox.create_tokens( + code="example string", + redirect_url="example string", + ) + assert_matches_type(DropboxCreateTokensResponse, dropbox, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_tokens(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.oauth2.dropbox.with_raw_response.create_tokens() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + dropbox = response.parse() + assert_matches_type(DropboxCreateTokensResponse, dropbox, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_tokens(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.oauth2.dropbox.with_streaming_response.create_tokens() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + dropbox = response.parse() + assert_matches_type(DropboxCreateTokensResponse, dropbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDropbox: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_tokens(self, async_client: AsyncGradient) -> None: + dropbox = await async_client.agents.evaluation_metrics.oauth2.dropbox.create_tokens() + assert_matches_type(DropboxCreateTokensResponse, dropbox, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_tokens_with_all_params(self, async_client: AsyncGradient) -> None: + dropbox = await async_client.agents.evaluation_metrics.oauth2.dropbox.create_tokens( + code="example string", + redirect_url="example string", + ) + assert_matches_type(DropboxCreateTokensResponse, dropbox, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_tokens(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.oauth2.dropbox.with_raw_response.create_tokens() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + dropbox = await response.parse() + assert_matches_type(DropboxCreateTokensResponse, dropbox, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_tokens(self, async_client: AsyncGradient) -> None: + async with ( + async_client.agents.evaluation_metrics.oauth2.dropbox.with_streaming_response.create_tokens() + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + dropbox = await response.parse() + assert_matches_type(DropboxCreateTokensResponse, dropbox, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/agents/evaluation_metrics/openai/__init__.py b/tests/api_resources/agents/evaluation_metrics/openai/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/openai/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/agents/evaluation_metrics/openai/test_keys.py b/tests/api_resources/agents/evaluation_metrics/openai/test_keys.py new file mode 100644 index 00000000..7da165c2 --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/openai/test_keys.py @@ -0,0 +1,557 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents.evaluation_metrics.openai import ( + KeyListResponse, + KeyCreateResponse, + KeyDeleteResponse, + KeyUpdateResponse, + KeyRetrieveResponse, + KeyListAgentsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestKeys: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.create() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.create( + api_key='"sk-proj--123456789098765432123456789"', + name='"Production Key"', + ) + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.openai.keys.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.openai.keys.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.openai.keys.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.openai.keys.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.agents.evaluation_metrics.openai.keys.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + api_key='"sk-ant-12345678901234567890123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.openai.keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.openai.keys.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + client.agents.evaluation_metrics.openai.keys.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.list() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.list( + page=0, + per_page=0, + ) + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.openai.keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.openai.keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.openai.keys.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.openai.keys.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.agents.evaluation_metrics.openai.keys.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_agents(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_agents_with_all_params(self, client: Gradient) -> None: + key = client.agents.evaluation_metrics.openai.keys.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_agents(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.openai.keys.with_raw_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_agents(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.openai.keys.with_streaming_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_agents(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.agents.evaluation_metrics.openai.keys.with_raw_response.list_agents( + uuid="", + ) + + +class TestAsyncKeys: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.create() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.create( + api_key='"sk-proj--123456789098765432123456789"', + name='"Production Key"', + ) + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.openai.keys.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.openai.keys.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + api_key='"sk-ant-12345678901234567890123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.openai.keys.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.list() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.list( + page=0, + per_page=0, + ) + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.openai.keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.openai.keys.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyDeleteResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_agents(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_agents_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.agents.evaluation_metrics.openai.keys.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_agents(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_agents(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.openai.keys.with_streaming_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyListAgentsResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_agents(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.agents.evaluation_metrics.openai.keys.with_raw_response.list_agents( + uuid="", + ) diff --git a/tests/api_resources/agents/evaluation_metrics/test_oauth2.py b/tests/api_resources/agents/evaluation_metrics/test_oauth2.py new file mode 100644 index 00000000..f247d94f --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/test_oauth2.py @@ -0,0 +1,98 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents.evaluation_metrics import Oauth2GenerateURLResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestOauth2: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_generate_url(self, client: Gradient) -> None: + oauth2 = client.agents.evaluation_metrics.oauth2.generate_url() + assert_matches_type(Oauth2GenerateURLResponse, oauth2, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_generate_url_with_all_params(self, client: Gradient) -> None: + oauth2 = client.agents.evaluation_metrics.oauth2.generate_url( + redirect_url="redirect_url", + type="type", + ) + assert_matches_type(Oauth2GenerateURLResponse, oauth2, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_generate_url(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.oauth2.with_raw_response.generate_url() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + oauth2 = response.parse() + assert_matches_type(Oauth2GenerateURLResponse, oauth2, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_generate_url(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.oauth2.with_streaming_response.generate_url() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + oauth2 = response.parse() + assert_matches_type(Oauth2GenerateURLResponse, oauth2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncOauth2: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_generate_url(self, async_client: AsyncGradient) -> None: + oauth2 = await async_client.agents.evaluation_metrics.oauth2.generate_url() + assert_matches_type(Oauth2GenerateURLResponse, oauth2, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_generate_url_with_all_params(self, async_client: AsyncGradient) -> None: + oauth2 = await async_client.agents.evaluation_metrics.oauth2.generate_url( + redirect_url="redirect_url", + type="type", + ) + assert_matches_type(Oauth2GenerateURLResponse, oauth2, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_generate_url(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.oauth2.with_raw_response.generate_url() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + oauth2 = await response.parse() + assert_matches_type(Oauth2GenerateURLResponse, oauth2, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_generate_url(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.oauth2.with_streaming_response.generate_url() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + oauth2 = await response.parse() + assert_matches_type(Oauth2GenerateURLResponse, oauth2, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/agents/evaluation_metrics/test_scheduled_indexing.py b/tests/api_resources/agents/evaluation_metrics/test_scheduled_indexing.py new file mode 100644 index 00000000..388e06c9 --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/test_scheduled_indexing.py @@ -0,0 +1,274 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents.evaluation_metrics import ( + ScheduledIndexingCreateResponse, + ScheduledIndexingDeleteResponse, + ScheduledIndexingRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestScheduledIndexing: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + scheduled_indexing = client.agents.evaluation_metrics.scheduled_indexing.create() + assert_matches_type(ScheduledIndexingCreateResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + scheduled_indexing = client.agents.evaluation_metrics.scheduled_indexing.create( + days=[123], + knowledge_base_uuid="123e4567-e89b-12d3-a456-426614174000", + time="example string", + ) + assert_matches_type(ScheduledIndexingCreateResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scheduled_indexing = response.parse() + assert_matches_type(ScheduledIndexingCreateResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.scheduled_indexing.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scheduled_indexing = response.parse() + assert_matches_type(ScheduledIndexingCreateResponse, scheduled_indexing, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + scheduled_indexing = client.agents.evaluation_metrics.scheduled_indexing.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(ScheduledIndexingRetrieveResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scheduled_indexing = response.parse() + assert_matches_type(ScheduledIndexingRetrieveResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.scheduled_indexing.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scheduled_indexing = response.parse() + assert_matches_type(ScheduledIndexingRetrieveResponse, scheduled_indexing, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + scheduled_indexing = client.agents.evaluation_metrics.scheduled_indexing.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(ScheduledIndexingDeleteResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scheduled_indexing = response.parse() + assert_matches_type(ScheduledIndexingDeleteResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.scheduled_indexing.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scheduled_indexing = response.parse() + assert_matches_type(ScheduledIndexingDeleteResponse, scheduled_indexing, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.delete( + "", + ) + + +class TestAsyncScheduledIndexing: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + scheduled_indexing = await async_client.agents.evaluation_metrics.scheduled_indexing.create() + assert_matches_type(ScheduledIndexingCreateResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + scheduled_indexing = await async_client.agents.evaluation_metrics.scheduled_indexing.create( + days=[123], + knowledge_base_uuid="123e4567-e89b-12d3-a456-426614174000", + time="example string", + ) + assert_matches_type(ScheduledIndexingCreateResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scheduled_indexing = await response.parse() + assert_matches_type(ScheduledIndexingCreateResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with ( + async_client.agents.evaluation_metrics.scheduled_indexing.with_streaming_response.create() + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scheduled_indexing = await response.parse() + assert_matches_type(ScheduledIndexingCreateResponse, scheduled_indexing, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + scheduled_indexing = await async_client.agents.evaluation_metrics.scheduled_indexing.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(ScheduledIndexingRetrieveResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scheduled_indexing = await response.parse() + assert_matches_type(ScheduledIndexingRetrieveResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.scheduled_indexing.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scheduled_indexing = await response.parse() + assert_matches_type(ScheduledIndexingRetrieveResponse, scheduled_indexing, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + await async_client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + scheduled_indexing = await async_client.agents.evaluation_metrics.scheduled_indexing.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(ScheduledIndexingDeleteResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scheduled_indexing = await response.parse() + assert_matches_type(ScheduledIndexingDeleteResponse, scheduled_indexing, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.scheduled_indexing.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scheduled_indexing = await response.parse() + assert_matches_type(ScheduledIndexingDeleteResponse, scheduled_indexing, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.agents.evaluation_metrics.scheduled_indexing.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/agents/evaluation_metrics/test_workspaces.py b/tests/api_resources/agents/evaluation_metrics/test_workspaces.py new file mode 100644 index 00000000..4f85212d --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/test_workspaces.py @@ -0,0 +1,521 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents.evaluation_metrics import ( + WorkspaceListResponse, + WorkspaceCreateResponse, + WorkspaceDeleteResponse, + WorkspaceUpdateResponse, + WorkspaceRetrieveResponse, + WorkspaceListEvaluationTestCasesResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestWorkspaces: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + workspace = client.agents.evaluation_metrics.workspaces.create() + assert_matches_type(WorkspaceCreateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + workspace = client.agents.evaluation_metrics.workspaces.create( + agent_uuids=["example string"], + description="example string", + name="example name", + ) + assert_matches_type(WorkspaceCreateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.workspaces.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = response.parse() + assert_matches_type(WorkspaceCreateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.workspaces.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = response.parse() + assert_matches_type(WorkspaceCreateResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + workspace = client.agents.evaluation_metrics.workspaces.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.workspaces.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = response.parse() + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.workspaces.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = response.parse() + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_uuid` but received ''"): + client.agents.evaluation_metrics.workspaces.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + workspace = client.agents.evaluation_metrics.workspaces.update( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(WorkspaceUpdateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + workspace = client.agents.evaluation_metrics.workspaces.update( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + description="example string", + name="example name", + body_workspace_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(WorkspaceUpdateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.workspaces.with_raw_response.update( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = response.parse() + assert_matches_type(WorkspaceUpdateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.workspaces.with_streaming_response.update( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = response.parse() + assert_matches_type(WorkspaceUpdateResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_workspace_uuid` but received ''"): + client.agents.evaluation_metrics.workspaces.with_raw_response.update( + path_workspace_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + workspace = client.agents.evaluation_metrics.workspaces.list() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.workspaces.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = response.parse() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.workspaces.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = response.parse() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + workspace = client.agents.evaluation_metrics.workspaces.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(WorkspaceDeleteResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.workspaces.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = response.parse() + assert_matches_type(WorkspaceDeleteResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.workspaces.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = response.parse() + assert_matches_type(WorkspaceDeleteResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_uuid` but received ''"): + client.agents.evaluation_metrics.workspaces.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_evaluation_test_cases(self, client: Gradient) -> None: + workspace = client.agents.evaluation_metrics.workspaces.list_evaluation_test_cases( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(WorkspaceListEvaluationTestCasesResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_evaluation_test_cases(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.workspaces.with_raw_response.list_evaluation_test_cases( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = response.parse() + assert_matches_type(WorkspaceListEvaluationTestCasesResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_evaluation_test_cases(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.workspaces.with_streaming_response.list_evaluation_test_cases( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = response.parse() + assert_matches_type(WorkspaceListEvaluationTestCasesResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_evaluation_test_cases(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_uuid` but received ''"): + client.agents.evaluation_metrics.workspaces.with_raw_response.list_evaluation_test_cases( + "", + ) + + +class TestAsyncWorkspaces: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + workspace = await async_client.agents.evaluation_metrics.workspaces.create() + assert_matches_type(WorkspaceCreateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + workspace = await async_client.agents.evaluation_metrics.workspaces.create( + agent_uuids=["example string"], + description="example string", + name="example name", + ) + assert_matches_type(WorkspaceCreateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.workspaces.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = await response.parse() + assert_matches_type(WorkspaceCreateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.workspaces.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = await response.parse() + assert_matches_type(WorkspaceCreateResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + workspace = await async_client.agents.evaluation_metrics.workspaces.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.workspaces.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = await response.parse() + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.workspaces.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = await response.parse() + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_uuid` but received ''"): + await async_client.agents.evaluation_metrics.workspaces.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + workspace = await async_client.agents.evaluation_metrics.workspaces.update( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(WorkspaceUpdateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + workspace = await async_client.agents.evaluation_metrics.workspaces.update( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + description="example string", + name="example name", + body_workspace_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(WorkspaceUpdateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.workspaces.with_raw_response.update( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = await response.parse() + assert_matches_type(WorkspaceUpdateResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.workspaces.with_streaming_response.update( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = await response.parse() + assert_matches_type(WorkspaceUpdateResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_workspace_uuid` but received ''"): + await async_client.agents.evaluation_metrics.workspaces.with_raw_response.update( + path_workspace_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + workspace = await async_client.agents.evaluation_metrics.workspaces.list() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.workspaces.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = await response.parse() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.workspaces.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = await response.parse() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + workspace = await async_client.agents.evaluation_metrics.workspaces.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(WorkspaceDeleteResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.workspaces.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = await response.parse() + assert_matches_type(WorkspaceDeleteResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.workspaces.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = await response.parse() + assert_matches_type(WorkspaceDeleteResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_uuid` but received ''"): + await async_client.agents.evaluation_metrics.workspaces.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_evaluation_test_cases(self, async_client: AsyncGradient) -> None: + workspace = await async_client.agents.evaluation_metrics.workspaces.list_evaluation_test_cases( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(WorkspaceListEvaluationTestCasesResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_evaluation_test_cases(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.workspaces.with_raw_response.list_evaluation_test_cases( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = await response.parse() + assert_matches_type(WorkspaceListEvaluationTestCasesResponse, workspace, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_evaluation_test_cases(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.workspaces.with_streaming_response.list_evaluation_test_cases( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = await response.parse() + assert_matches_type(WorkspaceListEvaluationTestCasesResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_evaluation_test_cases(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_uuid` but received ''"): + await async_client.agents.evaluation_metrics.workspaces.with_raw_response.list_evaluation_test_cases( + "", + ) diff --git a/tests/api_resources/agents/evaluation_metrics/workspaces/__init__.py b/tests/api_resources/agents/evaluation_metrics/workspaces/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/workspaces/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/agents/evaluation_metrics/workspaces/test_agents.py b/tests/api_resources/agents/evaluation_metrics/workspaces/test_agents.py new file mode 100644 index 00000000..4154843c --- /dev/null +++ b/tests/api_resources/agents/evaluation_metrics/workspaces/test_agents.py @@ -0,0 +1,237 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents.evaluation_metrics.workspaces import ( + AgentListResponse, + AgentMoveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAgents: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + agent = client.agents.evaluation_metrics.workspaces.agents.list( + workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + agent = client.agents.evaluation_metrics.workspaces.agents.list( + workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + only_deployed=True, + page=0, + per_page=0, + ) + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.workspaces.agents.with_raw_response.list( + workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.workspaces.agents.with_streaming_response.list( + workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_uuid` but received ''"): + client.agents.evaluation_metrics.workspaces.agents.with_raw_response.list( + workspace_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_move(self, client: Gradient) -> None: + agent = client.agents.evaluation_metrics.workspaces.agents.move( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentMoveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_move_with_all_params(self, client: Gradient) -> None: + agent = client.agents.evaluation_metrics.workspaces.agents.move( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuids=["example string"], + body_workspace_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(AgentMoveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_move(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.workspaces.agents.with_raw_response.move( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentMoveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_move(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.workspaces.agents.with_streaming_response.move( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentMoveResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_move(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_workspace_uuid` but received ''"): + client.agents.evaluation_metrics.workspaces.agents.with_raw_response.move( + path_workspace_uuid="", + ) + + +class TestAsyncAgents: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.evaluation_metrics.workspaces.agents.list( + workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.evaluation_metrics.workspaces.agents.list( + workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + only_deployed=True, + page=0, + per_page=0, + ) + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.workspaces.agents.with_raw_response.list( + workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.workspaces.agents.with_streaming_response.list( + workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_uuid` but received ''"): + await async_client.agents.evaluation_metrics.workspaces.agents.with_raw_response.list( + workspace_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_move(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.evaluation_metrics.workspaces.agents.move( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentMoveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_move_with_all_params(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.evaluation_metrics.workspaces.agents.move( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuids=["example string"], + body_workspace_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(AgentMoveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_move(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.workspaces.agents.with_raw_response.move( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentMoveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_move(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.workspaces.agents.with_streaming_response.move( + path_workspace_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentMoveResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_move(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_workspace_uuid` but received ''"): + await async_client.agents.evaluation_metrics.workspaces.agents.with_raw_response.move( + path_workspace_uuid="", + ) diff --git a/tests/api_resources/agents/test_api_keys.py b/tests/api_resources/agents/test_api_keys.py new file mode 100644 index 00000000..dbb19890 --- /dev/null +++ b/tests/api_resources/agents/test_api_keys.py @@ -0,0 +1,574 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import ( + APIKeyListResponse, + APIKeyCreateResponse, + APIKeyDeleteResponse, + APIKeyUpdateResponse, + APIKeyRegenerateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAPIKeys: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + api_key = client.agents.api_keys.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + api_key = client.agents.api_keys.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_agent_uuid='"12345678-1234-1234-1234-123456789012"', + name="Production Key", + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.api_keys.with_raw_response.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.api_keys.with_streaming_response.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_create(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_agent_uuid` but received ''"): + client.agents.api_keys.with_raw_response.create( + path_agent_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + api_key = client.agents.api_keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + api_key = client.agents.api_keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_agent_uuid='"12345678-1234-1234-1234-123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.api_keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.api_keys.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_agent_uuid` but received ''"): + client.agents.api_keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + client.agents.api_keys.with_raw_response.update( + path_api_key_uuid="", + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + api_key = client.agents.api_keys.list( + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + api_key = client.agents.api_keys.list( + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.api_keys.with_raw_response.list( + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.api_keys.with_streaming_response.list( + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + client.agents.api_keys.with_raw_response.list( + agent_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + api_key = client.agents.api_keys.delete( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.agents.api_keys.with_raw_response.delete( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.agents.api_keys.with_streaming_response.delete( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + client.agents.api_keys.with_raw_response.delete( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.agents.api_keys.with_raw_response.delete( + api_key_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_regenerate(self, client: Gradient) -> None: + api_key = client.agents.api_keys.regenerate( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyRegenerateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_regenerate(self, client: Gradient) -> None: + response = client.agents.api_keys.with_raw_response.regenerate( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyRegenerateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_regenerate(self, client: Gradient) -> None: + with client.agents.api_keys.with_streaming_response.regenerate( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyRegenerateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_regenerate(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + client.agents.api_keys.with_raw_response.regenerate( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.agents.api_keys.with_raw_response.regenerate( + api_key_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + +class TestAsyncAPIKeys: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + api_key = await async_client.agents.api_keys.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + api_key = await async_client.agents.api_keys.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_agent_uuid='"12345678-1234-1234-1234-123456789012"', + name="Production Key", + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.api_keys.with_raw_response.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.api_keys.with_streaming_response.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_create(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_agent_uuid` but received ''"): + await async_client.agents.api_keys.with_raw_response.create( + path_agent_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + api_key = await async_client.agents.api_keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + api_key = await async_client.agents.api_keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_agent_uuid='"12345678-1234-1234-1234-123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.api_keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.api_keys.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_agent_uuid` but received ''"): + await async_client.agents.api_keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + await async_client.agents.api_keys.with_raw_response.update( + path_api_key_uuid="", + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + api_key = await async_client.agents.api_keys.list( + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + api_key = await async_client.agents.api_keys.list( + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.api_keys.with_raw_response.list( + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.api_keys.with_streaming_response.list( + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + await async_client.agents.api_keys.with_raw_response.list( + agent_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + api_key = await async_client.agents.api_keys.delete( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.api_keys.with_raw_response.delete( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.agents.api_keys.with_streaming_response.delete( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + await async_client.agents.api_keys.with_raw_response.delete( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.agents.api_keys.with_raw_response.delete( + api_key_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_regenerate(self, async_client: AsyncGradient) -> None: + api_key = await async_client.agents.api_keys.regenerate( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyRegenerateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_regenerate(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.api_keys.with_raw_response.regenerate( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyRegenerateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_regenerate(self, async_client: AsyncGradient) -> None: + async with async_client.agents.api_keys.with_streaming_response.regenerate( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyRegenerateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_regenerate(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + await async_client.agents.api_keys.with_raw_response.regenerate( + api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.agents.api_keys.with_raw_response.regenerate( + api_key_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) diff --git a/tests/api_resources/agents/test_evaluation_datasets.py b/tests/api_resources/agents/test_evaluation_datasets.py new file mode 100644 index 00000000..64dceb03 --- /dev/null +++ b/tests/api_resources/agents/test_evaluation_datasets.py @@ -0,0 +1,209 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import ( + EvaluationDatasetCreateResponse, + EvaluationDatasetCreateFileUploadPresignedURLsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEvaluationDatasets: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + evaluation_dataset = client.agents.evaluation_datasets.create() + assert_matches_type(EvaluationDatasetCreateResponse, evaluation_dataset, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + evaluation_dataset = client.agents.evaluation_datasets.create( + file_upload_dataset={ + "original_file_name": "example name", + "size_in_bytes": "12345", + "stored_object_key": "example string", + }, + name="example name", + ) + assert_matches_type(EvaluationDatasetCreateResponse, evaluation_dataset, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.evaluation_datasets.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_dataset = response.parse() + assert_matches_type(EvaluationDatasetCreateResponse, evaluation_dataset, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.evaluation_datasets.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_dataset = response.parse() + assert_matches_type(EvaluationDatasetCreateResponse, evaluation_dataset, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_file_upload_presigned_urls(self, client: Gradient) -> None: + evaluation_dataset = client.agents.evaluation_datasets.create_file_upload_presigned_urls() + assert_matches_type( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, evaluation_dataset, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_file_upload_presigned_urls_with_all_params(self, client: Gradient) -> None: + evaluation_dataset = client.agents.evaluation_datasets.create_file_upload_presigned_urls( + files=[ + { + "file_name": "example name", + "file_size": "file_size", + } + ], + ) + assert_matches_type( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, evaluation_dataset, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_file_upload_presigned_urls(self, client: Gradient) -> None: + response = client.agents.evaluation_datasets.with_raw_response.create_file_upload_presigned_urls() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_dataset = response.parse() + assert_matches_type( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, evaluation_dataset, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_file_upload_presigned_urls(self, client: Gradient) -> None: + with client.agents.evaluation_datasets.with_streaming_response.create_file_upload_presigned_urls() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_dataset = response.parse() + assert_matches_type( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, evaluation_dataset, path=["response"] + ) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncEvaluationDatasets: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + evaluation_dataset = await async_client.agents.evaluation_datasets.create() + assert_matches_type(EvaluationDatasetCreateResponse, evaluation_dataset, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_dataset = await async_client.agents.evaluation_datasets.create( + file_upload_dataset={ + "original_file_name": "example name", + "size_in_bytes": "12345", + "stored_object_key": "example string", + }, + name="example name", + ) + assert_matches_type(EvaluationDatasetCreateResponse, evaluation_dataset, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_datasets.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_dataset = await response.parse() + assert_matches_type(EvaluationDatasetCreateResponse, evaluation_dataset, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_datasets.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_dataset = await response.parse() + assert_matches_type(EvaluationDatasetCreateResponse, evaluation_dataset, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_file_upload_presigned_urls(self, async_client: AsyncGradient) -> None: + evaluation_dataset = await async_client.agents.evaluation_datasets.create_file_upload_presigned_urls() + assert_matches_type( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, evaluation_dataset, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_file_upload_presigned_urls_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_dataset = await async_client.agents.evaluation_datasets.create_file_upload_presigned_urls( + files=[ + { + "file_name": "example name", + "file_size": "file_size", + } + ], + ) + assert_matches_type( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, evaluation_dataset, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_file_upload_presigned_urls(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_datasets.with_raw_response.create_file_upload_presigned_urls() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_dataset = await response.parse() + assert_matches_type( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, evaluation_dataset, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_file_upload_presigned_urls(self, async_client: AsyncGradient) -> None: + async with ( + async_client.agents.evaluation_datasets.with_streaming_response.create_file_upload_presigned_urls() + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_dataset = await response.parse() + assert_matches_type( + EvaluationDatasetCreateFileUploadPresignedURLsResponse, evaluation_dataset, path=["response"] + ) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/agents/test_evaluation_metrics.py b/tests/api_resources/agents/test_evaluation_metrics.py new file mode 100644 index 00000000..088353bb --- /dev/null +++ b/tests/api_resources/agents/test_evaluation_metrics.py @@ -0,0 +1,157 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import ( + EvaluationMetricListResponse, + EvaluationMetricListRegionsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEvaluationMetrics: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + evaluation_metric = client.agents.evaluation_metrics.list() + assert_matches_type(EvaluationMetricListResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_metric = response.parse() + assert_matches_type(EvaluationMetricListResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_metric = response.parse() + assert_matches_type(EvaluationMetricListResponse, evaluation_metric, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_regions(self, client: Gradient) -> None: + evaluation_metric = client.agents.evaluation_metrics.list_regions() + assert_matches_type(EvaluationMetricListRegionsResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_regions_with_all_params(self, client: Gradient) -> None: + evaluation_metric = client.agents.evaluation_metrics.list_regions( + serves_batch=True, + serves_inference=True, + ) + assert_matches_type(EvaluationMetricListRegionsResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_regions(self, client: Gradient) -> None: + response = client.agents.evaluation_metrics.with_raw_response.list_regions() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_metric = response.parse() + assert_matches_type(EvaluationMetricListRegionsResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_regions(self, client: Gradient) -> None: + with client.agents.evaluation_metrics.with_streaming_response.list_regions() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_metric = response.parse() + assert_matches_type(EvaluationMetricListRegionsResponse, evaluation_metric, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncEvaluationMetrics: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + evaluation_metric = await async_client.agents.evaluation_metrics.list() + assert_matches_type(EvaluationMetricListResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_metric = await response.parse() + assert_matches_type(EvaluationMetricListResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_metric = await response.parse() + assert_matches_type(EvaluationMetricListResponse, evaluation_metric, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_regions(self, async_client: AsyncGradient) -> None: + evaluation_metric = await async_client.agents.evaluation_metrics.list_regions() + assert_matches_type(EvaluationMetricListRegionsResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_regions_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_metric = await async_client.agents.evaluation_metrics.list_regions( + serves_batch=True, + serves_inference=True, + ) + assert_matches_type(EvaluationMetricListRegionsResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_regions(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_metrics.with_raw_response.list_regions() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_metric = await response.parse() + assert_matches_type(EvaluationMetricListRegionsResponse, evaluation_metric, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_regions(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_metrics.with_streaming_response.list_regions() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_metric = await response.parse() + assert_matches_type(EvaluationMetricListRegionsResponse, evaluation_metric, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/agents/test_evaluation_runs.py b/tests/api_resources/agents/test_evaluation_runs.py new file mode 100644 index 00000000..c6acaf82 --- /dev/null +++ b/tests/api_resources/agents/test_evaluation_runs.py @@ -0,0 +1,385 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import ( + EvaluationRunCreateResponse, + EvaluationRunRetrieveResponse, + EvaluationRunListResultsResponse, + EvaluationRunRetrieveResultsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEvaluationRuns: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + evaluation_run = client.agents.evaluation_runs.create() + assert_matches_type(EvaluationRunCreateResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + evaluation_run = client.agents.evaluation_runs.create( + agent_uuids=["example string"], + run_name="Evaluation Run Name", + test_case_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(EvaluationRunCreateResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.evaluation_runs.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_run = response.parse() + assert_matches_type(EvaluationRunCreateResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.evaluation_runs.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_run = response.parse() + assert_matches_type(EvaluationRunCreateResponse, evaluation_run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + evaluation_run = client.agents.evaluation_runs.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationRunRetrieveResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.agents.evaluation_runs.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_run = response.parse() + assert_matches_type(EvaluationRunRetrieveResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.agents.evaluation_runs.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_run = response.parse() + assert_matches_type(EvaluationRunRetrieveResponse, evaluation_run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `evaluation_run_uuid` but received ''"): + client.agents.evaluation_runs.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_results(self, client: Gradient) -> None: + evaluation_run = client.agents.evaluation_runs.list_results( + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationRunListResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_results_with_all_params(self, client: Gradient) -> None: + evaluation_run = client.agents.evaluation_runs.list_results( + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(EvaluationRunListResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_results(self, client: Gradient) -> None: + response = client.agents.evaluation_runs.with_raw_response.list_results( + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_run = response.parse() + assert_matches_type(EvaluationRunListResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_results(self, client: Gradient) -> None: + with client.agents.evaluation_runs.with_streaming_response.list_results( + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_run = response.parse() + assert_matches_type(EvaluationRunListResultsResponse, evaluation_run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_results(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `evaluation_run_uuid` but received ''"): + client.agents.evaluation_runs.with_raw_response.list_results( + evaluation_run_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_results(self, client: Gradient) -> None: + evaluation_run = client.agents.evaluation_runs.retrieve_results( + prompt_id=1, + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationRunRetrieveResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve_results(self, client: Gradient) -> None: + response = client.agents.evaluation_runs.with_raw_response.retrieve_results( + prompt_id=1, + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_run = response.parse() + assert_matches_type(EvaluationRunRetrieveResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve_results(self, client: Gradient) -> None: + with client.agents.evaluation_runs.with_streaming_response.retrieve_results( + prompt_id=1, + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_run = response.parse() + assert_matches_type(EvaluationRunRetrieveResultsResponse, evaluation_run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve_results(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `evaluation_run_uuid` but received ''"): + client.agents.evaluation_runs.with_raw_response.retrieve_results( + prompt_id=1, + evaluation_run_uuid="", + ) + + +class TestAsyncEvaluationRuns: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + evaluation_run = await async_client.agents.evaluation_runs.create() + assert_matches_type(EvaluationRunCreateResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_run = await async_client.agents.evaluation_runs.create( + agent_uuids=["example string"], + run_name="Evaluation Run Name", + test_case_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(EvaluationRunCreateResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_runs.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_run = await response.parse() + assert_matches_type(EvaluationRunCreateResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_runs.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_run = await response.parse() + assert_matches_type(EvaluationRunCreateResponse, evaluation_run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + evaluation_run = await async_client.agents.evaluation_runs.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationRunRetrieveResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_runs.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_run = await response.parse() + assert_matches_type(EvaluationRunRetrieveResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_runs.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_run = await response.parse() + assert_matches_type(EvaluationRunRetrieveResponse, evaluation_run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `evaluation_run_uuid` but received ''"): + await async_client.agents.evaluation_runs.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_results(self, async_client: AsyncGradient) -> None: + evaluation_run = await async_client.agents.evaluation_runs.list_results( + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationRunListResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_results_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_run = await async_client.agents.evaluation_runs.list_results( + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(EvaluationRunListResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_results(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_runs.with_raw_response.list_results( + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_run = await response.parse() + assert_matches_type(EvaluationRunListResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_results(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_runs.with_streaming_response.list_results( + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_run = await response.parse() + assert_matches_type(EvaluationRunListResultsResponse, evaluation_run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_results(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `evaluation_run_uuid` but received ''"): + await async_client.agents.evaluation_runs.with_raw_response.list_results( + evaluation_run_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_results(self, async_client: AsyncGradient) -> None: + evaluation_run = await async_client.agents.evaluation_runs.retrieve_results( + prompt_id=1, + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationRunRetrieveResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve_results(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_runs.with_raw_response.retrieve_results( + prompt_id=1, + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_run = await response.parse() + assert_matches_type(EvaluationRunRetrieveResultsResponse, evaluation_run, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve_results(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_runs.with_streaming_response.retrieve_results( + prompt_id=1, + evaluation_run_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_run = await response.parse() + assert_matches_type(EvaluationRunRetrieveResultsResponse, evaluation_run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve_results(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `evaluation_run_uuid` but received ''"): + await async_client.agents.evaluation_runs.with_raw_response.retrieve_results( + prompt_id=1, + evaluation_run_uuid="", + ) diff --git a/tests/api_resources/agents/test_evaluation_test_cases.py b/tests/api_resources/agents/test_evaluation_test_cases.py new file mode 100644 index 00000000..7cd0a07e --- /dev/null +++ b/tests/api_resources/agents/test_evaluation_test_cases.py @@ -0,0 +1,508 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import ( + EvaluationTestCaseListResponse, + EvaluationTestCaseCreateResponse, + EvaluationTestCaseUpdateResponse, + EvaluationTestCaseRetrieveResponse, + EvaluationTestCaseListEvaluationRunsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEvaluationTestCases: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.create() + assert_matches_type(EvaluationTestCaseCreateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.create( + dataset_uuid="123e4567-e89b-12d3-a456-426614174000", + description="example string", + metrics=["example string"], + name="example name", + star_metric={ + "metric_uuid": "123e4567-e89b-12d3-a456-426614174000", + "name": "example name", + "success_threshold": 123, + "success_threshold_pct": 123, + }, + workspace_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(EvaluationTestCaseCreateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.evaluation_test_cases.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseCreateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.evaluation_test_cases.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseCreateResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.retrieve( + test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationTestCaseRetrieveResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.retrieve( + test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + evaluation_test_case_version=0, + ) + assert_matches_type(EvaluationTestCaseRetrieveResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.agents.evaluation_test_cases.with_raw_response.retrieve( + test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseRetrieveResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.agents.evaluation_test_cases.with_streaming_response.retrieve( + test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseRetrieveResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `test_case_uuid` but received ''"): + client.agents.evaluation_test_cases.with_raw_response.retrieve( + test_case_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.update( + path_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationTestCaseUpdateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.update( + path_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + dataset_uuid="123e4567-e89b-12d3-a456-426614174000", + description="example string", + metrics={"metric_uuids": ["example string"]}, + name="example name", + star_metric={ + "metric_uuid": "123e4567-e89b-12d3-a456-426614174000", + "name": "example name", + "success_threshold": 123, + "success_threshold_pct": 123, + }, + body_test_case_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(EvaluationTestCaseUpdateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.evaluation_test_cases.with_raw_response.update( + path_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseUpdateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.evaluation_test_cases.with_streaming_response.update( + path_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseUpdateResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_test_case_uuid` but received ''"): + client.agents.evaluation_test_cases.with_raw_response.update( + path_test_case_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.list() + assert_matches_type(EvaluationTestCaseListResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.evaluation_test_cases.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseListResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.evaluation_test_cases.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseListResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_evaluation_runs(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.list_evaluation_runs( + evaluation_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationTestCaseListEvaluationRunsResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_evaluation_runs_with_all_params(self, client: Gradient) -> None: + evaluation_test_case = client.agents.evaluation_test_cases.list_evaluation_runs( + evaluation_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + evaluation_test_case_version=0, + ) + assert_matches_type(EvaluationTestCaseListEvaluationRunsResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_evaluation_runs(self, client: Gradient) -> None: + response = client.agents.evaluation_test_cases.with_raw_response.list_evaluation_runs( + evaluation_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseListEvaluationRunsResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_evaluation_runs(self, client: Gradient) -> None: + with client.agents.evaluation_test_cases.with_streaming_response.list_evaluation_runs( + evaluation_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = response.parse() + assert_matches_type(EvaluationTestCaseListEvaluationRunsResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_evaluation_runs(self, client: Gradient) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `evaluation_test_case_uuid` but received ''" + ): + client.agents.evaluation_test_cases.with_raw_response.list_evaluation_runs( + evaluation_test_case_uuid="", + ) + + +class TestAsyncEvaluationTestCases: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.create() + assert_matches_type(EvaluationTestCaseCreateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.create( + dataset_uuid="123e4567-e89b-12d3-a456-426614174000", + description="example string", + metrics=["example string"], + name="example name", + star_metric={ + "metric_uuid": "123e4567-e89b-12d3-a456-426614174000", + "name": "example name", + "success_threshold": 123, + "success_threshold_pct": 123, + }, + workspace_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(EvaluationTestCaseCreateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_test_cases.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseCreateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_test_cases.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseCreateResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.retrieve( + test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationTestCaseRetrieveResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.retrieve( + test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + evaluation_test_case_version=0, + ) + assert_matches_type(EvaluationTestCaseRetrieveResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_test_cases.with_raw_response.retrieve( + test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseRetrieveResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_test_cases.with_streaming_response.retrieve( + test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseRetrieveResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `test_case_uuid` but received ''"): + await async_client.agents.evaluation_test_cases.with_raw_response.retrieve( + test_case_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.update( + path_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationTestCaseUpdateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.update( + path_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + dataset_uuid="123e4567-e89b-12d3-a456-426614174000", + description="example string", + metrics={"metric_uuids": ["example string"]}, + name="example name", + star_metric={ + "metric_uuid": "123e4567-e89b-12d3-a456-426614174000", + "name": "example name", + "success_threshold": 123, + "success_threshold_pct": 123, + }, + body_test_case_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(EvaluationTestCaseUpdateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_test_cases.with_raw_response.update( + path_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseUpdateResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_test_cases.with_streaming_response.update( + path_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseUpdateResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_test_case_uuid` but received ''"): + await async_client.agents.evaluation_test_cases.with_raw_response.update( + path_test_case_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.list() + assert_matches_type(EvaluationTestCaseListResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_test_cases.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseListResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_test_cases.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseListResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_evaluation_runs(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.list_evaluation_runs( + evaluation_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(EvaluationTestCaseListEvaluationRunsResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_evaluation_runs_with_all_params(self, async_client: AsyncGradient) -> None: + evaluation_test_case = await async_client.agents.evaluation_test_cases.list_evaluation_runs( + evaluation_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + evaluation_test_case_version=0, + ) + assert_matches_type(EvaluationTestCaseListEvaluationRunsResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_evaluation_runs(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.evaluation_test_cases.with_raw_response.list_evaluation_runs( + evaluation_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseListEvaluationRunsResponse, evaluation_test_case, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_evaluation_runs(self, async_client: AsyncGradient) -> None: + async with async_client.agents.evaluation_test_cases.with_streaming_response.list_evaluation_runs( + evaluation_test_case_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + evaluation_test_case = await response.parse() + assert_matches_type(EvaluationTestCaseListEvaluationRunsResponse, evaluation_test_case, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_evaluation_runs(self, async_client: AsyncGradient) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `evaluation_test_case_uuid` but received ''" + ): + await async_client.agents.evaluation_test_cases.with_raw_response.list_evaluation_runs( + evaluation_test_case_uuid="", + ) diff --git a/tests/api_resources/agents/test_functions.py b/tests/api_resources/agents/test_functions.py new file mode 100644 index 00000000..64d55331 --- /dev/null +++ b/tests/api_resources/agents/test_functions.py @@ -0,0 +1,384 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import ( + FunctionCreateResponse, + FunctionDeleteResponse, + FunctionUpdateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestFunctions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + function = client.agents.functions.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(FunctionCreateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + function = client.agents.functions.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_agent_uuid='"12345678-1234-1234-1234-123456789012"', + description='"My Function Description"', + faas_name='"my-function"', + faas_namespace='"default"', + function_name='"My Function"', + input_schema={}, + output_schema={}, + ) + assert_matches_type(FunctionCreateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.functions.with_raw_response.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + function = response.parse() + assert_matches_type(FunctionCreateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.functions.with_streaming_response.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + function = response.parse() + assert_matches_type(FunctionCreateResponse, function, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_create(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_agent_uuid` but received ''"): + client.agents.functions.with_raw_response.create( + path_agent_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + function = client.agents.functions.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(FunctionUpdateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + function = client.agents.functions.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_agent_uuid='"12345678-1234-1234-1234-123456789012"', + description='"My Function Description"', + faas_name='"my-function"', + faas_namespace='"default"', + function_name='"My Function"', + body_function_uuid='"12345678-1234-1234-1234-123456789012"', + input_schema={}, + output_schema={}, + ) + assert_matches_type(FunctionUpdateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.functions.with_raw_response.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + function = response.parse() + assert_matches_type(FunctionUpdateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.functions.with_streaming_response.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + function = response.parse() + assert_matches_type(FunctionUpdateResponse, function, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_agent_uuid` but received ''"): + client.agents.functions.with_raw_response.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_function_uuid` but received ''"): + client.agents.functions.with_raw_response.update( + path_function_uuid="", + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + function = client.agents.functions.delete( + function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(FunctionDeleteResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.agents.functions.with_raw_response.delete( + function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + function = response.parse() + assert_matches_type(FunctionDeleteResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.agents.functions.with_streaming_response.delete( + function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + function = response.parse() + assert_matches_type(FunctionDeleteResponse, function, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + client.agents.functions.with_raw_response.delete( + function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_uuid` but received ''"): + client.agents.functions.with_raw_response.delete( + function_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + +class TestAsyncFunctions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + function = await async_client.agents.functions.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(FunctionCreateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + function = await async_client.agents.functions.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_agent_uuid='"12345678-1234-1234-1234-123456789012"', + description='"My Function Description"', + faas_name='"my-function"', + faas_namespace='"default"', + function_name='"My Function"', + input_schema={}, + output_schema={}, + ) + assert_matches_type(FunctionCreateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.functions.with_raw_response.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + function = await response.parse() + assert_matches_type(FunctionCreateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.functions.with_streaming_response.create( + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + function = await response.parse() + assert_matches_type(FunctionCreateResponse, function, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_create(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_agent_uuid` but received ''"): + await async_client.agents.functions.with_raw_response.create( + path_agent_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + function = await async_client.agents.functions.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(FunctionUpdateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + function = await async_client.agents.functions.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_agent_uuid='"12345678-1234-1234-1234-123456789012"', + description='"My Function Description"', + faas_name='"my-function"', + faas_namespace='"default"', + function_name='"My Function"', + body_function_uuid='"12345678-1234-1234-1234-123456789012"', + input_schema={}, + output_schema={}, + ) + assert_matches_type(FunctionUpdateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.functions.with_raw_response.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + function = await response.parse() + assert_matches_type(FunctionUpdateResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.functions.with_streaming_response.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + function = await response.parse() + assert_matches_type(FunctionUpdateResponse, function, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_agent_uuid` but received ''"): + await async_client.agents.functions.with_raw_response.update( + path_function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_function_uuid` but received ''"): + await async_client.agents.functions.with_raw_response.update( + path_function_uuid="", + path_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + function = await async_client.agents.functions.delete( + function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(FunctionDeleteResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.functions.with_raw_response.delete( + function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + function = await response.parse() + assert_matches_type(FunctionDeleteResponse, function, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.agents.functions.with_streaming_response.delete( + function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + function = await response.parse() + assert_matches_type(FunctionDeleteResponse, function, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + await async_client.agents.functions.with_raw_response.delete( + function_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_uuid` but received ''"): + await async_client.agents.functions.with_raw_response.delete( + function_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) diff --git a/tests/api_resources/agents/test_knowledge_bases.py b/tests/api_resources/agents/test_knowledge_bases.py new file mode 100644 index 00000000..2cf09753 --- /dev/null +++ b/tests/api_resources/agents/test_knowledge_bases.py @@ -0,0 +1,316 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import APILinkKnowledgeBaseOutput, KnowledgeBaseDetachResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestKnowledgeBases: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_attach(self, client: Gradient) -> None: + knowledge_base = client.agents.knowledge_bases.attach( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_attach(self, client: Gradient) -> None: + response = client.agents.knowledge_bases.with_raw_response.attach( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_attach(self, client: Gradient) -> None: + with client.agents.knowledge_bases.with_streaming_response.attach( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_attach(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + client.agents.knowledge_bases.with_raw_response.attach( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_attach_single(self, client: Gradient) -> None: + knowledge_base = client.agents.knowledge_bases.attach_single( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_attach_single(self, client: Gradient) -> None: + response = client.agents.knowledge_bases.with_raw_response.attach_single( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_attach_single(self, client: Gradient) -> None: + with client.agents.knowledge_bases.with_streaming_response.attach_single( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_attach_single(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + client.agents.knowledge_bases.with_raw_response.attach_single( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + client.agents.knowledge_bases.with_raw_response.attach_single( + knowledge_base_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_detach(self, client: Gradient) -> None: + knowledge_base = client.agents.knowledge_bases.detach( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseDetachResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_detach(self, client: Gradient) -> None: + response = client.agents.knowledge_bases.with_raw_response.detach( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseDetachResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_detach(self, client: Gradient) -> None: + with client.agents.knowledge_bases.with_streaming_response.detach( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseDetachResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_detach(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + client.agents.knowledge_bases.with_raw_response.detach( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + client.agents.knowledge_bases.with_raw_response.detach( + knowledge_base_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + +class TestAsyncKnowledgeBases: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_attach(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.agents.knowledge_bases.attach( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_attach(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.knowledge_bases.with_raw_response.attach( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_attach(self, async_client: AsyncGradient) -> None: + async with async_client.agents.knowledge_bases.with_streaming_response.attach( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_attach(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + await async_client.agents.knowledge_bases.with_raw_response.attach( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_attach_single(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.agents.knowledge_bases.attach_single( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_attach_single(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.knowledge_bases.with_raw_response.attach_single( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_attach_single(self, async_client: AsyncGradient) -> None: + async with async_client.agents.knowledge_bases.with_streaming_response.attach_single( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(APILinkKnowledgeBaseOutput, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_attach_single(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + await async_client.agents.knowledge_bases.with_raw_response.attach_single( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + await async_client.agents.knowledge_bases.with_raw_response.attach_single( + knowledge_base_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_detach(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.agents.knowledge_bases.detach( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseDetachResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_detach(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.knowledge_bases.with_raw_response.detach( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseDetachResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_detach(self, async_client: AsyncGradient) -> None: + async with async_client.agents.knowledge_bases.with_streaming_response.detach( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseDetachResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_detach(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_uuid` but received ''"): + await async_client.agents.knowledge_bases.with_raw_response.detach( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + await async_client.agents.knowledge_bases.with_raw_response.detach( + knowledge_base_uuid="", + agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) diff --git a/tests/api_resources/agents/test_routes.py b/tests/api_resources/agents/test_routes.py new file mode 100644 index 00000000..3444dcc7 --- /dev/null +++ b/tests/api_resources/agents/test_routes.py @@ -0,0 +1,487 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import ( + RouteAddResponse, + RouteViewResponse, + RouteDeleteResponse, + RouteUpdateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRoutes: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + route = client.agents.routes.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(RouteUpdateResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + route = client.agents.routes.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_child_agent_uuid='"12345678-1234-1234-1234-123456789012"', + if_case='"use this to get weather information"', + body_parent_agent_uuid='"12345678-1234-1234-1234-123456789012"', + route_name='"weather_route"', + uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(RouteUpdateResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.routes.with_raw_response.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + route = response.parse() + assert_matches_type(RouteUpdateResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.routes.with_streaming_response.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + route = response.parse() + assert_matches_type(RouteUpdateResponse, route, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `path_parent_agent_uuid` but received ''" + ): + client.agents.routes.with_raw_response.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_child_agent_uuid` but received ''"): + client.agents.routes.with_raw_response.update( + path_child_agent_uuid="", + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + route = client.agents.routes.delete( + child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(RouteDeleteResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.agents.routes.with_raw_response.delete( + child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + route = response.parse() + assert_matches_type(RouteDeleteResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.agents.routes.with_streaming_response.delete( + child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + route = response.parse() + assert_matches_type(RouteDeleteResponse, route, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `parent_agent_uuid` but received ''"): + client.agents.routes.with_raw_response.delete( + child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + parent_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `child_agent_uuid` but received ''"): + client.agents.routes.with_raw_response.delete( + child_agent_uuid="", + parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Gradient) -> None: + route = client.agents.routes.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(RouteAddResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add_with_all_params(self, client: Gradient) -> None: + route = client.agents.routes.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_child_agent_uuid='"12345678-1234-1234-1234-123456789012"', + if_case='"use this to get weather information"', + body_parent_agent_uuid='"12345678-1234-1234-1234-123456789012"', + route_name='"weather_route"', + ) + assert_matches_type(RouteAddResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Gradient) -> None: + response = client.agents.routes.with_raw_response.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + route = response.parse() + assert_matches_type(RouteAddResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Gradient) -> None: + with client.agents.routes.with_streaming_response.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + route = response.parse() + assert_matches_type(RouteAddResponse, route, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_add(self, client: Gradient) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `path_parent_agent_uuid` but received ''" + ): + client.agents.routes.with_raw_response.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_child_agent_uuid` but received ''"): + client.agents.routes.with_raw_response.add( + path_child_agent_uuid="", + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_view(self, client: Gradient) -> None: + route = client.agents.routes.view( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(RouteViewResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_view(self, client: Gradient) -> None: + response = client.agents.routes.with_raw_response.view( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + route = response.parse() + assert_matches_type(RouteViewResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_view(self, client: Gradient) -> None: + with client.agents.routes.with_streaming_response.view( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + route = response.parse() + assert_matches_type(RouteViewResponse, route, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_view(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.agents.routes.with_raw_response.view( + "", + ) + + +class TestAsyncRoutes: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + route = await async_client.agents.routes.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(RouteUpdateResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + route = await async_client.agents.routes.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_child_agent_uuid='"12345678-1234-1234-1234-123456789012"', + if_case='"use this to get weather information"', + body_parent_agent_uuid='"12345678-1234-1234-1234-123456789012"', + route_name='"weather_route"', + uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(RouteUpdateResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.routes.with_raw_response.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + route = await response.parse() + assert_matches_type(RouteUpdateResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.routes.with_streaming_response.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + route = await response.parse() + assert_matches_type(RouteUpdateResponse, route, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `path_parent_agent_uuid` but received ''" + ): + await async_client.agents.routes.with_raw_response.update( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_child_agent_uuid` but received ''"): + await async_client.agents.routes.with_raw_response.update( + path_child_agent_uuid="", + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + route = await async_client.agents.routes.delete( + child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(RouteDeleteResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.routes.with_raw_response.delete( + child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + route = await response.parse() + assert_matches_type(RouteDeleteResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.agents.routes.with_streaming_response.delete( + child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + route = await response.parse() + assert_matches_type(RouteDeleteResponse, route, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `parent_agent_uuid` but received ''"): + await async_client.agents.routes.with_raw_response.delete( + child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + parent_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `child_agent_uuid` but received ''"): + await async_client.agents.routes.with_raw_response.delete( + child_agent_uuid="", + parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add(self, async_client: AsyncGradient) -> None: + route = await async_client.agents.routes.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(RouteAddResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add_with_all_params(self, async_client: AsyncGradient) -> None: + route = await async_client.agents.routes.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_child_agent_uuid='"12345678-1234-1234-1234-123456789012"', + if_case='"use this to get weather information"', + body_parent_agent_uuid='"12345678-1234-1234-1234-123456789012"', + route_name='"weather_route"', + ) + assert_matches_type(RouteAddResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_add(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.routes.with_raw_response.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + route = await response.parse() + assert_matches_type(RouteAddResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_add(self, async_client: AsyncGradient) -> None: + async with async_client.agents.routes.with_streaming_response.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + route = await response.parse() + assert_matches_type(RouteAddResponse, route, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_add(self, async_client: AsyncGradient) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `path_parent_agent_uuid` but received ''" + ): + await async_client.agents.routes.with_raw_response.add( + path_child_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + path_parent_agent_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_child_agent_uuid` but received ''"): + await async_client.agents.routes.with_raw_response.add( + path_child_agent_uuid="", + path_parent_agent_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_view(self, async_client: AsyncGradient) -> None: + route = await async_client.agents.routes.view( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(RouteViewResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_view(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.routes.with_raw_response.view( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + route = await response.parse() + assert_matches_type(RouteViewResponse, route, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_view(self, async_client: AsyncGradient) -> None: + async with async_client.agents.routes.with_streaming_response.view( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + route = await response.parse() + assert_matches_type(RouteViewResponse, route, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_view(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.agents.routes.with_raw_response.view( + "", + ) diff --git a/tests/api_resources/agents/test_versions.py b/tests/api_resources/agents/test_versions.py new file mode 100644 index 00000000..d12e362e --- /dev/null +++ b/tests/api_resources/agents/test_versions.py @@ -0,0 +1,232 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.agents import VersionListResponse, VersionUpdateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestVersions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + version = client.agents.versions.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(VersionUpdateResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + version = client.agents.versions.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_uuid='"12345678-1234-1234-1234-123456789012"', + version_hash="c3658d8b5c05494cd03ce042926ef08157889ed54b1b74b5ee0b3d66dcee4b73", + ) + assert_matches_type(VersionUpdateResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.versions.with_raw_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(VersionUpdateResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.versions.with_streaming_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = response.parse() + assert_matches_type(VersionUpdateResponse, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + client.agents.versions.with_raw_response.update( + path_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + version = client.agents.versions.list( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(VersionListResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + version = client.agents.versions.list( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(VersionListResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.versions.with_raw_response.list( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(VersionListResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.versions.with_streaming_response.list( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = response.parse() + assert_matches_type(VersionListResponse, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.agents.versions.with_raw_response.list( + uuid="", + ) + + +class TestAsyncVersions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + version = await async_client.agents.versions.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(VersionUpdateResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + version = await async_client.agents.versions.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_uuid='"12345678-1234-1234-1234-123456789012"', + version_hash="c3658d8b5c05494cd03ce042926ef08157889ed54b1b74b5ee0b3d66dcee4b73", + ) + assert_matches_type(VersionUpdateResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.versions.with_raw_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = await response.parse() + assert_matches_type(VersionUpdateResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.versions.with_streaming_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = await response.parse() + assert_matches_type(VersionUpdateResponse, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + await async_client.agents.versions.with_raw_response.update( + path_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + version = await async_client.agents.versions.list( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(VersionListResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + version = await async_client.agents.versions.list( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(VersionListResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.versions.with_raw_response.list( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = await response.parse() + assert_matches_type(VersionListResponse, version, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.versions.with_streaming_response.list( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = await response.parse() + assert_matches_type(VersionListResponse, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.agents.versions.with_raw_response.list( + uuid="", + ) diff --git a/tests/api_resources/chat/__init__.py b/tests/api_resources/chat/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/chat/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py new file mode 100644 index 00000000..fce393fd --- /dev/null +++ b/tests/api_resources/chat/test_completions.py @@ -0,0 +1,396 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.chat import CompletionCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCompletions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_1(self, client: Gradient) -> None: + completion = client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_1(self, client: Gradient) -> None: + completion = client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=True, + max_completion_tokens=256, + max_tokens=0, + metadata={"foo": "string"}, + n=1, + presence_penalty=-2, + stop="\n", + stream=False, + stream_options={"include_usage": True}, + temperature=1, + tool_choice="none", + tools=[ + { + "function": { + "name": "name", + "description": "description", + "parameters": {"foo": "bar"}, + }, + "type": "function", + } + ], + top_logprobs=0, + top_p=1, + user="user-1234", + ) + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_1(self, client: Gradient) -> None: + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_1(self, client: Gradient) -> None: + with client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = response.parse() + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_2(self, client: Gradient) -> None: + completion_stream = client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) + completion_stream.response.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_2(self, client: Gradient) -> None: + completion_stream = client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=True, + max_completion_tokens=256, + max_tokens=0, + metadata={"foo": "string"}, + n=1, + presence_penalty=-2, + stop="\n", + stream_options={"include_usage": True}, + temperature=1, + tool_choice="none", + tools=[ + { + "function": { + "name": "name", + "description": "description", + "parameters": {"foo": "bar"}, + }, + "type": "function", + } + ], + top_logprobs=0, + top_p=1, + user="user-1234", + ) + completion_stream.response.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_2(self, client: Gradient) -> None: + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_2(self, client: Gradient) -> None: + with client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + +class TestAsyncCompletions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncGradient) -> None: + completion = await async_client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + completion = await async_client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=True, + max_completion_tokens=256, + max_tokens=0, + metadata={"foo": "string"}, + n=1, + presence_penalty=-2, + stop="\n", + stream=False, + stream_options={"include_usage": True}, + temperature=1, + tool_choice="none", + tools=[ + { + "function": { + "name": "name", + "description": "description", + "parameters": {"foo": "bar"}, + }, + "type": "function", + } + ], + top_logprobs=0, + top_p=1, + user="user-1234", + ) + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = await response.parse() + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = await response.parse() + assert_matches_type(CompletionCreateResponse, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncGradient) -> None: + completion_stream = await async_client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) + await completion_stream.response.aclose() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + completion_stream = await async_client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=True, + max_completion_tokens=256, + max_tokens=0, + metadata={"foo": "string"}, + n=1, + presence_penalty=-2, + stop="\n", + stream_options={"include_usage": True}, + temperature=1, + tool_choice="none", + tools=[ + { + "function": { + "name": "name", + "description": "description", + "parameters": {"foo": "bar"}, + }, + "type": "function", + } + ], + top_logprobs=0, + top_p=1, + user="user-1234", + ) + await completion_stream.response.aclose() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/databases/__init__.py b/tests/api_resources/databases/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/databases/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/databases/schema_registry/__init__.py b/tests/api_resources/databases/schema_registry/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/databases/schema_registry/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/databases/schema_registry/test_config.py b/tests/api_resources/databases/schema_registry/test_config.py new file mode 100644 index 00000000..ebd60c4c --- /dev/null +++ b/tests/api_resources/databases/schema_registry/test_config.py @@ -0,0 +1,423 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.databases.schema_registry import ( + ConfigUpdateResponse, + ConfigRetrieveResponse, + ConfigUpdateSubjectResponse, + ConfigRetrieveSubjectResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestConfig: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + config = client.databases.schema_registry.config.retrieve( + "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + assert_matches_type(ConfigRetrieveResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.databases.schema_registry.config.with_raw_response.retrieve( + "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + config = response.parse() + assert_matches_type(ConfigRetrieveResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.databases.schema_registry.config.with_streaming_response.retrieve( + "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + config = response.parse() + assert_matches_type(ConfigRetrieveResponse, config, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_cluster_uuid` but received ''"): + client.databases.schema_registry.config.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + config = client.databases.schema_registry.config.update( + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + assert_matches_type(ConfigUpdateResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.databases.schema_registry.config.with_raw_response.update( + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + config = response.parse() + assert_matches_type(ConfigUpdateResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.databases.schema_registry.config.with_streaming_response.update( + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + config = response.parse() + assert_matches_type(ConfigUpdateResponse, config, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_cluster_uuid` but received ''"): + client.databases.schema_registry.config.with_raw_response.update( + database_cluster_uuid="", + compatibility_level="BACKWARD", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_subject(self, client: Gradient) -> None: + config = client.databases.schema_registry.config.retrieve_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + assert_matches_type(ConfigRetrieveSubjectResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve_subject(self, client: Gradient) -> None: + response = client.databases.schema_registry.config.with_raw_response.retrieve_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + config = response.parse() + assert_matches_type(ConfigRetrieveSubjectResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve_subject(self, client: Gradient) -> None: + with client.databases.schema_registry.config.with_streaming_response.retrieve_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + config = response.parse() + assert_matches_type(ConfigRetrieveSubjectResponse, config, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve_subject(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_cluster_uuid` but received ''"): + client.databases.schema_registry.config.with_raw_response.retrieve_subject( + subject_name="customer-schema", + database_cluster_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `subject_name` but received ''"): + client.databases.schema_registry.config.with_raw_response.retrieve_subject( + subject_name="", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_subject(self, client: Gradient) -> None: + config = client.databases.schema_registry.config.update_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + assert_matches_type(ConfigUpdateSubjectResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update_subject(self, client: Gradient) -> None: + response = client.databases.schema_registry.config.with_raw_response.update_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + config = response.parse() + assert_matches_type(ConfigUpdateSubjectResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update_subject(self, client: Gradient) -> None: + with client.databases.schema_registry.config.with_streaming_response.update_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + config = response.parse() + assert_matches_type(ConfigUpdateSubjectResponse, config, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update_subject(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_cluster_uuid` but received ''"): + client.databases.schema_registry.config.with_raw_response.update_subject( + subject_name="customer-schema", + database_cluster_uuid="", + compatibility_level="BACKWARD", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `subject_name` but received ''"): + client.databases.schema_registry.config.with_raw_response.update_subject( + subject_name="", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + + +class TestAsyncConfig: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + config = await async_client.databases.schema_registry.config.retrieve( + "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + assert_matches_type(ConfigRetrieveResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.databases.schema_registry.config.with_raw_response.retrieve( + "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + config = await response.parse() + assert_matches_type(ConfigRetrieveResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.databases.schema_registry.config.with_streaming_response.retrieve( + "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + config = await response.parse() + assert_matches_type(ConfigRetrieveResponse, config, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_cluster_uuid` but received ''"): + await async_client.databases.schema_registry.config.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + config = await async_client.databases.schema_registry.config.update( + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + assert_matches_type(ConfigUpdateResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.databases.schema_registry.config.with_raw_response.update( + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + config = await response.parse() + assert_matches_type(ConfigUpdateResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.databases.schema_registry.config.with_streaming_response.update( + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + config = await response.parse() + assert_matches_type(ConfigUpdateResponse, config, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_cluster_uuid` but received ''"): + await async_client.databases.schema_registry.config.with_raw_response.update( + database_cluster_uuid="", + compatibility_level="BACKWARD", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_subject(self, async_client: AsyncGradient) -> None: + config = await async_client.databases.schema_registry.config.retrieve_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + assert_matches_type(ConfigRetrieveSubjectResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve_subject(self, async_client: AsyncGradient) -> None: + response = await async_client.databases.schema_registry.config.with_raw_response.retrieve_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + config = await response.parse() + assert_matches_type(ConfigRetrieveSubjectResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve_subject(self, async_client: AsyncGradient) -> None: + async with async_client.databases.schema_registry.config.with_streaming_response.retrieve_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + config = await response.parse() + assert_matches_type(ConfigRetrieveSubjectResponse, config, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve_subject(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_cluster_uuid` but received ''"): + await async_client.databases.schema_registry.config.with_raw_response.retrieve_subject( + subject_name="customer-schema", + database_cluster_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `subject_name` but received ''"): + await async_client.databases.schema_registry.config.with_raw_response.retrieve_subject( + subject_name="", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_subject(self, async_client: AsyncGradient) -> None: + config = await async_client.databases.schema_registry.config.update_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + assert_matches_type(ConfigUpdateSubjectResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update_subject(self, async_client: AsyncGradient) -> None: + response = await async_client.databases.schema_registry.config.with_raw_response.update_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + config = await response.parse() + assert_matches_type(ConfigUpdateSubjectResponse, config, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update_subject(self, async_client: AsyncGradient) -> None: + async with async_client.databases.schema_registry.config.with_streaming_response.update_subject( + subject_name="customer-schema", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + config = await response.parse() + assert_matches_type(ConfigUpdateSubjectResponse, config, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update_subject(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_cluster_uuid` but received ''"): + await async_client.databases.schema_registry.config.with_raw_response.update_subject( + subject_name="customer-schema", + database_cluster_uuid="", + compatibility_level="BACKWARD", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `subject_name` but received ''"): + await async_client.databases.schema_registry.config.with_raw_response.update_subject( + subject_name="", + database_cluster_uuid="9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + compatibility_level="BACKWARD", + ) diff --git a/tests/api_resources/gpu_droplets/__init__.py b/tests/api_resources/gpu_droplets/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/gpu_droplets/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/gpu_droplets/account/__init__.py b/tests/api_resources/gpu_droplets/account/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/gpu_droplets/account/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/gpu_droplets/account/test_keys.py b/tests/api_resources/gpu_droplets/account/test_keys.py new file mode 100644 index 00000000..93817d1e --- /dev/null +++ b/tests/api_resources/gpu_droplets/account/test_keys.py @@ -0,0 +1,399 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets.account import ( + KeyListResponse, + KeyCreateResponse, + KeyUpdateResponse, + KeyRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestKeys: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + key = client.gpu_droplets.account.keys.create( + name="My SSH Public Key", + public_key="ssh-rsa AEXAMPLEaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example", + ) + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.gpu_droplets.account.keys.with_raw_response.create( + name="My SSH Public Key", + public_key="ssh-rsa AEXAMPLEaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.gpu_droplets.account.keys.with_streaming_response.create( + name="My SSH Public Key", + public_key="ssh-rsa AEXAMPLEaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + key = client.gpu_droplets.account.keys.retrieve( + 512189, + ) + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.account.keys.with_raw_response.retrieve( + 512189, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.account.keys.with_streaming_response.retrieve( + 512189, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + key = client.gpu_droplets.account.keys.update( + ssh_key_identifier=512189, + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + key = client.gpu_droplets.account.keys.update( + ssh_key_identifier=512189, + name="My SSH Public Key", + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.gpu_droplets.account.keys.with_raw_response.update( + ssh_key_identifier=512189, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.gpu_droplets.account.keys.with_streaming_response.update( + ssh_key_identifier=512189, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + key = client.gpu_droplets.account.keys.list() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + key = client.gpu_droplets.account.keys.list( + page=1, + per_page=1, + ) + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.account.keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.account.keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + key = client.gpu_droplets.account.keys.delete( + 512189, + ) + assert key is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.account.keys.with_raw_response.delete( + 512189, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = response.parse() + assert key is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.account.keys.with_streaming_response.delete( + 512189, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = response.parse() + assert key is None + + assert cast(Any, response.is_closed) is True + + +class TestAsyncKeys: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + key = await async_client.gpu_droplets.account.keys.create( + name="My SSH Public Key", + public_key="ssh-rsa AEXAMPLEaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example", + ) + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.account.keys.with_raw_response.create( + name="My SSH Public Key", + public_key="ssh-rsa AEXAMPLEaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.account.keys.with_streaming_response.create( + name="My SSH Public Key", + public_key="ssh-rsa AEXAMPLEaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyCreateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + key = await async_client.gpu_droplets.account.keys.retrieve( + 512189, + ) + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.account.keys.with_raw_response.retrieve( + 512189, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.account.keys.with_streaming_response.retrieve( + 512189, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyRetrieveResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + key = await async_client.gpu_droplets.account.keys.update( + ssh_key_identifier=512189, + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.gpu_droplets.account.keys.update( + ssh_key_identifier=512189, + name="My SSH Public Key", + ) + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.account.keys.with_raw_response.update( + ssh_key_identifier=512189, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.account.keys.with_streaming_response.update( + ssh_key_identifier=512189, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyUpdateResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + key = await async_client.gpu_droplets.account.keys.list() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + key = await async_client.gpu_droplets.account.keys.list( + page=1, + per_page=1, + ) + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.account.keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.account.keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert_matches_type(KeyListResponse, key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + key = await async_client.gpu_droplets.account.keys.delete( + 512189, + ) + assert key is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.account.keys.with_raw_response.delete( + 512189, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + key = await response.parse() + assert key is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.account.keys.with_streaming_response.delete( + 512189, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + key = await response.parse() + assert key is None + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/firewalls/__init__.py b/tests/api_resources/gpu_droplets/firewalls/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/gpu_droplets/firewalls/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/gpu_droplets/firewalls/test_droplets.py b/tests/api_resources/gpu_droplets/firewalls/test_droplets.py new file mode 100644 index 00000000..693e315d --- /dev/null +++ b/tests/api_resources/gpu_droplets/firewalls/test_droplets.py @@ -0,0 +1,206 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDroplets: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Gradient) -> None: + droplet = client.gpu_droplets.firewalls.droplets.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.droplets.with_raw_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + droplet = response.parse() + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.droplets.with_streaming_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + droplet = response.parse() + assert droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_add(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.droplets.with_raw_response.add( + firewall_id="", + droplet_ids=[49696269], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_remove(self, client: Gradient) -> None: + droplet = client.gpu_droplets.firewalls.droplets.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_remove(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.droplets.with_raw_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + droplet = response.parse() + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_remove(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.droplets.with_streaming_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + droplet = response.parse() + assert droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_remove(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.droplets.with_raw_response.remove( + firewall_id="", + droplet_ids=[49696269], + ) + + +class TestAsyncDroplets: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add(self, async_client: AsyncGradient) -> None: + droplet = await async_client.gpu_droplets.firewalls.droplets.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_add(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.droplets.with_raw_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + droplet = await response.parse() + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_add(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.droplets.with_streaming_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + droplet = await response.parse() + assert droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_add(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.droplets.with_raw_response.add( + firewall_id="", + droplet_ids=[49696269], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_remove(self, async_client: AsyncGradient) -> None: + droplet = await async_client.gpu_droplets.firewalls.droplets.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_remove(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.droplets.with_raw_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + droplet = await response.parse() + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_remove(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.droplets.with_streaming_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + droplet_ids=[49696269], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + droplet = await response.parse() + assert droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_remove(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.droplets.with_raw_response.remove( + firewall_id="", + droplet_ids=[49696269], + ) diff --git a/tests/api_resources/gpu_droplets/firewalls/test_rules.py b/tests/api_resources/gpu_droplets/firewalls/test_rules.py new file mode 100644 index 00000000..27694390 --- /dev/null +++ b/tests/api_resources/gpu_droplets/firewalls/test_rules.py @@ -0,0 +1,326 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRules: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Gradient) -> None: + rule = client.gpu_droplets.firewalls.rules.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add_with_all_params(self, client: Gradient) -> None: + rule = client.gpu_droplets.firewalls.rules.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + inbound_rules=[ + { + "ports": "3306", + "protocol": "tcp", + "sources": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [49696269], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + } + ], + outbound_rules=[ + { + "destinations": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [49696269], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + "ports": "3306", + "protocol": "tcp", + } + ], + ) + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.rules.with_raw_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rule = response.parse() + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.rules.with_streaming_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rule = response.parse() + assert rule is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_add(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.rules.with_raw_response.add( + firewall_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_remove(self, client: Gradient) -> None: + rule = client.gpu_droplets.firewalls.rules.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_remove_with_all_params(self, client: Gradient) -> None: + rule = client.gpu_droplets.firewalls.rules.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + inbound_rules=[ + { + "ports": "3306", + "protocol": "tcp", + "sources": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [49696269], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + } + ], + outbound_rules=[ + { + "destinations": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [49696269], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + "ports": "3306", + "protocol": "tcp", + } + ], + ) + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_remove(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.rules.with_raw_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rule = response.parse() + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_remove(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.rules.with_streaming_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rule = response.parse() + assert rule is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_remove(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.rules.with_raw_response.remove( + firewall_id="", + ) + + +class TestAsyncRules: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add(self, async_client: AsyncGradient) -> None: + rule = await async_client.gpu_droplets.firewalls.rules.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add_with_all_params(self, async_client: AsyncGradient) -> None: + rule = await async_client.gpu_droplets.firewalls.rules.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + inbound_rules=[ + { + "ports": "3306", + "protocol": "tcp", + "sources": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [49696269], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + } + ], + outbound_rules=[ + { + "destinations": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [49696269], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + "ports": "3306", + "protocol": "tcp", + } + ], + ) + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_add(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.rules.with_raw_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rule = await response.parse() + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_add(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.rules.with_streaming_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rule = await response.parse() + assert rule is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_add(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.rules.with_raw_response.add( + firewall_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_remove(self, async_client: AsyncGradient) -> None: + rule = await async_client.gpu_droplets.firewalls.rules.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_remove_with_all_params(self, async_client: AsyncGradient) -> None: + rule = await async_client.gpu_droplets.firewalls.rules.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + inbound_rules=[ + { + "ports": "3306", + "protocol": "tcp", + "sources": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [49696269], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + } + ], + outbound_rules=[ + { + "destinations": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [49696269], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + "ports": "3306", + "protocol": "tcp", + } + ], + ) + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_remove(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.rules.with_raw_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rule = await response.parse() + assert rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_remove(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.rules.with_streaming_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rule = await response.parse() + assert rule is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_remove(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.rules.with_raw_response.remove( + firewall_id="", + ) diff --git a/tests/api_resources/gpu_droplets/firewalls/test_tags.py b/tests/api_resources/gpu_droplets/firewalls/test_tags.py new file mode 100644 index 00000000..50c7563b --- /dev/null +++ b/tests/api_resources/gpu_droplets/firewalls/test_tags.py @@ -0,0 +1,206 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestTags: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Gradient) -> None: + tag = client.gpu_droplets.firewalls.tags.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) + assert tag is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.tags.with_raw_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tag = response.parse() + assert tag is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.tags.with_streaming_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tag = response.parse() + assert tag is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_add(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.tags.with_raw_response.add( + firewall_id="", + tags=["frontend"], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_remove(self, client: Gradient) -> None: + tag = client.gpu_droplets.firewalls.tags.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) + assert tag is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_remove(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.tags.with_raw_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tag = response.parse() + assert tag is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_remove(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.tags.with_streaming_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tag = response.parse() + assert tag is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_remove(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.tags.with_raw_response.remove( + firewall_id="", + tags=["frontend"], + ) + + +class TestAsyncTags: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add(self, async_client: AsyncGradient) -> None: + tag = await async_client.gpu_droplets.firewalls.tags.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) + assert tag is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_add(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.tags.with_raw_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tag = await response.parse() + assert tag is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_add(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.tags.with_streaming_response.add( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tag = await response.parse() + assert tag is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_add(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.tags.with_raw_response.add( + firewall_id="", + tags=["frontend"], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_remove(self, async_client: AsyncGradient) -> None: + tag = await async_client.gpu_droplets.firewalls.tags.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) + assert tag is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_remove(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.tags.with_raw_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tag = await response.parse() + assert tag is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_remove(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.tags.with_streaming_response.remove( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + tags=["frontend"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tag = await response.parse() + assert tag is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_remove(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.tags.with_raw_response.remove( + firewall_id="", + tags=["frontend"], + ) diff --git a/tests/api_resources/gpu_droplets/floating_ips/__init__.py b/tests/api_resources/gpu_droplets/floating_ips/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/gpu_droplets/floating_ips/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/gpu_droplets/floating_ips/test_actions.py b/tests/api_resources/gpu_droplets/floating_ips/test_actions.py new file mode 100644 index 00000000..7f7ab06a --- /dev/null +++ b/tests/api_resources/gpu_droplets/floating_ips/test_actions.py @@ -0,0 +1,396 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets.floating_ips import ( + ActionListResponse, + ActionCreateResponse, + ActionRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestActions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.floating_ips.actions.create( + floating_ip="45.55.96.47", + type="assign", + ) + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.actions.with_raw_response.create( + floating_ip="45.55.96.47", + type="assign", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.actions.with_streaming_response.create( + floating_ip="45.55.96.47", + type="assign", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_create_overload_1(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + client.gpu_droplets.floating_ips.actions.with_raw_response.create( + floating_ip="", + type="assign", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.floating_ips.actions.create( + floating_ip="45.55.96.47", + droplet_id=758604968, + type="assign", + ) + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.actions.with_raw_response.create( + floating_ip="45.55.96.47", + droplet_id=758604968, + type="assign", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.actions.with_streaming_response.create( + floating_ip="45.55.96.47", + droplet_id=758604968, + type="assign", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_create_overload_2(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + client.gpu_droplets.floating_ips.actions.with_raw_response.create( + floating_ip="", + droplet_id=758604968, + type="assign", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + action = client.gpu_droplets.floating_ips.actions.retrieve( + action_id=36804636, + floating_ip="45.55.96.47", + ) + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.actions.with_raw_response.retrieve( + action_id=36804636, + floating_ip="45.55.96.47", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.actions.with_streaming_response.retrieve( + action_id=36804636, + floating_ip="45.55.96.47", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + client.gpu_droplets.floating_ips.actions.with_raw_response.retrieve( + action_id=36804636, + floating_ip="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + action = client.gpu_droplets.floating_ips.actions.list( + "45.55.96.47", + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.actions.with_raw_response.list( + "45.55.96.47", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.actions.with_streaming_response.list( + "45.55.96.47", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + client.gpu_droplets.floating_ips.actions.with_raw_response.list( + "", + ) + + +class TestAsyncActions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.floating_ips.actions.create( + floating_ip="45.55.96.47", + type="assign", + ) + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.actions.with_raw_response.create( + floating_ip="45.55.96.47", + type="assign", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.actions.with_streaming_response.create( + floating_ip="45.55.96.47", + type="assign", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_create_overload_1(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + await async_client.gpu_droplets.floating_ips.actions.with_raw_response.create( + floating_ip="", + type="assign", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.floating_ips.actions.create( + floating_ip="45.55.96.47", + droplet_id=758604968, + type="assign", + ) + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.actions.with_raw_response.create( + floating_ip="45.55.96.47", + droplet_id=758604968, + type="assign", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.actions.with_streaming_response.create( + floating_ip="45.55.96.47", + droplet_id=758604968, + type="assign", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionCreateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_create_overload_2(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + await async_client.gpu_droplets.floating_ips.actions.with_raw_response.create( + floating_ip="", + droplet_id=758604968, + type="assign", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.floating_ips.actions.retrieve( + action_id=36804636, + floating_ip="45.55.96.47", + ) + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.actions.with_raw_response.retrieve( + action_id=36804636, + floating_ip="45.55.96.47", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.actions.with_streaming_response.retrieve( + action_id=36804636, + floating_ip="45.55.96.47", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + await async_client.gpu_droplets.floating_ips.actions.with_raw_response.retrieve( + action_id=36804636, + floating_ip="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.floating_ips.actions.list( + "45.55.96.47", + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.actions.with_raw_response.list( + "45.55.96.47", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.actions.with_streaming_response.list( + "45.55.96.47", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + await async_client.gpu_droplets.floating_ips.actions.with_raw_response.list( + "", + ) diff --git a/tests/api_resources/gpu_droplets/images/__init__.py b/tests/api_resources/gpu_droplets/images/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/gpu_droplets/images/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/gpu_droplets/images/test_actions.py b/tests/api_resources/gpu_droplets/images/test_actions.py new file mode 100644 index 00000000..ad5d4892 --- /dev/null +++ b/tests/api_resources/gpu_droplets/images/test_actions.py @@ -0,0 +1,321 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.shared import Action +from gradient.types.gpu_droplets.images import ActionListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestActions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.images.actions.create( + image_id=62137902, + type="convert", + ) + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.images.actions.with_raw_response.create( + image_id=62137902, + type="convert", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.images.actions.with_streaming_response.create( + image_id=62137902, + type="convert", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(Action, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.images.actions.create( + image_id=62137902, + region="nyc3", + type="convert", + ) + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.images.actions.with_raw_response.create( + image_id=62137902, + region="nyc3", + type="convert", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.images.actions.with_streaming_response.create( + image_id=62137902, + region="nyc3", + type="convert", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(Action, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + action = client.gpu_droplets.images.actions.retrieve( + action_id=36804636, + image_id=62137902, + ) + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.images.actions.with_raw_response.retrieve( + action_id=36804636, + image_id=62137902, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.images.actions.with_streaming_response.retrieve( + action_id=36804636, + image_id=62137902, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(Action, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + action = client.gpu_droplets.images.actions.list( + 62137902, + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.images.actions.with_raw_response.list( + 62137902, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.images.actions.with_streaming_response.list( + 62137902, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncActions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.images.actions.create( + image_id=62137902, + type="convert", + ) + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.actions.with_raw_response.create( + image_id=62137902, + type="convert", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.actions.with_streaming_response.create( + image_id=62137902, + type="convert", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(Action, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.images.actions.create( + image_id=62137902, + region="nyc3", + type="convert", + ) + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.actions.with_raw_response.create( + image_id=62137902, + region="nyc3", + type="convert", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.actions.with_streaming_response.create( + image_id=62137902, + region="nyc3", + type="convert", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(Action, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.images.actions.retrieve( + action_id=36804636, + image_id=62137902, + ) + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.actions.with_raw_response.retrieve( + action_id=36804636, + image_id=62137902, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(Action, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.actions.with_streaming_response.retrieve( + action_id=36804636, + image_id=62137902, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(Action, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.images.actions.list( + 62137902, + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.actions.with_raw_response.list( + 62137902, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.actions.with_streaming_response.list( + 62137902, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/load_balancers/__init__.py b/tests/api_resources/gpu_droplets/load_balancers/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/gpu_droplets/load_balancers/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/gpu_droplets/load_balancers/test_droplets.py b/tests/api_resources/gpu_droplets/load_balancers/test_droplets.py new file mode 100644 index 00000000..e6eefd23 --- /dev/null +++ b/tests/api_resources/gpu_droplets/load_balancers/test_droplets.py @@ -0,0 +1,206 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDroplets: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Gradient) -> None: + droplet = client.gpu_droplets.load_balancers.droplets.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.droplets.with_raw_response.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + droplet = response.parse() + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.droplets.with_streaming_response.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + droplet = response.parse() + assert droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_add(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.droplets.with_raw_response.add( + lb_id="", + droplet_ids=[3164444, 3164445], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_remove(self, client: Gradient) -> None: + droplet = client.gpu_droplets.load_balancers.droplets.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_remove(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.droplets.with_raw_response.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + droplet = response.parse() + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_remove(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.droplets.with_streaming_response.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + droplet = response.parse() + assert droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_remove(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.droplets.with_raw_response.remove( + lb_id="", + droplet_ids=[3164444, 3164445], + ) + + +class TestAsyncDroplets: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add(self, async_client: AsyncGradient) -> None: + droplet = await async_client.gpu_droplets.load_balancers.droplets.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_add(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.droplets.with_raw_response.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + droplet = await response.parse() + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_add(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.droplets.with_streaming_response.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + droplet = await response.parse() + assert droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_add(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.droplets.with_raw_response.add( + lb_id="", + droplet_ids=[3164444, 3164445], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_remove(self, async_client: AsyncGradient) -> None: + droplet = await async_client.gpu_droplets.load_balancers.droplets.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_remove(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.droplets.with_raw_response.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + droplet = await response.parse() + assert droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_remove(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.droplets.with_streaming_response.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + droplet_ids=[3164444, 3164445], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + droplet = await response.parse() + assert droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_remove(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.droplets.with_raw_response.remove( + lb_id="", + droplet_ids=[3164444, 3164445], + ) diff --git a/tests/api_resources/gpu_droplets/load_balancers/test_forwarding_rules.py b/tests/api_resources/gpu_droplets/load_balancers/test_forwarding_rules.py new file mode 100644 index 00000000..a3cc0bd1 --- /dev/null +++ b/tests/api_resources/gpu_droplets/load_balancers/test_forwarding_rules.py @@ -0,0 +1,318 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestForwardingRules: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Gradient) -> None: + forwarding_rule = client.gpu_droplets.load_balancers.forwarding_rules.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert forwarding_rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.forwarding_rules.with_raw_response.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + forwarding_rule = response.parse() + assert forwarding_rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.forwarding_rules.with_streaming_response.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + forwarding_rule = response.parse() + assert forwarding_rule is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_add(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.forwarding_rules.with_raw_response.add( + lb_id="", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_remove(self, client: Gradient) -> None: + forwarding_rule = client.gpu_droplets.load_balancers.forwarding_rules.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert forwarding_rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_remove(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.forwarding_rules.with_raw_response.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + forwarding_rule = response.parse() + assert forwarding_rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_remove(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.forwarding_rules.with_streaming_response.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + forwarding_rule = response.parse() + assert forwarding_rule is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_remove(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.forwarding_rules.with_raw_response.remove( + lb_id="", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + +class TestAsyncForwardingRules: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add(self, async_client: AsyncGradient) -> None: + forwarding_rule = await async_client.gpu_droplets.load_balancers.forwarding_rules.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert forwarding_rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_add(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.forwarding_rules.with_raw_response.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + forwarding_rule = await response.parse() + assert forwarding_rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_add(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.forwarding_rules.with_streaming_response.add( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + forwarding_rule = await response.parse() + assert forwarding_rule is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_add(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.forwarding_rules.with_raw_response.add( + lb_id="", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_remove(self, async_client: AsyncGradient) -> None: + forwarding_rule = await async_client.gpu_droplets.load_balancers.forwarding_rules.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert forwarding_rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_remove(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.forwarding_rules.with_raw_response.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + forwarding_rule = await response.parse() + assert forwarding_rule is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_remove(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.forwarding_rules.with_streaming_response.remove( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + forwarding_rule = await response.parse() + assert forwarding_rule is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_remove(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.forwarding_rules.with_raw_response.remove( + lb_id="", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) diff --git a/tests/api_resources/gpu_droplets/test_actions.py b/tests/api_resources/gpu_droplets/test_actions.py new file mode 100644 index 00000000..e514196b --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_actions.py @@ -0,0 +1,1209 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + ActionListResponse, + ActionInitiateResponse, + ActionRetrieveResponse, + ActionBulkInitiateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestActions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.retrieve( + action_id=36804636, + droplet_id=3164444, + ) + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.retrieve( + action_id=36804636, + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.retrieve( + action_id=36804636, + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.list( + droplet_id=3164444, + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.list( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.list( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.list( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_bulk_initiate_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.bulk_initiate( + type="reboot", + ) + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_bulk_initiate_with_all_params_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.bulk_initiate( + type="reboot", + tag_name="tag_name", + ) + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_bulk_initiate_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.bulk_initiate( + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_bulk_initiate_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.bulk_initiate( + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_bulk_initiate_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.bulk_initiate( + type="reboot", + ) + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_bulk_initiate_with_all_params_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.bulk_initiate( + type="reboot", + tag_name="tag_name", + name="Nifty New Snapshot", + ) + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_bulk_initiate_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.bulk_initiate( + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_bulk_initiate_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.bulk_initiate( + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="enable_backups", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_with_all_params_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="enable_backups", + backup_policy={ + "hour": 20, + "plan": "daily", + "weekday": "SUN", + }, + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="enable_backups", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="enable_backups", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_3(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="enable_backups", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_with_all_params_overload_3(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="enable_backups", + backup_policy={ + "hour": 20, + "plan": "weekly", + "weekday": "SUN", + }, + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_3(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="enable_backups", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_3(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="enable_backups", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_4(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_with_all_params_overload_4(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + image=12389723, + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_4(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_4(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_5(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_with_all_params_overload_5(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + disk=True, + size="s-2vcpu-2gb", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_5(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_5(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_6(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_with_all_params_overload_6(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + image="ubuntu-20-04-x64", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_6(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_6(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_7(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_with_all_params_overload_7(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + name="nifty-new-name", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_7(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_7(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_8(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_with_all_params_overload_8(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + kernel=12389723, + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_8(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_8(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_overload_9(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_with_all_params_overload_9(self, client: Gradient) -> None: + action = client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + name="Nifty New Snapshot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_overload_9(self, client: Gradient) -> None: + response = client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_overload_9(self, client: Gradient) -> None: + with client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncActions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.retrieve( + action_id=36804636, + droplet_id=3164444, + ) + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.retrieve( + action_id=36804636, + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.retrieve( + action_id=36804636, + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.list( + droplet_id=3164444, + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.list( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.list( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.list( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_bulk_initiate_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.bulk_initiate( + type="reboot", + ) + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_bulk_initiate_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.bulk_initiate( + type="reboot", + tag_name="tag_name", + ) + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_bulk_initiate_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.bulk_initiate( + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_bulk_initiate_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.bulk_initiate( + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_bulk_initiate_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.bulk_initiate( + type="reboot", + ) + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_bulk_initiate_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.bulk_initiate( + type="reboot", + tag_name="tag_name", + name="Nifty New Snapshot", + ) + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_bulk_initiate_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.bulk_initiate( + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_bulk_initiate_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.bulk_initiate( + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionBulkInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="enable_backups", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="enable_backups", + backup_policy={ + "hour": 20, + "plan": "daily", + "weekday": "SUN", + }, + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="enable_backups", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="enable_backups", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_3(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="enable_backups", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_with_all_params_overload_3(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="enable_backups", + backup_policy={ + "hour": 20, + "plan": "weekly", + "weekday": "SUN", + }, + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_3(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="enable_backups", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_3(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="enable_backups", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_4(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_with_all_params_overload_4(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + image=12389723, + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_4(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_4(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_5(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_with_all_params_overload_5(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + disk=True, + size="s-2vcpu-2gb", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_5(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_5(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_6(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_with_all_params_overload_6(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + image="ubuntu-20-04-x64", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_6(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_6(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_7(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_with_all_params_overload_7(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + name="nifty-new-name", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_7(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_7(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_8(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_with_all_params_overload_8(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + kernel=12389723, + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_8(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_8(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_overload_9(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_with_all_params_overload_9(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.actions.initiate( + droplet_id=3164444, + type="reboot", + name="Nifty New Snapshot", + ) + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_overload_9(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.actions.with_raw_response.initiate( + droplet_id=3164444, + type="reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_overload_9(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.actions.with_streaming_response.initiate( + droplet_id=3164444, + type="reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/test_autoscale.py b/tests/api_resources/gpu_droplets/test_autoscale.py new file mode 100644 index 00000000..bbb0c2e4 --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_autoscale.py @@ -0,0 +1,953 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + AutoscaleListResponse, + AutoscaleCreateResponse, + AutoscaleUpdateResponse, + AutoscaleRetrieveResponse, + AutoscaleListHistoryResponse, + AutoscaleListMembersResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAutoscale: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.create( + config={ + "max_instances": 5, + "min_instances": 1, + }, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + assert_matches_type(AutoscaleCreateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.create( + config={ + "max_instances": 5, + "min_instances": 1, + "cooldown_minutes": 10, + "target_cpu_utilization": 0.5, + "target_memory_utilization": 0.6, + }, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + "ipv6": True, + "name": "example.com", + "project_id": "746c6152-2fa2-11ed-92d3-27aaa54e4988", + "tags": ["env:prod", "web"], + "user_data": "#cloud-config\nruncmd:\n - touch /test.txt\n", + "vpc_uuid": "760e09ef-dc84-11e8-981e-3cfdfeaae000", + "with_droplet_agent": True, + }, + name="my-autoscale-pool", + ) + assert_matches_type(AutoscaleCreateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.gpu_droplets.autoscale.with_raw_response.create( + config={ + "max_instances": 5, + "min_instances": 1, + }, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = response.parse() + assert_matches_type(AutoscaleCreateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.gpu_droplets.autoscale.with_streaming_response.create( + config={ + "max_instances": 5, + "min_instances": 1, + }, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = response.parse() + assert_matches_type(AutoscaleCreateResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.retrieve( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + assert_matches_type(AutoscaleRetrieveResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.autoscale.with_raw_response.retrieve( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = response.parse() + assert_matches_type(AutoscaleRetrieveResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.autoscale.with_streaming_response.retrieve( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = response.parse() + assert_matches_type(AutoscaleRetrieveResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + client.gpu_droplets.autoscale.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.update( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + assert_matches_type(AutoscaleUpdateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.update( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + "ipv6": True, + "name": "example.com", + "project_id": "746c6152-2fa2-11ed-92d3-27aaa54e4988", + "tags": ["env:prod", "web"], + "user_data": "#cloud-config\nruncmd:\n - touch /test.txt\n", + "vpc_uuid": "760e09ef-dc84-11e8-981e-3cfdfeaae000", + "with_droplet_agent": True, + }, + name="my-autoscale-pool", + ) + assert_matches_type(AutoscaleUpdateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.gpu_droplets.autoscale.with_raw_response.update( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = response.parse() + assert_matches_type(AutoscaleUpdateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.gpu_droplets.autoscale.with_streaming_response.update( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = response.parse() + assert_matches_type(AutoscaleUpdateResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + client.gpu_droplets.autoscale.with_raw_response.update( + autoscale_pool_id="", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.list() + assert_matches_type(AutoscaleListResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.list( + name="name", + page=1, + per_page=1, + ) + assert_matches_type(AutoscaleListResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.autoscale.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = response.parse() + assert_matches_type(AutoscaleListResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.autoscale.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = response.parse() + assert_matches_type(AutoscaleListResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.delete( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + assert autoscale is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.autoscale.with_raw_response.delete( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = response.parse() + assert autoscale is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.autoscale.with_streaming_response.delete( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = response.parse() + assert autoscale is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + client.gpu_droplets.autoscale.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_dangerous(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.delete_dangerous( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + x_dangerous=True, + ) + assert autoscale is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete_dangerous(self, client: Gradient) -> None: + response = client.gpu_droplets.autoscale.with_raw_response.delete_dangerous( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + x_dangerous=True, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = response.parse() + assert autoscale is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete_dangerous(self, client: Gradient) -> None: + with client.gpu_droplets.autoscale.with_streaming_response.delete_dangerous( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + x_dangerous=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = response.parse() + assert autoscale is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete_dangerous(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + client.gpu_droplets.autoscale.with_raw_response.delete_dangerous( + autoscale_pool_id="", + x_dangerous=True, + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_history(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.list_history( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + assert_matches_type(AutoscaleListHistoryResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_history_with_all_params(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.list_history( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + page=1, + per_page=1, + ) + assert_matches_type(AutoscaleListHistoryResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_history(self, client: Gradient) -> None: + response = client.gpu_droplets.autoscale.with_raw_response.list_history( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = response.parse() + assert_matches_type(AutoscaleListHistoryResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_history(self, client: Gradient) -> None: + with client.gpu_droplets.autoscale.with_streaming_response.list_history( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = response.parse() + assert_matches_type(AutoscaleListHistoryResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_history(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + client.gpu_droplets.autoscale.with_raw_response.list_history( + autoscale_pool_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_members(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.list_members( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + assert_matches_type(AutoscaleListMembersResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_members_with_all_params(self, client: Gradient) -> None: + autoscale = client.gpu_droplets.autoscale.list_members( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + page=1, + per_page=1, + ) + assert_matches_type(AutoscaleListMembersResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_members(self, client: Gradient) -> None: + response = client.gpu_droplets.autoscale.with_raw_response.list_members( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = response.parse() + assert_matches_type(AutoscaleListMembersResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_members(self, client: Gradient) -> None: + with client.gpu_droplets.autoscale.with_streaming_response.list_members( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = response.parse() + assert_matches_type(AutoscaleListMembersResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_members(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + client.gpu_droplets.autoscale.with_raw_response.list_members( + autoscale_pool_id="", + ) + + +class TestAsyncAutoscale: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.create( + config={ + "max_instances": 5, + "min_instances": 1, + }, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + assert_matches_type(AutoscaleCreateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.create( + config={ + "max_instances": 5, + "min_instances": 1, + "cooldown_minutes": 10, + "target_cpu_utilization": 0.5, + "target_memory_utilization": 0.6, + }, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + "ipv6": True, + "name": "example.com", + "project_id": "746c6152-2fa2-11ed-92d3-27aaa54e4988", + "tags": ["env:prod", "web"], + "user_data": "#cloud-config\nruncmd:\n - touch /test.txt\n", + "vpc_uuid": "760e09ef-dc84-11e8-981e-3cfdfeaae000", + "with_droplet_agent": True, + }, + name="my-autoscale-pool", + ) + assert_matches_type(AutoscaleCreateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.autoscale.with_raw_response.create( + config={ + "max_instances": 5, + "min_instances": 1, + }, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = await response.parse() + assert_matches_type(AutoscaleCreateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.autoscale.with_streaming_response.create( + config={ + "max_instances": 5, + "min_instances": 1, + }, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = await response.parse() + assert_matches_type(AutoscaleCreateResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.retrieve( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + assert_matches_type(AutoscaleRetrieveResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.autoscale.with_raw_response.retrieve( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = await response.parse() + assert_matches_type(AutoscaleRetrieveResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.autoscale.with_streaming_response.retrieve( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = await response.parse() + assert_matches_type(AutoscaleRetrieveResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + await async_client.gpu_droplets.autoscale.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.update( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + assert_matches_type(AutoscaleUpdateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.update( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + "ipv6": True, + "name": "example.com", + "project_id": "746c6152-2fa2-11ed-92d3-27aaa54e4988", + "tags": ["env:prod", "web"], + "user_data": "#cloud-config\nruncmd:\n - touch /test.txt\n", + "vpc_uuid": "760e09ef-dc84-11e8-981e-3cfdfeaae000", + "with_droplet_agent": True, + }, + name="my-autoscale-pool", + ) + assert_matches_type(AutoscaleUpdateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.autoscale.with_raw_response.update( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = await response.parse() + assert_matches_type(AutoscaleUpdateResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.autoscale.with_streaming_response.update( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = await response.parse() + assert_matches_type(AutoscaleUpdateResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + await async_client.gpu_droplets.autoscale.with_raw_response.update( + autoscale_pool_id="", + config={"target_number_instances": 2}, + droplet_template={ + "image": "ubuntu-20-04-x64", + "region": "nyc3", + "size": "c-2", + "ssh_keys": ["3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + }, + name="my-autoscale-pool", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.list() + assert_matches_type(AutoscaleListResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.list( + name="name", + page=1, + per_page=1, + ) + assert_matches_type(AutoscaleListResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.autoscale.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = await response.parse() + assert_matches_type(AutoscaleListResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.autoscale.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = await response.parse() + assert_matches_type(AutoscaleListResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.delete( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + assert autoscale is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.autoscale.with_raw_response.delete( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = await response.parse() + assert autoscale is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.autoscale.with_streaming_response.delete( + "0d3db13e-a604-4944-9827-7ec2642d32ac", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = await response.parse() + assert autoscale is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + await async_client.gpu_droplets.autoscale.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_dangerous(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.delete_dangerous( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + x_dangerous=True, + ) + assert autoscale is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete_dangerous(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.autoscale.with_raw_response.delete_dangerous( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + x_dangerous=True, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = await response.parse() + assert autoscale is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete_dangerous(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.autoscale.with_streaming_response.delete_dangerous( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + x_dangerous=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = await response.parse() + assert autoscale is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete_dangerous(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + await async_client.gpu_droplets.autoscale.with_raw_response.delete_dangerous( + autoscale_pool_id="", + x_dangerous=True, + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_history(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.list_history( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + assert_matches_type(AutoscaleListHistoryResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_history_with_all_params(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.list_history( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + page=1, + per_page=1, + ) + assert_matches_type(AutoscaleListHistoryResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_history(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.autoscale.with_raw_response.list_history( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = await response.parse() + assert_matches_type(AutoscaleListHistoryResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_history(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.autoscale.with_streaming_response.list_history( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = await response.parse() + assert_matches_type(AutoscaleListHistoryResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_history(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + await async_client.gpu_droplets.autoscale.with_raw_response.list_history( + autoscale_pool_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_members(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.list_members( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + assert_matches_type(AutoscaleListMembersResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_members_with_all_params(self, async_client: AsyncGradient) -> None: + autoscale = await async_client.gpu_droplets.autoscale.list_members( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + page=1, + per_page=1, + ) + assert_matches_type(AutoscaleListMembersResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_members(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.autoscale.with_raw_response.list_members( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + autoscale = await response.parse() + assert_matches_type(AutoscaleListMembersResponse, autoscale, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_members(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.autoscale.with_streaming_response.list_members( + autoscale_pool_id="0d3db13e-a604-4944-9827-7ec2642d32ac", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + autoscale = await response.parse() + assert_matches_type(AutoscaleListMembersResponse, autoscale, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_members(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `autoscale_pool_id` but received ''"): + await async_client.gpu_droplets.autoscale.with_raw_response.list_members( + autoscale_pool_id="", + ) diff --git a/tests/api_resources/gpu_droplets/test_backups.py b/tests/api_resources/gpu_droplets/test_backups.py new file mode 100644 index 00000000..c6e854e4 --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_backups.py @@ -0,0 +1,315 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + BackupListResponse, + BackupListPoliciesResponse, + BackupRetrievePolicyResponse, + BackupListSupportedPoliciesResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestBackups: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + backup = client.gpu_droplets.backups.list( + droplet_id=3164444, + ) + assert_matches_type(BackupListResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + backup = client.gpu_droplets.backups.list( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(BackupListResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.backups.with_raw_response.list( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backup = response.parse() + assert_matches_type(BackupListResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.backups.with_streaming_response.list( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backup = response.parse() + assert_matches_type(BackupListResponse, backup, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_policies(self, client: Gradient) -> None: + backup = client.gpu_droplets.backups.list_policies() + assert_matches_type(BackupListPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_policies_with_all_params(self, client: Gradient) -> None: + backup = client.gpu_droplets.backups.list_policies( + page=1, + per_page=1, + ) + assert_matches_type(BackupListPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_policies(self, client: Gradient) -> None: + response = client.gpu_droplets.backups.with_raw_response.list_policies() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backup = response.parse() + assert_matches_type(BackupListPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_policies(self, client: Gradient) -> None: + with client.gpu_droplets.backups.with_streaming_response.list_policies() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backup = response.parse() + assert_matches_type(BackupListPoliciesResponse, backup, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_supported_policies(self, client: Gradient) -> None: + backup = client.gpu_droplets.backups.list_supported_policies() + assert_matches_type(BackupListSupportedPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_supported_policies(self, client: Gradient) -> None: + response = client.gpu_droplets.backups.with_raw_response.list_supported_policies() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backup = response.parse() + assert_matches_type(BackupListSupportedPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_supported_policies(self, client: Gradient) -> None: + with client.gpu_droplets.backups.with_streaming_response.list_supported_policies() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backup = response.parse() + assert_matches_type(BackupListSupportedPoliciesResponse, backup, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_policy(self, client: Gradient) -> None: + backup = client.gpu_droplets.backups.retrieve_policy( + 3164444, + ) + assert_matches_type(BackupRetrievePolicyResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve_policy(self, client: Gradient) -> None: + response = client.gpu_droplets.backups.with_raw_response.retrieve_policy( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backup = response.parse() + assert_matches_type(BackupRetrievePolicyResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve_policy(self, client: Gradient) -> None: + with client.gpu_droplets.backups.with_streaming_response.retrieve_policy( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backup = response.parse() + assert_matches_type(BackupRetrievePolicyResponse, backup, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncBackups: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + backup = await async_client.gpu_droplets.backups.list( + droplet_id=3164444, + ) + assert_matches_type(BackupListResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + backup = await async_client.gpu_droplets.backups.list( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(BackupListResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.backups.with_raw_response.list( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backup = await response.parse() + assert_matches_type(BackupListResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.backups.with_streaming_response.list( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backup = await response.parse() + assert_matches_type(BackupListResponse, backup, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_policies(self, async_client: AsyncGradient) -> None: + backup = await async_client.gpu_droplets.backups.list_policies() + assert_matches_type(BackupListPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_policies_with_all_params(self, async_client: AsyncGradient) -> None: + backup = await async_client.gpu_droplets.backups.list_policies( + page=1, + per_page=1, + ) + assert_matches_type(BackupListPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_policies(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.backups.with_raw_response.list_policies() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backup = await response.parse() + assert_matches_type(BackupListPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_policies(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.backups.with_streaming_response.list_policies() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backup = await response.parse() + assert_matches_type(BackupListPoliciesResponse, backup, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_supported_policies(self, async_client: AsyncGradient) -> None: + backup = await async_client.gpu_droplets.backups.list_supported_policies() + assert_matches_type(BackupListSupportedPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_supported_policies(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.backups.with_raw_response.list_supported_policies() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backup = await response.parse() + assert_matches_type(BackupListSupportedPoliciesResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_supported_policies(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.backups.with_streaming_response.list_supported_policies() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backup = await response.parse() + assert_matches_type(BackupListSupportedPoliciesResponse, backup, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_policy(self, async_client: AsyncGradient) -> None: + backup = await async_client.gpu_droplets.backups.retrieve_policy( + 3164444, + ) + assert_matches_type(BackupRetrievePolicyResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve_policy(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.backups.with_raw_response.retrieve_policy( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backup = await response.parse() + assert_matches_type(BackupRetrievePolicyResponse, backup, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve_policy(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.backups.with_streaming_response.retrieve_policy( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backup = await response.parse() + assert_matches_type(BackupRetrievePolicyResponse, backup, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/test_destroy_with_associated_resources.py b/tests/api_resources/gpu_droplets/test_destroy_with_associated_resources.py new file mode 100644 index 00000000..80f1bd7c --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_destroy_with_associated_resources.py @@ -0,0 +1,431 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + DestroyWithAssociatedResourceListResponse, + DestroyWithAssociatedResourceCheckStatusResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDestroyWithAssociatedResources: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + destroy_with_associated_resource = client.gpu_droplets.destroy_with_associated_resources.list( + 3164444, + ) + assert_matches_type( + DestroyWithAssociatedResourceListResponse, destroy_with_associated_resource, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.destroy_with_associated_resources.with_raw_response.list( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = response.parse() + assert_matches_type( + DestroyWithAssociatedResourceListResponse, destroy_with_associated_resource, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.list( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = response.parse() + assert_matches_type( + DestroyWithAssociatedResourceListResponse, destroy_with_associated_resource, path=["response"] + ) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_check_status(self, client: Gradient) -> None: + destroy_with_associated_resource = client.gpu_droplets.destroy_with_associated_resources.check_status( + 3164444, + ) + assert_matches_type( + DestroyWithAssociatedResourceCheckStatusResponse, destroy_with_associated_resource, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_check_status(self, client: Gradient) -> None: + response = client.gpu_droplets.destroy_with_associated_resources.with_raw_response.check_status( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = response.parse() + assert_matches_type( + DestroyWithAssociatedResourceCheckStatusResponse, destroy_with_associated_resource, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_check_status(self, client: Gradient) -> None: + with client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.check_status( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = response.parse() + assert_matches_type( + DestroyWithAssociatedResourceCheckStatusResponse, destroy_with_associated_resource, path=["response"] + ) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_dangerous(self, client: Gradient) -> None: + destroy_with_associated_resource = client.gpu_droplets.destroy_with_associated_resources.delete_dangerous( + droplet_id=3164444, + x_dangerous=True, + ) + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete_dangerous(self, client: Gradient) -> None: + response = client.gpu_droplets.destroy_with_associated_resources.with_raw_response.delete_dangerous( + droplet_id=3164444, + x_dangerous=True, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = response.parse() + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete_dangerous(self, client: Gradient) -> None: + with client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.delete_dangerous( + droplet_id=3164444, + x_dangerous=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = response.parse() + assert destroy_with_associated_resource is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_selective(self, client: Gradient) -> None: + destroy_with_associated_resource = client.gpu_droplets.destroy_with_associated_resources.delete_selective( + droplet_id=3164444, + ) + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_selective_with_all_params(self, client: Gradient) -> None: + destroy_with_associated_resource = client.gpu_droplets.destroy_with_associated_resources.delete_selective( + droplet_id=3164444, + floating_ips=["6186916"], + reserved_ips=["6186916"], + snapshots=["61486916"], + volume_snapshots=["edb0478d-7436-11ea-86e6-0a58ac144b91"], + volumes=["ba49449a-7435-11ea-b89e-0a58ac14480f"], + ) + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete_selective(self, client: Gradient) -> None: + response = client.gpu_droplets.destroy_with_associated_resources.with_raw_response.delete_selective( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = response.parse() + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete_selective(self, client: Gradient) -> None: + with client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.delete_selective( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = response.parse() + assert destroy_with_associated_resource is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retry(self, client: Gradient) -> None: + destroy_with_associated_resource = client.gpu_droplets.destroy_with_associated_resources.retry( + 3164444, + ) + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retry(self, client: Gradient) -> None: + response = client.gpu_droplets.destroy_with_associated_resources.with_raw_response.retry( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = response.parse() + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retry(self, client: Gradient) -> None: + with client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.retry( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = response.parse() + assert destroy_with_associated_resource is None + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDestroyWithAssociatedResources: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + destroy_with_associated_resource = await async_client.gpu_droplets.destroy_with_associated_resources.list( + 3164444, + ) + assert_matches_type( + DestroyWithAssociatedResourceListResponse, destroy_with_associated_resource, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.destroy_with_associated_resources.with_raw_response.list( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = await response.parse() + assert_matches_type( + DestroyWithAssociatedResourceListResponse, destroy_with_associated_resource, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.list( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = await response.parse() + assert_matches_type( + DestroyWithAssociatedResourceListResponse, destroy_with_associated_resource, path=["response"] + ) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_check_status(self, async_client: AsyncGradient) -> None: + destroy_with_associated_resource = ( + await async_client.gpu_droplets.destroy_with_associated_resources.check_status( + 3164444, + ) + ) + assert_matches_type( + DestroyWithAssociatedResourceCheckStatusResponse, destroy_with_associated_resource, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_check_status(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.destroy_with_associated_resources.with_raw_response.check_status( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = await response.parse() + assert_matches_type( + DestroyWithAssociatedResourceCheckStatusResponse, destroy_with_associated_resource, path=["response"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_check_status(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.check_status( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = await response.parse() + assert_matches_type( + DestroyWithAssociatedResourceCheckStatusResponse, destroy_with_associated_resource, path=["response"] + ) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_dangerous(self, async_client: AsyncGradient) -> None: + destroy_with_associated_resource = ( + await async_client.gpu_droplets.destroy_with_associated_resources.delete_dangerous( + droplet_id=3164444, + x_dangerous=True, + ) + ) + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete_dangerous(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.destroy_with_associated_resources.with_raw_response.delete_dangerous( + droplet_id=3164444, + x_dangerous=True, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = await response.parse() + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete_dangerous(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.delete_dangerous( + droplet_id=3164444, + x_dangerous=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = await response.parse() + assert destroy_with_associated_resource is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_selective(self, async_client: AsyncGradient) -> None: + destroy_with_associated_resource = ( + await async_client.gpu_droplets.destroy_with_associated_resources.delete_selective( + droplet_id=3164444, + ) + ) + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_selective_with_all_params(self, async_client: AsyncGradient) -> None: + destroy_with_associated_resource = ( + await async_client.gpu_droplets.destroy_with_associated_resources.delete_selective( + droplet_id=3164444, + floating_ips=["6186916"], + reserved_ips=["6186916"], + snapshots=["61486916"], + volume_snapshots=["edb0478d-7436-11ea-86e6-0a58ac144b91"], + volumes=["ba49449a-7435-11ea-b89e-0a58ac14480f"], + ) + ) + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete_selective(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.destroy_with_associated_resources.with_raw_response.delete_selective( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = await response.parse() + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete_selective(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.delete_selective( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = await response.parse() + assert destroy_with_associated_resource is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retry(self, async_client: AsyncGradient) -> None: + destroy_with_associated_resource = await async_client.gpu_droplets.destroy_with_associated_resources.retry( + 3164444, + ) + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retry(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.destroy_with_associated_resources.with_raw_response.retry( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + destroy_with_associated_resource = await response.parse() + assert destroy_with_associated_resource is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retry(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.destroy_with_associated_resources.with_streaming_response.retry( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + destroy_with_associated_resource = await response.parse() + assert destroy_with_associated_resource is None + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/test_firewalls.py b/tests/api_resources/gpu_droplets/test_firewalls.py new file mode 100644 index 00000000..3d8469b3 --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_firewalls.py @@ -0,0 +1,617 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + FirewallListResponse, + FirewallCreateResponse, + FirewallUpdateResponse, + FirewallRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestFirewalls: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + firewall = client.gpu_droplets.firewalls.create() + assert_matches_type(FirewallCreateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + firewall = client.gpu_droplets.firewalls.create( + body={ + "droplet_ids": [8043964], + "inbound_rules": [ + { + "ports": "80", + "protocol": "tcp", + "sources": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + }, + { + "ports": "22", + "protocol": "tcp", + "sources": { + "addresses": ["18.0.0.0/8"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["gateway"], + }, + }, + ], + "name": "firewall", + "outbound_rules": [ + { + "destinations": { + "addresses": ["0.0.0.0/0", "::/0"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + "ports": "80", + "protocol": "tcp", + } + ], + "tags": ["base-image", "prod"], + }, + ) + assert_matches_type(FirewallCreateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = response.parse() + assert_matches_type(FirewallCreateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = response.parse() + assert_matches_type(FirewallCreateResponse, firewall, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + firewall = client.gpu_droplets.firewalls.retrieve( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) + assert_matches_type(FirewallRetrieveResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.with_raw_response.retrieve( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = response.parse() + assert_matches_type(FirewallRetrieveResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.with_streaming_response.retrieve( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = response.parse() + assert_matches_type(FirewallRetrieveResponse, firewall, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + firewall = client.gpu_droplets.firewalls.update( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + firewall={"name": "frontend-firewall"}, + ) + assert_matches_type(FirewallUpdateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + firewall = client.gpu_droplets.firewalls.update( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + firewall={ + "droplet_ids": [8043964], + "inbound_rules": [ + { + "ports": "8080", + "protocol": "tcp", + "sources": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + }, + { + "ports": "22", + "protocol": "tcp", + "sources": { + "addresses": ["18.0.0.0/8"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["gateway"], + }, + }, + ], + "name": "frontend-firewall", + "outbound_rules": [ + { + "destinations": { + "addresses": ["0.0.0.0/0", "::/0"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + "ports": "8080", + "protocol": "tcp", + } + ], + "tags": ["frontend"], + }, + ) + assert_matches_type(FirewallUpdateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.with_raw_response.update( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + firewall={"name": "frontend-firewall"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = response.parse() + assert_matches_type(FirewallUpdateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.with_streaming_response.update( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + firewall={"name": "frontend-firewall"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = response.parse() + assert_matches_type(FirewallUpdateResponse, firewall, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.with_raw_response.update( + firewall_id="", + firewall={"name": "frontend-firewall"}, + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + firewall = client.gpu_droplets.firewalls.list() + assert_matches_type(FirewallListResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + firewall = client.gpu_droplets.firewalls.list( + page=1, + per_page=1, + ) + assert_matches_type(FirewallListResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = response.parse() + assert_matches_type(FirewallListResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = response.parse() + assert_matches_type(FirewallListResponse, firewall, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + firewall = client.gpu_droplets.firewalls.delete( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) + assert firewall is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.firewalls.with_raw_response.delete( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = response.parse() + assert firewall is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.firewalls.with_streaming_response.delete( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = response.parse() + assert firewall is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + client.gpu_droplets.firewalls.with_raw_response.delete( + "", + ) + + +class TestAsyncFirewalls: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + firewall = await async_client.gpu_droplets.firewalls.create() + assert_matches_type(FirewallCreateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + firewall = await async_client.gpu_droplets.firewalls.create( + body={ + "droplet_ids": [8043964], + "inbound_rules": [ + { + "ports": "80", + "protocol": "tcp", + "sources": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + }, + { + "ports": "22", + "protocol": "tcp", + "sources": { + "addresses": ["18.0.0.0/8"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["gateway"], + }, + }, + ], + "name": "firewall", + "outbound_rules": [ + { + "destinations": { + "addresses": ["0.0.0.0/0", "::/0"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + "ports": "80", + "protocol": "tcp", + } + ], + "tags": ["base-image", "prod"], + }, + ) + assert_matches_type(FirewallCreateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = await response.parse() + assert_matches_type(FirewallCreateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = await response.parse() + assert_matches_type(FirewallCreateResponse, firewall, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + firewall = await async_client.gpu_droplets.firewalls.retrieve( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) + assert_matches_type(FirewallRetrieveResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.with_raw_response.retrieve( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = await response.parse() + assert_matches_type(FirewallRetrieveResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.with_streaming_response.retrieve( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = await response.parse() + assert_matches_type(FirewallRetrieveResponse, firewall, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + firewall = await async_client.gpu_droplets.firewalls.update( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + firewall={"name": "frontend-firewall"}, + ) + assert_matches_type(FirewallUpdateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + firewall = await async_client.gpu_droplets.firewalls.update( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + firewall={ + "droplet_ids": [8043964], + "inbound_rules": [ + { + "ports": "8080", + "protocol": "tcp", + "sources": { + "addresses": ["1.2.3.4", "18.0.0.0/8"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + }, + { + "ports": "22", + "protocol": "tcp", + "sources": { + "addresses": ["18.0.0.0/8"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["gateway"], + }, + }, + ], + "name": "frontend-firewall", + "outbound_rules": [ + { + "destinations": { + "addresses": ["0.0.0.0/0", "::/0"], + "droplet_ids": [8043964], + "kubernetes_ids": ["41b74c5d-9bd0-5555-5555-a57c495b81a3"], + "load_balancer_uids": ["4de7ac8b-495b-4884-9a69-1050c6793cd6"], + "tags": ["base-image", "prod"], + }, + "ports": "8080", + "protocol": "tcp", + } + ], + "tags": ["frontend"], + }, + ) + assert_matches_type(FirewallUpdateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.with_raw_response.update( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + firewall={"name": "frontend-firewall"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = await response.parse() + assert_matches_type(FirewallUpdateResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.with_streaming_response.update( + firewall_id="bb4b2611-3d72-467b-8602-280330ecd65c", + firewall={"name": "frontend-firewall"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = await response.parse() + assert_matches_type(FirewallUpdateResponse, firewall, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.with_raw_response.update( + firewall_id="", + firewall={"name": "frontend-firewall"}, + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + firewall = await async_client.gpu_droplets.firewalls.list() + assert_matches_type(FirewallListResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + firewall = await async_client.gpu_droplets.firewalls.list( + page=1, + per_page=1, + ) + assert_matches_type(FirewallListResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = await response.parse() + assert_matches_type(FirewallListResponse, firewall, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = await response.parse() + assert_matches_type(FirewallListResponse, firewall, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + firewall = await async_client.gpu_droplets.firewalls.delete( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) + assert firewall is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.firewalls.with_raw_response.delete( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + firewall = await response.parse() + assert firewall is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.firewalls.with_streaming_response.delete( + "bb4b2611-3d72-467b-8602-280330ecd65c", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + firewall = await response.parse() + assert firewall is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `firewall_id` but received ''"): + await async_client.gpu_droplets.firewalls.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/gpu_droplets/test_floating_ips.py b/tests/api_resources/gpu_droplets/test_floating_ips.py new file mode 100644 index 00000000..3119bf28 --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_floating_ips.py @@ -0,0 +1,424 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + FloatingIPListResponse, + FloatingIPCreateResponse, + FloatingIPRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestFloatingIPs: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_1(self, client: Gradient) -> None: + floating_ip = client.gpu_droplets.floating_ips.create( + droplet_id=2457247, + ) + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.with_raw_response.create( + droplet_id=2457247, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = response.parse() + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.with_streaming_response.create( + droplet_id=2457247, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = response.parse() + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_2(self, client: Gradient) -> None: + floating_ip = client.gpu_droplets.floating_ips.create( + region="nyc3", + ) + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_2(self, client: Gradient) -> None: + floating_ip = client.gpu_droplets.floating_ips.create( + region="nyc3", + project_id="746c6152-2fa2-11ed-92d3-27aaa54e4988", + ) + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.with_raw_response.create( + region="nyc3", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = response.parse() + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.with_streaming_response.create( + region="nyc3", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = response.parse() + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + floating_ip = client.gpu_droplets.floating_ips.retrieve( + "45.55.96.47", + ) + assert_matches_type(FloatingIPRetrieveResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.with_raw_response.retrieve( + "45.55.96.47", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = response.parse() + assert_matches_type(FloatingIPRetrieveResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.with_streaming_response.retrieve( + "45.55.96.47", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = response.parse() + assert_matches_type(FloatingIPRetrieveResponse, floating_ip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + client.gpu_droplets.floating_ips.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + floating_ip = client.gpu_droplets.floating_ips.list() + assert_matches_type(FloatingIPListResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + floating_ip = client.gpu_droplets.floating_ips.list( + page=1, + per_page=1, + ) + assert_matches_type(FloatingIPListResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = response.parse() + assert_matches_type(FloatingIPListResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = response.parse() + assert_matches_type(FloatingIPListResponse, floating_ip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + floating_ip = client.gpu_droplets.floating_ips.delete( + "45.55.96.47", + ) + assert floating_ip is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.floating_ips.with_raw_response.delete( + "45.55.96.47", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = response.parse() + assert floating_ip is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.floating_ips.with_streaming_response.delete( + "45.55.96.47", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = response.parse() + assert floating_ip is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + client.gpu_droplets.floating_ips.with_raw_response.delete( + "", + ) + + +class TestAsyncFloatingIPs: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncGradient) -> None: + floating_ip = await async_client.gpu_droplets.floating_ips.create( + droplet_id=2457247, + ) + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.with_raw_response.create( + droplet_id=2457247, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = await response.parse() + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.with_streaming_response.create( + droplet_id=2457247, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = await response.parse() + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncGradient) -> None: + floating_ip = await async_client.gpu_droplets.floating_ips.create( + region="nyc3", + ) + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + floating_ip = await async_client.gpu_droplets.floating_ips.create( + region="nyc3", + project_id="746c6152-2fa2-11ed-92d3-27aaa54e4988", + ) + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.with_raw_response.create( + region="nyc3", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = await response.parse() + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.with_streaming_response.create( + region="nyc3", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = await response.parse() + assert_matches_type(FloatingIPCreateResponse, floating_ip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + floating_ip = await async_client.gpu_droplets.floating_ips.retrieve( + "45.55.96.47", + ) + assert_matches_type(FloatingIPRetrieveResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.with_raw_response.retrieve( + "45.55.96.47", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = await response.parse() + assert_matches_type(FloatingIPRetrieveResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.with_streaming_response.retrieve( + "45.55.96.47", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = await response.parse() + assert_matches_type(FloatingIPRetrieveResponse, floating_ip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + await async_client.gpu_droplets.floating_ips.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + floating_ip = await async_client.gpu_droplets.floating_ips.list() + assert_matches_type(FloatingIPListResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + floating_ip = await async_client.gpu_droplets.floating_ips.list( + page=1, + per_page=1, + ) + assert_matches_type(FloatingIPListResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = await response.parse() + assert_matches_type(FloatingIPListResponse, floating_ip, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = await response.parse() + assert_matches_type(FloatingIPListResponse, floating_ip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + floating_ip = await async_client.gpu_droplets.floating_ips.delete( + "45.55.96.47", + ) + assert floating_ip is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.floating_ips.with_raw_response.delete( + "45.55.96.47", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + floating_ip = await response.parse() + assert floating_ip is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.floating_ips.with_streaming_response.delete( + "45.55.96.47", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + floating_ip = await response.parse() + assert floating_ip is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip` but received ''"): + await async_client.gpu_droplets.floating_ips.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/gpu_droplets/test_images.py b/tests/api_resources/gpu_droplets/test_images.py new file mode 100644 index 00000000..4c4146e2 --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_images.py @@ -0,0 +1,417 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + ImageListResponse, + ImageCreateResponse, + ImageUpdateResponse, + ImageRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestImages: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + image = client.gpu_droplets.images.create() + assert_matches_type(ImageCreateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + image = client.gpu_droplets.images.create( + description=" ", + distribution="Ubuntu", + name="Nifty New Snapshot", + region="nyc3", + tags=["base-image", "prod"], + url="http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img", + ) + assert_matches_type(ImageCreateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.gpu_droplets.images.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImageCreateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.gpu_droplets.images.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImageCreateResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + image = client.gpu_droplets.images.retrieve( + 0, + ) + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.images.with_raw_response.retrieve( + 0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.images.with_streaming_response.retrieve( + 0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + image = client.gpu_droplets.images.update( + image_id=62137902, + ) + assert_matches_type(ImageUpdateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + image = client.gpu_droplets.images.update( + image_id=62137902, + description=" ", + distribution="Ubuntu", + name="Nifty New Snapshot", + ) + assert_matches_type(ImageUpdateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.gpu_droplets.images.with_raw_response.update( + image_id=62137902, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImageUpdateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.gpu_droplets.images.with_streaming_response.update( + image_id=62137902, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImageUpdateResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + image = client.gpu_droplets.images.list() + assert_matches_type(ImageListResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + image = client.gpu_droplets.images.list( + page=1, + per_page=1, + private=True, + tag_name="tag_name", + type="application", + ) + assert_matches_type(ImageListResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.images.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImageListResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.images.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImageListResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + image = client.gpu_droplets.images.delete( + 62137902, + ) + assert image is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.images.with_raw_response.delete( + 62137902, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert image is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.images.with_streaming_response.delete( + 62137902, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert image is None + + assert cast(Any, response.is_closed) is True + + +class TestAsyncImages: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + image = await async_client.gpu_droplets.images.create() + assert_matches_type(ImageCreateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + image = await async_client.gpu_droplets.images.create( + description=" ", + distribution="Ubuntu", + name="Nifty New Snapshot", + region="nyc3", + tags=["base-image", "prod"], + url="http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img", + ) + assert_matches_type(ImageCreateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(ImageCreateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImageCreateResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + image = await async_client.gpu_droplets.images.retrieve( + 0, + ) + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.with_raw_response.retrieve( + 0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.with_streaming_response.retrieve( + 0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + image = await async_client.gpu_droplets.images.update( + image_id=62137902, + ) + assert_matches_type(ImageUpdateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + image = await async_client.gpu_droplets.images.update( + image_id=62137902, + description=" ", + distribution="Ubuntu", + name="Nifty New Snapshot", + ) + assert_matches_type(ImageUpdateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.with_raw_response.update( + image_id=62137902, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(ImageUpdateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.with_streaming_response.update( + image_id=62137902, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImageUpdateResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + image = await async_client.gpu_droplets.images.list() + assert_matches_type(ImageListResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + image = await async_client.gpu_droplets.images.list( + page=1, + per_page=1, + private=True, + tag_name="tag_name", + type="application", + ) + assert_matches_type(ImageListResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(ImageListResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImageListResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + image = await async_client.gpu_droplets.images.delete( + 62137902, + ) + assert image is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.images.with_raw_response.delete( + 62137902, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert image is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.images.with_streaming_response.delete( + 62137902, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert image is None + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/test_load_balancers.py b/tests/api_resources/gpu_droplets/test_load_balancers.py new file mode 100644 index 00000000..363520e4 --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_load_balancers.py @@ -0,0 +1,1443 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + LoadBalancerListResponse, + LoadBalancerCreateResponse, + LoadBalancerUpdateResponse, + LoadBalancerRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLoadBalancers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_1(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_1(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "tls_passthrough": False, + } + ], + algorithm="round_robin", + disable_lets_encrypt_dns_records=True, + domains=[ + { + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "is_managed": True, + "name": "example.com", + } + ], + droplet_ids=[3164444, 3164445], + enable_backend_keepalive=True, + enable_proxy_protocol=True, + firewall={ + "allow": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + "deny": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + }, + glb_settings={ + "cdn": {"is_enabled": True}, + "failover_threshold": 50, + "region_priorities": { + "nyc1": 1, + "fra1": 2, + "sgp1": 3, + }, + "target_port": 80, + "target_protocol": "http", + }, + health_check={ + "check_interval_seconds": 10, + "healthy_threshold": 3, + "path": "/", + "port": 80, + "protocol": "http", + "response_timeout_seconds": 5, + "unhealthy_threshold": 5, + }, + http_idle_timeout_seconds=90, + name="example-lb-01", + network="EXTERNAL", + network_stack="IPV4", + project_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + redirect_http_to_https=True, + region="nyc3", + size="lb-small", + size_unit=3, + sticky_sessions={ + "cookie_name": "DO-LB", + "cookie_ttl_seconds": 300, + "type": "cookies", + }, + target_load_balancer_ids=["7dbf91fe-cbdb-48dc-8290-c3a181554905", "996fa239-fac3-42a2-b9a1-9fa822268b7a"], + tls_cipher_policy="STRONG", + type="REGIONAL", + vpc_uuid="c33931f2-a26a-4e61-b85c-4e95a2ec431b", + ) + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.with_raw_response.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = response.parse() + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.with_streaming_response.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = response.parse() + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_2(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_2(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "tls_passthrough": False, + } + ], + algorithm="round_robin", + disable_lets_encrypt_dns_records=True, + domains=[ + { + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "is_managed": True, + "name": "example.com", + } + ], + enable_backend_keepalive=True, + enable_proxy_protocol=True, + firewall={ + "allow": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + "deny": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + }, + glb_settings={ + "cdn": {"is_enabled": True}, + "failover_threshold": 50, + "region_priorities": { + "nyc1": 1, + "fra1": 2, + "sgp1": 3, + }, + "target_port": 80, + "target_protocol": "http", + }, + health_check={ + "check_interval_seconds": 10, + "healthy_threshold": 3, + "path": "/", + "port": 80, + "protocol": "http", + "response_timeout_seconds": 5, + "unhealthy_threshold": 5, + }, + http_idle_timeout_seconds=90, + name="example-lb-01", + network="EXTERNAL", + network_stack="IPV4", + project_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + redirect_http_to_https=True, + region="nyc3", + size="lb-small", + size_unit=3, + sticky_sessions={ + "cookie_name": "DO-LB", + "cookie_ttl_seconds": 300, + "type": "cookies", + }, + tag="prod:web", + target_load_balancer_ids=["7dbf91fe-cbdb-48dc-8290-c3a181554905", "996fa239-fac3-42a2-b9a1-9fa822268b7a"], + tls_cipher_policy="STRONG", + type="REGIONAL", + vpc_uuid="c33931f2-a26a-4e61-b85c-4e95a2ec431b", + ) + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.with_raw_response.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = response.parse() + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.with_streaming_response.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = response.parse() + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.retrieve( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + assert_matches_type(LoadBalancerRetrieveResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.with_raw_response.retrieve( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = response.parse() + assert_matches_type(LoadBalancerRetrieveResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.with_streaming_response.retrieve( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = response.parse() + assert_matches_type(LoadBalancerRetrieveResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_overload_1(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params_overload_1(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "tls_passthrough": False, + } + ], + algorithm="round_robin", + disable_lets_encrypt_dns_records=True, + domains=[ + { + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "is_managed": True, + "name": "example.com", + } + ], + droplet_ids=[3164444, 3164445], + enable_backend_keepalive=True, + enable_proxy_protocol=True, + firewall={ + "allow": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + "deny": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + }, + glb_settings={ + "cdn": {"is_enabled": True}, + "failover_threshold": 50, + "region_priorities": { + "nyc1": 1, + "fra1": 2, + "sgp1": 3, + }, + "target_port": 80, + "target_protocol": "http", + }, + health_check={ + "check_interval_seconds": 10, + "healthy_threshold": 3, + "path": "/", + "port": 80, + "protocol": "http", + "response_timeout_seconds": 5, + "unhealthy_threshold": 5, + }, + http_idle_timeout_seconds=90, + name="example-lb-01", + network="EXTERNAL", + network_stack="IPV4", + project_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + redirect_http_to_https=True, + region="nyc3", + size="lb-small", + size_unit=3, + sticky_sessions={ + "cookie_name": "DO-LB", + "cookie_ttl_seconds": 300, + "type": "cookies", + }, + target_load_balancer_ids=["7dbf91fe-cbdb-48dc-8290-c3a181554905", "996fa239-fac3-42a2-b9a1-9fa822268b7a"], + tls_cipher_policy="STRONG", + type="REGIONAL", + vpc_uuid="c33931f2-a26a-4e61-b85c-4e95a2ec431b", + ) + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.with_raw_response.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = response.parse() + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.with_streaming_response.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = response.parse() + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update_overload_1(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.with_raw_response.update( + lb_id="", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_overload_2(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params_overload_2(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "tls_passthrough": False, + } + ], + algorithm="round_robin", + disable_lets_encrypt_dns_records=True, + domains=[ + { + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "is_managed": True, + "name": "example.com", + } + ], + enable_backend_keepalive=True, + enable_proxy_protocol=True, + firewall={ + "allow": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + "deny": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + }, + glb_settings={ + "cdn": {"is_enabled": True}, + "failover_threshold": 50, + "region_priorities": { + "nyc1": 1, + "fra1": 2, + "sgp1": 3, + }, + "target_port": 80, + "target_protocol": "http", + }, + health_check={ + "check_interval_seconds": 10, + "healthy_threshold": 3, + "path": "/", + "port": 80, + "protocol": "http", + "response_timeout_seconds": 5, + "unhealthy_threshold": 5, + }, + http_idle_timeout_seconds=90, + name="example-lb-01", + network="EXTERNAL", + network_stack="IPV4", + project_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + redirect_http_to_https=True, + region="nyc3", + size="lb-small", + size_unit=3, + sticky_sessions={ + "cookie_name": "DO-LB", + "cookie_ttl_seconds": 300, + "type": "cookies", + }, + tag="prod:web", + target_load_balancer_ids=["7dbf91fe-cbdb-48dc-8290-c3a181554905", "996fa239-fac3-42a2-b9a1-9fa822268b7a"], + tls_cipher_policy="STRONG", + type="REGIONAL", + vpc_uuid="c33931f2-a26a-4e61-b85c-4e95a2ec431b", + ) + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.with_raw_response.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = response.parse() + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.with_streaming_response.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = response.parse() + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update_overload_2(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.with_raw_response.update( + lb_id="", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.list() + assert_matches_type(LoadBalancerListResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.list( + page=1, + per_page=1, + ) + assert_matches_type(LoadBalancerListResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = response.parse() + assert_matches_type(LoadBalancerListResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = response.parse() + assert_matches_type(LoadBalancerListResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.delete( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + assert load_balancer is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.with_raw_response.delete( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = response.parse() + assert load_balancer is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.with_streaming_response.delete( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = response.parse() + assert load_balancer is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_cache(self, client: Gradient) -> None: + load_balancer = client.gpu_droplets.load_balancers.delete_cache( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + assert load_balancer is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete_cache(self, client: Gradient) -> None: + response = client.gpu_droplets.load_balancers.with_raw_response.delete_cache( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = response.parse() + assert load_balancer is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete_cache(self, client: Gradient) -> None: + with client.gpu_droplets.load_balancers.with_streaming_response.delete_cache( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = response.parse() + assert load_balancer is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete_cache(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + client.gpu_droplets.load_balancers.with_raw_response.delete_cache( + "", + ) + + +class TestAsyncLoadBalancers: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "tls_passthrough": False, + } + ], + algorithm="round_robin", + disable_lets_encrypt_dns_records=True, + domains=[ + { + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "is_managed": True, + "name": "example.com", + } + ], + droplet_ids=[3164444, 3164445], + enable_backend_keepalive=True, + enable_proxy_protocol=True, + firewall={ + "allow": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + "deny": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + }, + glb_settings={ + "cdn": {"is_enabled": True}, + "failover_threshold": 50, + "region_priorities": { + "nyc1": 1, + "fra1": 2, + "sgp1": 3, + }, + "target_port": 80, + "target_protocol": "http", + }, + health_check={ + "check_interval_seconds": 10, + "healthy_threshold": 3, + "path": "/", + "port": 80, + "protocol": "http", + "response_timeout_seconds": 5, + "unhealthy_threshold": 5, + }, + http_idle_timeout_seconds=90, + name="example-lb-01", + network="EXTERNAL", + network_stack="IPV4", + project_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + redirect_http_to_https=True, + region="nyc3", + size="lb-small", + size_unit=3, + sticky_sessions={ + "cookie_name": "DO-LB", + "cookie_ttl_seconds": 300, + "type": "cookies", + }, + target_load_balancer_ids=["7dbf91fe-cbdb-48dc-8290-c3a181554905", "996fa239-fac3-42a2-b9a1-9fa822268b7a"], + tls_cipher_policy="STRONG", + type="REGIONAL", + vpc_uuid="c33931f2-a26a-4e61-b85c-4e95a2ec431b", + ) + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.with_raw_response.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = await response.parse() + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.with_streaming_response.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = await response.parse() + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "tls_passthrough": False, + } + ], + algorithm="round_robin", + disable_lets_encrypt_dns_records=True, + domains=[ + { + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "is_managed": True, + "name": "example.com", + } + ], + enable_backend_keepalive=True, + enable_proxy_protocol=True, + firewall={ + "allow": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + "deny": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + }, + glb_settings={ + "cdn": {"is_enabled": True}, + "failover_threshold": 50, + "region_priorities": { + "nyc1": 1, + "fra1": 2, + "sgp1": 3, + }, + "target_port": 80, + "target_protocol": "http", + }, + health_check={ + "check_interval_seconds": 10, + "healthy_threshold": 3, + "path": "/", + "port": 80, + "protocol": "http", + "response_timeout_seconds": 5, + "unhealthy_threshold": 5, + }, + http_idle_timeout_seconds=90, + name="example-lb-01", + network="EXTERNAL", + network_stack="IPV4", + project_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + redirect_http_to_https=True, + region="nyc3", + size="lb-small", + size_unit=3, + sticky_sessions={ + "cookie_name": "DO-LB", + "cookie_ttl_seconds": 300, + "type": "cookies", + }, + tag="prod:web", + target_load_balancer_ids=["7dbf91fe-cbdb-48dc-8290-c3a181554905", "996fa239-fac3-42a2-b9a1-9fa822268b7a"], + tls_cipher_policy="STRONG", + type="REGIONAL", + vpc_uuid="c33931f2-a26a-4e61-b85c-4e95a2ec431b", + ) + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.with_raw_response.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = await response.parse() + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.with_streaming_response.create( + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = await response.parse() + assert_matches_type(LoadBalancerCreateResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.retrieve( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + assert_matches_type(LoadBalancerRetrieveResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.with_raw_response.retrieve( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = await response.parse() + assert_matches_type(LoadBalancerRetrieveResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.with_streaming_response.retrieve( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = await response.parse() + assert_matches_type(LoadBalancerRetrieveResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_overload_1(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "tls_passthrough": False, + } + ], + algorithm="round_robin", + disable_lets_encrypt_dns_records=True, + domains=[ + { + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "is_managed": True, + "name": "example.com", + } + ], + droplet_ids=[3164444, 3164445], + enable_backend_keepalive=True, + enable_proxy_protocol=True, + firewall={ + "allow": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + "deny": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + }, + glb_settings={ + "cdn": {"is_enabled": True}, + "failover_threshold": 50, + "region_priorities": { + "nyc1": 1, + "fra1": 2, + "sgp1": 3, + }, + "target_port": 80, + "target_protocol": "http", + }, + health_check={ + "check_interval_seconds": 10, + "healthy_threshold": 3, + "path": "/", + "port": 80, + "protocol": "http", + "response_timeout_seconds": 5, + "unhealthy_threshold": 5, + }, + http_idle_timeout_seconds=90, + name="example-lb-01", + network="EXTERNAL", + network_stack="IPV4", + project_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + redirect_http_to_https=True, + region="nyc3", + size="lb-small", + size_unit=3, + sticky_sessions={ + "cookie_name": "DO-LB", + "cookie_ttl_seconds": 300, + "type": "cookies", + }, + target_load_balancer_ids=["7dbf91fe-cbdb-48dc-8290-c3a181554905", "996fa239-fac3-42a2-b9a1-9fa822268b7a"], + tls_cipher_policy="STRONG", + type="REGIONAL", + vpc_uuid="c33931f2-a26a-4e61-b85c-4e95a2ec431b", + ) + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.with_raw_response.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = await response.parse() + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.with_streaming_response.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = await response.parse() + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update_overload_1(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.with_raw_response.update( + lb_id="", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_overload_2(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "tls_passthrough": False, + } + ], + algorithm="round_robin", + disable_lets_encrypt_dns_records=True, + domains=[ + { + "certificate_id": "892071a0-bb95-49bc-8021-3afd67a210bf", + "is_managed": True, + "name": "example.com", + } + ], + enable_backend_keepalive=True, + enable_proxy_protocol=True, + firewall={ + "allow": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + "deny": ["ip:1.2.3.4", "cidr:2.3.0.0/16"], + }, + glb_settings={ + "cdn": {"is_enabled": True}, + "failover_threshold": 50, + "region_priorities": { + "nyc1": 1, + "fra1": 2, + "sgp1": 3, + }, + "target_port": 80, + "target_protocol": "http", + }, + health_check={ + "check_interval_seconds": 10, + "healthy_threshold": 3, + "path": "/", + "port": 80, + "protocol": "http", + "response_timeout_seconds": 5, + "unhealthy_threshold": 5, + }, + http_idle_timeout_seconds=90, + name="example-lb-01", + network="EXTERNAL", + network_stack="IPV4", + project_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + redirect_http_to_https=True, + region="nyc3", + size="lb-small", + size_unit=3, + sticky_sessions={ + "cookie_name": "DO-LB", + "cookie_ttl_seconds": 300, + "type": "cookies", + }, + tag="prod:web", + target_load_balancer_ids=["7dbf91fe-cbdb-48dc-8290-c3a181554905", "996fa239-fac3-42a2-b9a1-9fa822268b7a"], + tls_cipher_policy="STRONG", + type="REGIONAL", + vpc_uuid="c33931f2-a26a-4e61-b85c-4e95a2ec431b", + ) + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.with_raw_response.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = await response.parse() + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.with_streaming_response.update( + lb_id="4de7ac8b-495b-4884-9a69-1050c6793cd6", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = await response.parse() + assert_matches_type(LoadBalancerUpdateResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update_overload_2(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.with_raw_response.update( + lb_id="", + forwarding_rules=[ + { + "entry_port": 443, + "entry_protocol": "https", + "target_port": 80, + "target_protocol": "http", + } + ], + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.list() + assert_matches_type(LoadBalancerListResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.list( + page=1, + per_page=1, + ) + assert_matches_type(LoadBalancerListResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = await response.parse() + assert_matches_type(LoadBalancerListResponse, load_balancer, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = await response.parse() + assert_matches_type(LoadBalancerListResponse, load_balancer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.delete( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + assert load_balancer is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.with_raw_response.delete( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = await response.parse() + assert load_balancer is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.with_streaming_response.delete( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = await response.parse() + assert load_balancer is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_cache(self, async_client: AsyncGradient) -> None: + load_balancer = await async_client.gpu_droplets.load_balancers.delete_cache( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + assert load_balancer is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete_cache(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.load_balancers.with_raw_response.delete_cache( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + load_balancer = await response.parse() + assert load_balancer is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete_cache(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.load_balancers.with_streaming_response.delete_cache( + "4de7ac8b-495b-4884-9a69-1050c6793cd6", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + load_balancer = await response.parse() + assert load_balancer is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete_cache(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `lb_id` but received ''"): + await async_client.gpu_droplets.load_balancers.with_raw_response.delete_cache( + "", + ) diff --git a/tests/api_resources/gpu_droplets/test_sizes.py b/tests/api_resources/gpu_droplets/test_sizes.py new file mode 100644 index 00000000..7fc4fe80 --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_sizes.py @@ -0,0 +1,98 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import SizeListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSizes: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + size = client.gpu_droplets.sizes.list() + assert_matches_type(SizeListResponse, size, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + size = client.gpu_droplets.sizes.list( + page=1, + per_page=1, + ) + assert_matches_type(SizeListResponse, size, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.sizes.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + size = response.parse() + assert_matches_type(SizeListResponse, size, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.sizes.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + size = response.parse() + assert_matches_type(SizeListResponse, size, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSizes: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + size = await async_client.gpu_droplets.sizes.list() + assert_matches_type(SizeListResponse, size, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + size = await async_client.gpu_droplets.sizes.list( + page=1, + per_page=1, + ) + assert_matches_type(SizeListResponse, size, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.sizes.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + size = await response.parse() + assert_matches_type(SizeListResponse, size, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.sizes.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + size = await response.parse() + assert_matches_type(SizeListResponse, size, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/test_snapshots.py b/tests/api_resources/gpu_droplets/test_snapshots.py new file mode 100644 index 00000000..5f8da45a --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_snapshots.py @@ -0,0 +1,236 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import SnapshotListResponse, SnapshotRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSnapshots: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.snapshots.retrieve( + 6372321, + ) + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.snapshots.with_raw_response.retrieve( + 6372321, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.snapshots.with_streaming_response.retrieve( + 6372321, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.snapshots.list() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.snapshots.list( + page=1, + per_page=1, + resource_type="droplet", + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.snapshots.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.snapshots.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.snapshots.delete( + 6372321, + ) + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.snapshots.with_raw_response.delete( + 6372321, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.snapshots.with_streaming_response.delete( + 6372321, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert snapshot is None + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSnapshots: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.snapshots.retrieve( + 6372321, + ) + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.snapshots.with_raw_response.retrieve( + 6372321, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.snapshots.with_streaming_response.retrieve( + 6372321, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.snapshots.list() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.snapshots.list( + page=1, + per_page=1, + resource_type="droplet", + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.snapshots.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.snapshots.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.snapshots.delete( + 6372321, + ) + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.snapshots.with_raw_response.delete( + 6372321, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.snapshots.with_streaming_response.delete( + 6372321, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert snapshot is None + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/test_volumes.py b/tests/api_resources/gpu_droplets/test_volumes.py new file mode 100644 index 00000000..8243625d --- /dev/null +++ b/tests/api_resources/gpu_droplets/test_volumes.py @@ -0,0 +1,568 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets import ( + VolumeListResponse, + VolumeCreateResponse, + VolumeRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestVolumes: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_1(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_1(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.create( + name="example", + region="nyc3", + size_gigabytes=10, + description="Block store for examples", + filesystem_label="example", + filesystem_type="ext4", + snapshot_id="b0798135-fb76-11eb-946a-0a58ac146f33", + tags=["base-image", "prod"], + ) + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.with_raw_response.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = response.parse() + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.with_streaming_response.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = response.parse() + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_2(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_2(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.create( + name="example", + region="nyc3", + size_gigabytes=10, + description="Block store for examples", + filesystem_label="example", + filesystem_type="ext4", + snapshot_id="b0798135-fb76-11eb-946a-0a58ac146f33", + tags=["base-image", "prod"], + ) + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.with_raw_response.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = response.parse() + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.with_streaming_response.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = response.parse() + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.retrieve( + "7724db7c-e098-11e5-b522-000f53304e51", + ) + assert_matches_type(VolumeRetrieveResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.with_raw_response.retrieve( + "7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = response.parse() + assert_matches_type(VolumeRetrieveResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.with_streaming_response.retrieve( + "7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = response.parse() + assert_matches_type(VolumeRetrieveResponse, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.list() + assert_matches_type(VolumeListResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.list( + name="name", + page=1, + per_page=1, + region="nyc3", + ) + assert_matches_type(VolumeListResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = response.parse() + assert_matches_type(VolumeListResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = response.parse() + assert_matches_type(VolumeListResponse, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.delete( + "7724db7c-e098-11e5-b522-000f53304e51", + ) + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.with_raw_response.delete( + "7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = response.parse() + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.with_streaming_response.delete( + "7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = response.parse() + assert volume is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_by_name(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.delete_by_name() + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_by_name_with_all_params(self, client: Gradient) -> None: + volume = client.gpu_droplets.volumes.delete_by_name( + name="name", + region="nyc3", + ) + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete_by_name(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.with_raw_response.delete_by_name() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = response.parse() + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete_by_name(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.with_streaming_response.delete_by_name() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = response.parse() + assert volume is None + + assert cast(Any, response.is_closed) is True + + +class TestAsyncVolumes: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.create( + name="example", + region="nyc3", + size_gigabytes=10, + description="Block store for examples", + filesystem_label="example", + filesystem_type="ext4", + snapshot_id="b0798135-fb76-11eb-946a-0a58ac146f33", + tags=["base-image", "prod"], + ) + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.with_raw_response.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = await response.parse() + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.with_streaming_response.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = await response.parse() + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.create( + name="example", + region="nyc3", + size_gigabytes=10, + description="Block store for examples", + filesystem_label="example", + filesystem_type="ext4", + snapshot_id="b0798135-fb76-11eb-946a-0a58ac146f33", + tags=["base-image", "prod"], + ) + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.with_raw_response.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = await response.parse() + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.with_streaming_response.create( + name="example", + region="nyc3", + size_gigabytes=10, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = await response.parse() + assert_matches_type(VolumeCreateResponse, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.retrieve( + "7724db7c-e098-11e5-b522-000f53304e51", + ) + assert_matches_type(VolumeRetrieveResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.with_raw_response.retrieve( + "7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = await response.parse() + assert_matches_type(VolumeRetrieveResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.with_streaming_response.retrieve( + "7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = await response.parse() + assert_matches_type(VolumeRetrieveResponse, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.list() + assert_matches_type(VolumeListResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.list( + name="name", + page=1, + per_page=1, + region="nyc3", + ) + assert_matches_type(VolumeListResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = await response.parse() + assert_matches_type(VolumeListResponse, volume, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = await response.parse() + assert_matches_type(VolumeListResponse, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.delete( + "7724db7c-e098-11e5-b522-000f53304e51", + ) + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.with_raw_response.delete( + "7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = await response.parse() + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.with_streaming_response.delete( + "7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = await response.parse() + assert volume is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_by_name(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.delete_by_name() + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_by_name_with_all_params(self, async_client: AsyncGradient) -> None: + volume = await async_client.gpu_droplets.volumes.delete_by_name( + name="name", + region="nyc3", + ) + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete_by_name(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.with_raw_response.delete_by_name() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = await response.parse() + assert volume is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete_by_name(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.with_streaming_response.delete_by_name() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = await response.parse() + assert volume is None + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/volumes/__init__.py b/tests/api_resources/gpu_droplets/volumes/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/gpu_droplets/volumes/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/gpu_droplets/volumes/test_actions.py b/tests/api_resources/gpu_droplets/volumes/test_actions.py new file mode 100644 index 00000000..7159db48 --- /dev/null +++ b/tests/api_resources/gpu_droplets/volumes/test_actions.py @@ -0,0 +1,825 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets.volumes import ( + ActionListResponse, + ActionRetrieveResponse, + ActionInitiateByIDResponse, + ActionInitiateByNameResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestActions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.retrieve( + action_id=36804636, + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.retrieve( + action_id=36804636, + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + page=1, + per_page=1, + ) + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.actions.with_raw_response.retrieve( + action_id=36804636, + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.actions.with_streaming_response.retrieve( + action_id=36804636, + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.actions.with_raw_response.retrieve( + action_id=36804636, + volume_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + page=1, + per_page=1, + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.actions.with_raw_response.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.actions.with_streaming_response.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.actions.with_raw_response.list( + volume_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_id_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_id_with_all_params_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + page=1, + per_page=1, + region="nyc3", + tags=["base-image", "prod"], + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_by_id_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_by_id_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_initiate_by_id_overload_1(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="", + droplet_id=11612190, + type="attach", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_id_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_id_with_all_params_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + page=1, + per_page=1, + region="nyc3", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_by_id_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_by_id_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_initiate_by_id_overload_2(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="", + droplet_id=11612190, + type="attach", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_id_overload_3(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + size_gigabytes=16384, + type="attach", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_id_with_all_params_overload_3(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + size_gigabytes=16384, + type="attach", + page=1, + per_page=1, + region="nyc3", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_by_id_overload_3(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + size_gigabytes=16384, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_by_id_overload_3(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + size_gigabytes=16384, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_initiate_by_id_overload_3(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="", + size_gigabytes=16384, + type="attach", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_name_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_name( + droplet_id=11612190, + type="attach", + ) + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_name_with_all_params_overload_1(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_name( + droplet_id=11612190, + type="attach", + page=1, + per_page=1, + region="nyc3", + tags=["base-image", "prod"], + ) + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_by_name_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_name( + droplet_id=11612190, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_by_name_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_name( + droplet_id=11612190, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_name_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_name( + droplet_id=11612190, + type="attach", + ) + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_by_name_with_all_params_overload_2(self, client: Gradient) -> None: + action = client.gpu_droplets.volumes.actions.initiate_by_name( + droplet_id=11612190, + type="attach", + page=1, + per_page=1, + region="nyc3", + ) + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_by_name_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_name( + droplet_id=11612190, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = response.parse() + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_by_name_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_name( + droplet_id=11612190, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = response.parse() + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncActions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.retrieve( + action_id=36804636, + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.retrieve( + action_id=36804636, + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + page=1, + per_page=1, + ) + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.actions.with_raw_response.retrieve( + action_id=36804636, + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.actions.with_streaming_response.retrieve( + action_id=36804636, + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionRetrieveResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.actions.with_raw_response.retrieve( + action_id=36804636, + volume_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + page=1, + per_page=1, + ) + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.actions.with_raw_response.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.actions.with_streaming_response.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionListResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.actions.with_raw_response.list( + volume_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_id_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_id_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + page=1, + per_page=1, + region="nyc3", + tags=["base-image", "prod"], + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_by_id_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_by_id_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_initiate_by_id_overload_1(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="", + droplet_id=11612190, + type="attach", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_id_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_id_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + page=1, + per_page=1, + region="nyc3", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_by_id_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_by_id_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + droplet_id=11612190, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_initiate_by_id_overload_2(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="", + droplet_id=11612190, + type="attach", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_id_overload_3(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + size_gigabytes=16384, + type="attach", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_id_with_all_params_overload_3(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + size_gigabytes=16384, + type="attach", + page=1, + per_page=1, + region="nyc3", + ) + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_by_id_overload_3(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + size_gigabytes=16384, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_by_id_overload_3(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_id( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + size_gigabytes=16384, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateByIDResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_initiate_by_id_overload_3(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_id( + volume_id="", + size_gigabytes=16384, + type="attach", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_name_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_name( + droplet_id=11612190, + type="attach", + ) + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_name_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_name( + droplet_id=11612190, + type="attach", + page=1, + per_page=1, + region="nyc3", + tags=["base-image", "prod"], + ) + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_by_name_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_name( + droplet_id=11612190, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_by_name_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_name( + droplet_id=11612190, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_name_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_name( + droplet_id=11612190, + type="attach", + ) + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_by_name_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + action = await async_client.gpu_droplets.volumes.actions.initiate_by_name( + droplet_id=11612190, + type="attach", + page=1, + per_page=1, + region="nyc3", + ) + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_by_name_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.actions.with_raw_response.initiate_by_name( + droplet_id=11612190, + type="attach", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + action = await response.parse() + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_by_name_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.actions.with_streaming_response.initiate_by_name( + droplet_id=11612190, + type="attach", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + action = await response.parse() + assert_matches_type(ActionInitiateByNameResponse, action, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/gpu_droplets/volumes/test_snapshots.py b/tests/api_resources/gpu_droplets/volumes/test_snapshots.py new file mode 100644 index 00000000..ec157513 --- /dev/null +++ b/tests/api_resources/gpu_droplets/volumes/test_snapshots.py @@ -0,0 +1,412 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.gpu_droplets.volumes import ( + SnapshotListResponse, + SnapshotCreateResponse, + SnapshotRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSnapshots: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.volumes.snapshots.create( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + name="big-data-snapshot1475261774", + ) + assert_matches_type(SnapshotCreateResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.volumes.snapshots.create( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + name="big-data-snapshot1475261774", + tags=["base-image", "prod"], + ) + assert_matches_type(SnapshotCreateResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.snapshots.with_raw_response.create( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + name="big-data-snapshot1475261774", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert_matches_type(SnapshotCreateResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.snapshots.with_streaming_response.create( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + name="big-data-snapshot1475261774", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert_matches_type(SnapshotCreateResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_create(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.snapshots.with_raw_response.create( + volume_id="", + name="big-data-snapshot1475261774", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.volumes.snapshots.retrieve( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.snapshots.with_raw_response.retrieve( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.snapshots.with_streaming_response.retrieve( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `snapshot_id` but received ''"): + client.gpu_droplets.volumes.snapshots.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.volumes.snapshots.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.volumes.snapshots.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + page=1, + per_page=1, + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.snapshots.with_raw_response.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.snapshots.with_streaming_response.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + client.gpu_droplets.volumes.snapshots.with_raw_response.list( + volume_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + snapshot = client.gpu_droplets.volumes.snapshots.delete( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.volumes.snapshots.with_raw_response.delete( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.volumes.snapshots.with_streaming_response.delete( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert snapshot is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `snapshot_id` but received ''"): + client.gpu_droplets.volumes.snapshots.with_raw_response.delete( + "", + ) + + +class TestAsyncSnapshots: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.volumes.snapshots.create( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + name="big-data-snapshot1475261774", + ) + assert_matches_type(SnapshotCreateResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.volumes.snapshots.create( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + name="big-data-snapshot1475261774", + tags=["base-image", "prod"], + ) + assert_matches_type(SnapshotCreateResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.snapshots.with_raw_response.create( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + name="big-data-snapshot1475261774", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert_matches_type(SnapshotCreateResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.snapshots.with_streaming_response.create( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + name="big-data-snapshot1475261774", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert_matches_type(SnapshotCreateResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_create(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.snapshots.with_raw_response.create( + volume_id="", + name="big-data-snapshot1475261774", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.volumes.snapshots.retrieve( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.snapshots.with_raw_response.retrieve( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.snapshots.with_streaming_response.retrieve( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `snapshot_id` but received ''"): + await async_client.gpu_droplets.volumes.snapshots.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.volumes.snapshots.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.volumes.snapshots.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + page=1, + per_page=1, + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.snapshots.with_raw_response.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.snapshots.with_streaming_response.list( + volume_id="7724db7c-e098-11e5-b522-000f53304e51", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `volume_id` but received ''"): + await async_client.gpu_droplets.volumes.snapshots.with_raw_response.list( + volume_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.gpu_droplets.volumes.snapshots.delete( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.volumes.snapshots.with_raw_response.delete( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.volumes.snapshots.with_streaming_response.delete( + "fbe805e8-866b-11e6-96bf-000f53315a41", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert snapshot is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `snapshot_id` but received ''"): + await async_client.gpu_droplets.volumes.snapshots.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/inference/__init__.py b/tests/api_resources/inference/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/inference/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/inference/test_api_keys.py b/tests/api_resources/inference/test_api_keys.py new file mode 100644 index 00000000..0bbfa00f --- /dev/null +++ b/tests/api_resources/inference/test_api_keys.py @@ -0,0 +1,448 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.inference import ( + APIKeyListResponse, + APIKeyCreateResponse, + APIKeyDeleteResponse, + APIKeyUpdateResponse, + APIKeyUpdateRegenerateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAPIKeys: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + api_key = client.inference.api_keys.create() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + api_key = client.inference.api_keys.create( + name="Production Key", + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.inference.api_keys.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.inference.api_keys.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + api_key = client.inference.api_keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + api_key = client.inference.api_keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.inference.api_keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.inference.api_keys.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + client.inference.api_keys.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + api_key = client.inference.api_keys.list() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + api_key = client.inference.api_keys.list( + page=0, + per_page=0, + ) + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.inference.api_keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.inference.api_keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + api_key = client.inference.api_keys.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.inference.api_keys.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.inference.api_keys.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.inference.api_keys.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_regenerate(self, client: Gradient) -> None: + api_key = client.inference.api_keys.update_regenerate( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyUpdateRegenerateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update_regenerate(self, client: Gradient) -> None: + response = client.inference.api_keys.with_raw_response.update_regenerate( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyUpdateRegenerateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update_regenerate(self, client: Gradient) -> None: + with client.inference.api_keys.with_streaming_response.update_regenerate( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyUpdateRegenerateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update_regenerate(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.inference.api_keys.with_raw_response.update_regenerate( + "", + ) + + +class TestAsyncAPIKeys: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + api_key = await async_client.inference.api_keys.create() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + api_key = await async_client.inference.api_keys.create( + name="Production Key", + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.inference.api_keys.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.inference.api_keys.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + api_key = await async_client.inference.api_keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + api_key = await async_client.inference.api_keys.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.inference.api_keys.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.inference.api_keys.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyUpdateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + await async_client.inference.api_keys.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + api_key = await async_client.inference.api_keys.list() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + api_key = await async_client.inference.api_keys.list( + page=0, + per_page=0, + ) + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.inference.api_keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.inference.api_keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyListResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + api_key = await async_client.inference.api_keys.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.inference.api_keys.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.inference.api_keys.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.inference.api_keys.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_regenerate(self, async_client: AsyncGradient) -> None: + api_key = await async_client.inference.api_keys.update_regenerate( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(APIKeyUpdateRegenerateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update_regenerate(self, async_client: AsyncGradient) -> None: + response = await async_client.inference.api_keys.with_raw_response.update_regenerate( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(APIKeyUpdateRegenerateResponse, api_key, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update_regenerate(self, async_client: AsyncGradient) -> None: + async with async_client.inference.api_keys.with_streaming_response.update_regenerate( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyUpdateRegenerateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update_regenerate(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.inference.api_keys.with_raw_response.update_regenerate( + "", + ) diff --git a/tests/api_resources/knowledge_bases/__init__.py b/tests/api_resources/knowledge_bases/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/knowledge_bases/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/knowledge_bases/test_data_sources.py b/tests/api_resources/knowledge_bases/test_data_sources.py new file mode 100644 index 00000000..ca721d93 --- /dev/null +++ b/tests/api_resources/knowledge_bases/test_data_sources.py @@ -0,0 +1,463 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.knowledge_bases import ( + DataSourceListResponse, + DataSourceCreateResponse, + DataSourceDeleteResponse, + DataSourceCreatePresignedURLsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDataSources: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + data_source = client.knowledge_bases.data_sources.create( + path_knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(DataSourceCreateResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + data_source = client.knowledge_bases.data_sources.create( + path_knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + aws_data_source={ + "bucket_name": "example name", + "item_path": "example string", + "key_id": "123e4567-e89b-12d3-a456-426614174000", + "region": "example string", + "secret_key": "example string", + }, + body_knowledge_base_uuid='"12345678-1234-1234-1234-123456789012"', + spaces_data_source={ + "bucket_name": "example name", + "item_path": "example string", + "region": "example string", + }, + web_crawler_data_source={ + "base_url": "example string", + "crawling_option": "UNKNOWN", + "embed_media": True, + "exclude_tags": ["example string"], + }, + ) + assert_matches_type(DataSourceCreateResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.knowledge_bases.data_sources.with_raw_response.create( + path_knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_source = response.parse() + assert_matches_type(DataSourceCreateResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.knowledge_bases.data_sources.with_streaming_response.create( + path_knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_source = response.parse() + assert_matches_type(DataSourceCreateResponse, data_source, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_create(self, client: Gradient) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `path_knowledge_base_uuid` but received ''" + ): + client.knowledge_bases.data_sources.with_raw_response.create( + path_knowledge_base_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + data_source = client.knowledge_bases.data_sources.list( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(DataSourceListResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + data_source = client.knowledge_bases.data_sources.list( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(DataSourceListResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.knowledge_bases.data_sources.with_raw_response.list( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_source = response.parse() + assert_matches_type(DataSourceListResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.knowledge_bases.data_sources.with_streaming_response.list( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_source = response.parse() + assert_matches_type(DataSourceListResponse, data_source, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + client.knowledge_bases.data_sources.with_raw_response.list( + knowledge_base_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + data_source = client.knowledge_bases.data_sources.delete( + data_source_uuid='"123e4567-e89b-12d3-a456-426614174000"', + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.knowledge_bases.data_sources.with_raw_response.delete( + data_source_uuid='"123e4567-e89b-12d3-a456-426614174000"', + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_source = response.parse() + assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.knowledge_bases.data_sources.with_streaming_response.delete( + data_source_uuid='"123e4567-e89b-12d3-a456-426614174000"', + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_source = response.parse() + assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + client.knowledge_bases.data_sources.with_raw_response.delete( + data_source_uuid='"123e4567-e89b-12d3-a456-426614174000"', + knowledge_base_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_uuid` but received ''"): + client.knowledge_bases.data_sources.with_raw_response.delete( + data_source_uuid="", + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_presigned_urls(self, client: Gradient) -> None: + data_source = client.knowledge_bases.data_sources.create_presigned_urls() + assert_matches_type(DataSourceCreatePresignedURLsResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_presigned_urls_with_all_params(self, client: Gradient) -> None: + data_source = client.knowledge_bases.data_sources.create_presigned_urls( + files=[ + { + "file_name": "example name", + "file_size": "file_size", + } + ], + ) + assert_matches_type(DataSourceCreatePresignedURLsResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_presigned_urls(self, client: Gradient) -> None: + response = client.knowledge_bases.data_sources.with_raw_response.create_presigned_urls() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_source = response.parse() + assert_matches_type(DataSourceCreatePresignedURLsResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_presigned_urls(self, client: Gradient) -> None: + with client.knowledge_bases.data_sources.with_streaming_response.create_presigned_urls() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_source = response.parse() + assert_matches_type(DataSourceCreatePresignedURLsResponse, data_source, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDataSources: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + data_source = await async_client.knowledge_bases.data_sources.create( + path_knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(DataSourceCreateResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + data_source = await async_client.knowledge_bases.data_sources.create( + path_knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + aws_data_source={ + "bucket_name": "example name", + "item_path": "example string", + "key_id": "123e4567-e89b-12d3-a456-426614174000", + "region": "example string", + "secret_key": "example string", + }, + body_knowledge_base_uuid='"12345678-1234-1234-1234-123456789012"', + spaces_data_source={ + "bucket_name": "example name", + "item_path": "example string", + "region": "example string", + }, + web_crawler_data_source={ + "base_url": "example string", + "crawling_option": "UNKNOWN", + "embed_media": True, + "exclude_tags": ["example string"], + }, + ) + assert_matches_type(DataSourceCreateResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.data_sources.with_raw_response.create( + path_knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_source = await response.parse() + assert_matches_type(DataSourceCreateResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.data_sources.with_streaming_response.create( + path_knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_source = await response.parse() + assert_matches_type(DataSourceCreateResponse, data_source, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_create(self, async_client: AsyncGradient) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `path_knowledge_base_uuid` but received ''" + ): + await async_client.knowledge_bases.data_sources.with_raw_response.create( + path_knowledge_base_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + data_source = await async_client.knowledge_bases.data_sources.list( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(DataSourceListResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + data_source = await async_client.knowledge_bases.data_sources.list( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(DataSourceListResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.data_sources.with_raw_response.list( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_source = await response.parse() + assert_matches_type(DataSourceListResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.data_sources.with_streaming_response.list( + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_source = await response.parse() + assert_matches_type(DataSourceListResponse, data_source, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + await async_client.knowledge_bases.data_sources.with_raw_response.list( + knowledge_base_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + data_source = await async_client.knowledge_bases.data_sources.delete( + data_source_uuid='"123e4567-e89b-12d3-a456-426614174000"', + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.data_sources.with_raw_response.delete( + data_source_uuid='"123e4567-e89b-12d3-a456-426614174000"', + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_source = await response.parse() + assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.data_sources.with_streaming_response.delete( + data_source_uuid='"123e4567-e89b-12d3-a456-426614174000"', + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_source = await response.parse() + assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + await async_client.knowledge_bases.data_sources.with_raw_response.delete( + data_source_uuid='"123e4567-e89b-12d3-a456-426614174000"', + knowledge_base_uuid="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_uuid` but received ''"): + await async_client.knowledge_bases.data_sources.with_raw_response.delete( + data_source_uuid="", + knowledge_base_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_presigned_urls(self, async_client: AsyncGradient) -> None: + data_source = await async_client.knowledge_bases.data_sources.create_presigned_urls() + assert_matches_type(DataSourceCreatePresignedURLsResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_presigned_urls_with_all_params(self, async_client: AsyncGradient) -> None: + data_source = await async_client.knowledge_bases.data_sources.create_presigned_urls( + files=[ + { + "file_name": "example name", + "file_size": "file_size", + } + ], + ) + assert_matches_type(DataSourceCreatePresignedURLsResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_presigned_urls(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.data_sources.with_raw_response.create_presigned_urls() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_source = await response.parse() + assert_matches_type(DataSourceCreatePresignedURLsResponse, data_source, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_presigned_urls(self, async_client: AsyncGradient) -> None: + async with ( + async_client.knowledge_bases.data_sources.with_streaming_response.create_presigned_urls() + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_source = await response.parse() + assert_matches_type(DataSourceCreatePresignedURLsResponse, data_source, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/knowledge_bases/test_indexing_jobs.py b/tests/api_resources/knowledge_bases/test_indexing_jobs.py new file mode 100644 index 00000000..aab5d9ac --- /dev/null +++ b/tests/api_resources/knowledge_bases/test_indexing_jobs.py @@ -0,0 +1,533 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.knowledge_bases import ( + IndexingJobListResponse, + IndexingJobCreateResponse, + IndexingJobRetrieveResponse, + IndexingJobUpdateCancelResponse, + IndexingJobRetrieveSignedURLResponse, + IndexingJobRetrieveDataSourcesResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestIndexingJobs: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.create() + assert_matches_type(IndexingJobCreateResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.create( + data_source_uuids=["example string"], + knowledge_base_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(IndexingJobCreateResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.knowledge_bases.indexing_jobs.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = response.parse() + assert_matches_type(IndexingJobCreateResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.knowledge_bases.indexing_jobs.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = response.parse() + assert_matches_type(IndexingJobCreateResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(IndexingJobRetrieveResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.knowledge_bases.indexing_jobs.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = response.parse() + assert_matches_type(IndexingJobRetrieveResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.knowledge_bases.indexing_jobs.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = response.parse() + assert_matches_type(IndexingJobRetrieveResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.knowledge_bases.indexing_jobs.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.list() + assert_matches_type(IndexingJobListResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.list( + page=0, + per_page=0, + ) + assert_matches_type(IndexingJobListResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.knowledge_bases.indexing_jobs.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = response.parse() + assert_matches_type(IndexingJobListResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.knowledge_bases.indexing_jobs.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = response.parse() + assert_matches_type(IndexingJobListResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_data_sources(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.retrieve_data_sources( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(IndexingJobRetrieveDataSourcesResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve_data_sources(self, client: Gradient) -> None: + response = client.knowledge_bases.indexing_jobs.with_raw_response.retrieve_data_sources( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = response.parse() + assert_matches_type(IndexingJobRetrieveDataSourcesResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve_data_sources(self, client: Gradient) -> None: + with client.knowledge_bases.indexing_jobs.with_streaming_response.retrieve_data_sources( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = response.parse() + assert_matches_type(IndexingJobRetrieveDataSourcesResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve_data_sources(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `indexing_job_uuid` but received ''"): + client.knowledge_bases.indexing_jobs.with_raw_response.retrieve_data_sources( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_signed_url(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.retrieve_signed_url( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(IndexingJobRetrieveSignedURLResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve_signed_url(self, client: Gradient) -> None: + response = client.knowledge_bases.indexing_jobs.with_raw_response.retrieve_signed_url( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = response.parse() + assert_matches_type(IndexingJobRetrieveSignedURLResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve_signed_url(self, client: Gradient) -> None: + with client.knowledge_bases.indexing_jobs.with_streaming_response.retrieve_signed_url( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = response.parse() + assert_matches_type(IndexingJobRetrieveSignedURLResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve_signed_url(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `indexing_job_uuid` but received ''"): + client.knowledge_bases.indexing_jobs.with_raw_response.retrieve_signed_url( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_cancel(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.update_cancel( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(IndexingJobUpdateCancelResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_cancel_with_all_params(self, client: Gradient) -> None: + indexing_job = client.knowledge_bases.indexing_jobs.update_cancel( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(IndexingJobUpdateCancelResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update_cancel(self, client: Gradient) -> None: + response = client.knowledge_bases.indexing_jobs.with_raw_response.update_cancel( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = response.parse() + assert_matches_type(IndexingJobUpdateCancelResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update_cancel(self, client: Gradient) -> None: + with client.knowledge_bases.indexing_jobs.with_streaming_response.update_cancel( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = response.parse() + assert_matches_type(IndexingJobUpdateCancelResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update_cancel(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + client.knowledge_bases.indexing_jobs.with_raw_response.update_cancel( + path_uuid="", + ) + + +class TestAsyncIndexingJobs: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.create() + assert_matches_type(IndexingJobCreateResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.create( + data_source_uuids=["example string"], + knowledge_base_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(IndexingJobCreateResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.indexing_jobs.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = await response.parse() + assert_matches_type(IndexingJobCreateResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.indexing_jobs.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = await response.parse() + assert_matches_type(IndexingJobCreateResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(IndexingJobRetrieveResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.indexing_jobs.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = await response.parse() + assert_matches_type(IndexingJobRetrieveResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.indexing_jobs.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = await response.parse() + assert_matches_type(IndexingJobRetrieveResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.knowledge_bases.indexing_jobs.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.list() + assert_matches_type(IndexingJobListResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.list( + page=0, + per_page=0, + ) + assert_matches_type(IndexingJobListResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.indexing_jobs.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = await response.parse() + assert_matches_type(IndexingJobListResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.indexing_jobs.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = await response.parse() + assert_matches_type(IndexingJobListResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_data_sources(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.retrieve_data_sources( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(IndexingJobRetrieveDataSourcesResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve_data_sources(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.indexing_jobs.with_raw_response.retrieve_data_sources( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = await response.parse() + assert_matches_type(IndexingJobRetrieveDataSourcesResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve_data_sources(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.indexing_jobs.with_streaming_response.retrieve_data_sources( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = await response.parse() + assert_matches_type(IndexingJobRetrieveDataSourcesResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve_data_sources(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `indexing_job_uuid` but received ''"): + await async_client.knowledge_bases.indexing_jobs.with_raw_response.retrieve_data_sources( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_signed_url(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.retrieve_signed_url( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(IndexingJobRetrieveSignedURLResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve_signed_url(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.indexing_jobs.with_raw_response.retrieve_signed_url( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = await response.parse() + assert_matches_type(IndexingJobRetrieveSignedURLResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve_signed_url(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.indexing_jobs.with_streaming_response.retrieve_signed_url( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = await response.parse() + assert_matches_type(IndexingJobRetrieveSignedURLResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve_signed_url(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `indexing_job_uuid` but received ''"): + await async_client.knowledge_bases.indexing_jobs.with_raw_response.retrieve_signed_url( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_cancel(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.update_cancel( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(IndexingJobUpdateCancelResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_cancel_with_all_params(self, async_client: AsyncGradient) -> None: + indexing_job = await async_client.knowledge_bases.indexing_jobs.update_cancel( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(IndexingJobUpdateCancelResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update_cancel(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.indexing_jobs.with_raw_response.update_cancel( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + indexing_job = await response.parse() + assert_matches_type(IndexingJobUpdateCancelResponse, indexing_job, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update_cancel(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.indexing_jobs.with_streaming_response.update_cancel( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + indexing_job = await response.parse() + assert_matches_type(IndexingJobUpdateCancelResponse, indexing_job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update_cancel(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + await async_client.knowledge_bases.indexing_jobs.with_raw_response.update_cancel( + path_uuid="", + ) diff --git a/tests/api_resources/models/__init__.py b/tests/api_resources/models/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/models/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/models/providers/__init__.py b/tests/api_resources/models/providers/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/models/providers/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/models/providers/test_anthropic.py b/tests/api_resources/models/providers/test_anthropic.py new file mode 100644 index 00000000..b0aeb37c --- /dev/null +++ b/tests/api_resources/models/providers/test_anthropic.py @@ -0,0 +1,557 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.models.providers import ( + AnthropicListResponse, + AnthropicCreateResponse, + AnthropicDeleteResponse, + AnthropicUpdateResponse, + AnthropicRetrieveResponse, + AnthropicListAgentsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAnthropic: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.create() + assert_matches_type(AnthropicCreateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.create( + api_key='"sk-ant-12345678901234567890123456789012"', + name='"Production Key"', + ) + assert_matches_type(AnthropicCreateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.models.providers.anthropic.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = response.parse() + assert_matches_type(AnthropicCreateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.models.providers.anthropic.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = response.parse() + assert_matches_type(AnthropicCreateResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AnthropicRetrieveResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.models.providers.anthropic.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = response.parse() + assert_matches_type(AnthropicRetrieveResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.models.providers.anthropic.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = response.parse() + assert_matches_type(AnthropicRetrieveResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.models.providers.anthropic.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AnthropicUpdateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + api_key='"sk-ant-12345678901234567890123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(AnthropicUpdateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.models.providers.anthropic.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = response.parse() + assert_matches_type(AnthropicUpdateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.models.providers.anthropic.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = response.parse() + assert_matches_type(AnthropicUpdateResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + client.models.providers.anthropic.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.list() + assert_matches_type(AnthropicListResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.list( + page=0, + per_page=0, + ) + assert_matches_type(AnthropicListResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.models.providers.anthropic.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = response.parse() + assert_matches_type(AnthropicListResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.models.providers.anthropic.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = response.parse() + assert_matches_type(AnthropicListResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AnthropicDeleteResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.models.providers.anthropic.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = response.parse() + assert_matches_type(AnthropicDeleteResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.models.providers.anthropic.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = response.parse() + assert_matches_type(AnthropicDeleteResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.models.providers.anthropic.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_agents(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AnthropicListAgentsResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_agents_with_all_params(self, client: Gradient) -> None: + anthropic = client.models.providers.anthropic.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(AnthropicListAgentsResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_agents(self, client: Gradient) -> None: + response = client.models.providers.anthropic.with_raw_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = response.parse() + assert_matches_type(AnthropicListAgentsResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_agents(self, client: Gradient) -> None: + with client.models.providers.anthropic.with_streaming_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = response.parse() + assert_matches_type(AnthropicListAgentsResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_agents(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.models.providers.anthropic.with_raw_response.list_agents( + uuid="", + ) + + +class TestAsyncAnthropic: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.create() + assert_matches_type(AnthropicCreateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.create( + api_key='"sk-ant-12345678901234567890123456789012"', + name='"Production Key"', + ) + assert_matches_type(AnthropicCreateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.anthropic.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = await response.parse() + assert_matches_type(AnthropicCreateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.anthropic.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = await response.parse() + assert_matches_type(AnthropicCreateResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AnthropicRetrieveResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.anthropic.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = await response.parse() + assert_matches_type(AnthropicRetrieveResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.anthropic.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = await response.parse() + assert_matches_type(AnthropicRetrieveResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.models.providers.anthropic.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AnthropicUpdateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + api_key='"sk-ant-12345678901234567890123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(AnthropicUpdateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.anthropic.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = await response.parse() + assert_matches_type(AnthropicUpdateResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.anthropic.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = await response.parse() + assert_matches_type(AnthropicUpdateResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + await async_client.models.providers.anthropic.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.list() + assert_matches_type(AnthropicListResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.list( + page=0, + per_page=0, + ) + assert_matches_type(AnthropicListResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.anthropic.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = await response.parse() + assert_matches_type(AnthropicListResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.anthropic.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = await response.parse() + assert_matches_type(AnthropicListResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AnthropicDeleteResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.anthropic.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = await response.parse() + assert_matches_type(AnthropicDeleteResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.anthropic.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = await response.parse() + assert_matches_type(AnthropicDeleteResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.models.providers.anthropic.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_agents(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AnthropicListAgentsResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_agents_with_all_params(self, async_client: AsyncGradient) -> None: + anthropic = await async_client.models.providers.anthropic.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(AnthropicListAgentsResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_agents(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.anthropic.with_raw_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + anthropic = await response.parse() + assert_matches_type(AnthropicListAgentsResponse, anthropic, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_agents(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.anthropic.with_streaming_response.list_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + anthropic = await response.parse() + assert_matches_type(AnthropicListAgentsResponse, anthropic, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_agents(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.models.providers.anthropic.with_raw_response.list_agents( + uuid="", + ) diff --git a/tests/api_resources/models/providers/test_openai.py b/tests/api_resources/models/providers/test_openai.py new file mode 100644 index 00000000..c5780e05 --- /dev/null +++ b/tests/api_resources/models/providers/test_openai.py @@ -0,0 +1,557 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.models.providers import ( + OpenAIListResponse, + OpenAICreateResponse, + OpenAIDeleteResponse, + OpenAIUpdateResponse, + OpenAIRetrieveResponse, + OpenAIRetrieveAgentsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestOpenAI: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + openai = client.models.providers.openai.create() + assert_matches_type(OpenAICreateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + openai = client.models.providers.openai.create( + api_key='"sk-proj--123456789098765432123456789"', + name='"Production Key"', + ) + assert_matches_type(OpenAICreateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.models.providers.openai.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = response.parse() + assert_matches_type(OpenAICreateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.models.providers.openai.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = response.parse() + assert_matches_type(OpenAICreateResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + openai = client.models.providers.openai.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(OpenAIRetrieveResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.models.providers.openai.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = response.parse() + assert_matches_type(OpenAIRetrieveResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.models.providers.openai.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = response.parse() + assert_matches_type(OpenAIRetrieveResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.models.providers.openai.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + openai = client.models.providers.openai.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(OpenAIUpdateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + openai = client.models.providers.openai.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + api_key='"sk-ant-12345678901234567890123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(OpenAIUpdateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.models.providers.openai.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = response.parse() + assert_matches_type(OpenAIUpdateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.models.providers.openai.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = response.parse() + assert_matches_type(OpenAIUpdateResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + client.models.providers.openai.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + openai = client.models.providers.openai.list() + assert_matches_type(OpenAIListResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + openai = client.models.providers.openai.list( + page=0, + per_page=0, + ) + assert_matches_type(OpenAIListResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.models.providers.openai.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = response.parse() + assert_matches_type(OpenAIListResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.models.providers.openai.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = response.parse() + assert_matches_type(OpenAIListResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + openai = client.models.providers.openai.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(OpenAIDeleteResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.models.providers.openai.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = response.parse() + assert_matches_type(OpenAIDeleteResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.models.providers.openai.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = response.parse() + assert_matches_type(OpenAIDeleteResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + client.models.providers.openai.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_agents(self, client: Gradient) -> None: + openai = client.models.providers.openai.retrieve_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(OpenAIRetrieveAgentsResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_agents_with_all_params(self, client: Gradient) -> None: + openai = client.models.providers.openai.retrieve_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(OpenAIRetrieveAgentsResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve_agents(self, client: Gradient) -> None: + response = client.models.providers.openai.with_raw_response.retrieve_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = response.parse() + assert_matches_type(OpenAIRetrieveAgentsResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve_agents(self, client: Gradient) -> None: + with client.models.providers.openai.with_streaming_response.retrieve_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = response.parse() + assert_matches_type(OpenAIRetrieveAgentsResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve_agents(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.models.providers.openai.with_raw_response.retrieve_agents( + uuid="", + ) + + +class TestAsyncOpenAI: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.create() + assert_matches_type(OpenAICreateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.create( + api_key='"sk-proj--123456789098765432123456789"', + name='"Production Key"', + ) + assert_matches_type(OpenAICreateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.openai.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = await response.parse() + assert_matches_type(OpenAICreateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.openai.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = await response.parse() + assert_matches_type(OpenAICreateResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(OpenAIRetrieveResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.openai.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = await response.parse() + assert_matches_type(OpenAIRetrieveResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.openai.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = await response.parse() + assert_matches_type(OpenAIRetrieveResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.models.providers.openai.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(OpenAIUpdateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + api_key='"sk-ant-12345678901234567890123456789012"', + body_api_key_uuid='"12345678-1234-1234-1234-123456789012"', + name='"Production Key"', + ) + assert_matches_type(OpenAIUpdateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.openai.with_raw_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = await response.parse() + assert_matches_type(OpenAIUpdateResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.openai.with_streaming_response.update( + path_api_key_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = await response.parse() + assert_matches_type(OpenAIUpdateResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_api_key_uuid` but received ''"): + await async_client.models.providers.openai.with_raw_response.update( + path_api_key_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.list() + assert_matches_type(OpenAIListResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.list( + page=0, + per_page=0, + ) + assert_matches_type(OpenAIListResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.openai.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = await response.parse() + assert_matches_type(OpenAIListResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.openai.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = await response.parse() + assert_matches_type(OpenAIListResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(OpenAIDeleteResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.openai.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = await response.parse() + assert_matches_type(OpenAIDeleteResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.openai.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = await response.parse() + assert_matches_type(OpenAIDeleteResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_uuid` but received ''"): + await async_client.models.providers.openai.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_agents(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.retrieve_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(OpenAIRetrieveAgentsResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_agents_with_all_params(self, async_client: AsyncGradient) -> None: + openai = await async_client.models.providers.openai.retrieve_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + page=0, + per_page=0, + ) + assert_matches_type(OpenAIRetrieveAgentsResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve_agents(self, async_client: AsyncGradient) -> None: + response = await async_client.models.providers.openai.with_raw_response.retrieve_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + openai = await response.parse() + assert_matches_type(OpenAIRetrieveAgentsResponse, openai, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve_agents(self, async_client: AsyncGradient) -> None: + async with async_client.models.providers.openai.with_streaming_response.retrieve_agents( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + openai = await response.parse() + assert_matches_type(OpenAIRetrieveAgentsResponse, openai, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve_agents(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.models.providers.openai.with_raw_response.retrieve_agents( + uuid="", + ) diff --git a/tests/api_resources/nfs/__init__.py b/tests/api_resources/nfs/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/nfs/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/nfs/test_snapshots.py b/tests/api_resources/nfs/test_snapshots.py new file mode 100644 index 00000000..e17265f3 --- /dev/null +++ b/tests/api_resources/nfs/test_snapshots.py @@ -0,0 +1,297 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types.nfs import ( + SnapshotListResponse, + SnapshotRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSnapshots: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + snapshot = client.nfs.snapshots.retrieve( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.nfs.snapshots.with_raw_response.retrieve( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.nfs.snapshots.with_streaming_response.retrieve( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_snapshot_id` but received ''"): + client.nfs.snapshots.with_raw_response.retrieve( + nfs_snapshot_id="", + region="region", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + snapshot = client.nfs.snapshots.list( + region="region", + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + snapshot = client.nfs.snapshots.list( + region="region", + share_id="share_id", + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.nfs.snapshots.with_raw_response.list( + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.nfs.snapshots.with_streaming_response.list( + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + snapshot = client.nfs.snapshots.delete( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.nfs.snapshots.with_raw_response.delete( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = response.parse() + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.nfs.snapshots.with_streaming_response.delete( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = response.parse() + assert snapshot is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_snapshot_id` but received ''"): + client.nfs.snapshots.with_raw_response.delete( + nfs_snapshot_id="", + region="region", + ) + + +class TestAsyncSnapshots: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.nfs.snapshots.retrieve( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.snapshots.with_raw_response.retrieve( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.snapshots.with_streaming_response.retrieve( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert_matches_type(SnapshotRetrieveResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_snapshot_id` but received ''"): + await async_client.nfs.snapshots.with_raw_response.retrieve( + nfs_snapshot_id="", + region="region", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.nfs.snapshots.list( + region="region", + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.nfs.snapshots.list( + region="region", + share_id="share_id", + ) + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.snapshots.with_raw_response.list( + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.snapshots.with_streaming_response.list( + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert_matches_type(SnapshotListResponse, snapshot, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + snapshot = await async_client.nfs.snapshots.delete( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.snapshots.with_raw_response.delete( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + snapshot = await response.parse() + assert snapshot is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.snapshots.with_streaming_response.delete( + nfs_snapshot_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + snapshot = await response.parse() + assert snapshot is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_snapshot_id` but received ''"): + await async_client.nfs.snapshots.with_raw_response.delete( + nfs_snapshot_id="", + region="region", + ) diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py new file mode 100644 index 00000000..bf495274 --- /dev/null +++ b/tests/api_resources/test_agents.py @@ -0,0 +1,716 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types import ( + AgentListResponse, + AgentCreateResponse, + AgentDeleteResponse, + AgentUpdateResponse, + AgentRetrieveResponse, + AgentUpdateStatusResponse, + AgentRetrieveUsageResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAgents: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + agent = client.agents.create() + assert_matches_type(AgentCreateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + agent = client.agents.create( + anthropic_key_uuid='"12345678-1234-1234-1234-123456789012"', + description='"My Agent Description"', + instruction='"You are an agent who thinks deeply about the world"', + knowledge_base_uuid=["example string"], + model_provider_key_uuid='"12345678-1234-1234-1234-123456789012"', + model_uuid='"12345678-1234-1234-1234-123456789012"', + name='"My Agent"', + openai_key_uuid='"12345678-1234-1234-1234-123456789012"', + project_id='"12345678-1234-1234-1234-123456789012"', + region='"tor1"', + tags=["example string"], + workspace_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(AgentCreateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.agents.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentCreateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.agents.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentCreateResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + agent = client.agents.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentRetrieveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.agents.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentRetrieveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.agents.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentRetrieveResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.agents.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + agent = client.agents.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentUpdateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + agent = client.agents.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_log_insights_enabled=True, + allowed_domains=["example string"], + anthropic_key_uuid='"12345678-1234-1234-1234-123456789012"', + conversation_logs_enabled=True, + description='"My Agent Description"', + instruction='"You are an agent who thinks deeply about the world"', + k=5, + max_tokens=100, + model_provider_key_uuid='"12345678-1234-1234-1234-123456789012"', + model_uuid='"12345678-1234-1234-1234-123456789012"', + name='"My New Agent Name"', + openai_key_uuid='"12345678-1234-1234-1234-123456789012"', + project_id='"12345678-1234-1234-1234-123456789012"', + provide_citations=True, + retrieval_method="RETRIEVAL_METHOD_UNKNOWN", + tags=["example string"], + temperature=0.7, + top_p=0.9, + body_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(AgentUpdateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.agents.with_raw_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentUpdateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.agents.with_streaming_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentUpdateResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + client.agents.with_raw_response.update( + path_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + agent = client.agents.list() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + agent = client.agents.list( + only_deployed=True, + page=0, + per_page=0, + ) + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.agents.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.agents.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + agent = client.agents.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentDeleteResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.agents.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentDeleteResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.agents.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentDeleteResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.agents.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_usage(self, client: Gradient) -> None: + agent = client.agents.retrieve_usage( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentRetrieveUsageResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_usage_with_all_params(self, client: Gradient) -> None: + agent = client.agents.retrieve_usage( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + start="start", + stop="stop", + ) + assert_matches_type(AgentRetrieveUsageResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve_usage(self, client: Gradient) -> None: + response = client.agents.with_raw_response.retrieve_usage( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentRetrieveUsageResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve_usage(self, client: Gradient) -> None: + with client.agents.with_streaming_response.retrieve_usage( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentRetrieveUsageResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve_usage(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.agents.with_raw_response.retrieve_usage( + uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_status(self, client: Gradient) -> None: + agent = client.agents.update_status( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentUpdateStatusResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_status_with_all_params(self, client: Gradient) -> None: + agent = client.agents.update_status( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_uuid='"12345678-1234-1234-1234-123456789012"', + visibility="VISIBILITY_UNKNOWN", + ) + assert_matches_type(AgentUpdateStatusResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update_status(self, client: Gradient) -> None: + response = client.agents.with_raw_response.update_status( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentUpdateStatusResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update_status(self, client: Gradient) -> None: + with client.agents.with_streaming_response.update_status( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentUpdateStatusResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update_status(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + client.agents.with_raw_response.update_status( + path_uuid="", + ) + + +class TestAsyncAgents: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.create() + assert_matches_type(AgentCreateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.create( + anthropic_key_uuid='"12345678-1234-1234-1234-123456789012"', + description='"My Agent Description"', + instruction='"You are an agent who thinks deeply about the world"', + knowledge_base_uuid=["example string"], + model_provider_key_uuid='"12345678-1234-1234-1234-123456789012"', + model_uuid='"12345678-1234-1234-1234-123456789012"', + name='"My Agent"', + openai_key_uuid='"12345678-1234-1234-1234-123456789012"', + project_id='"12345678-1234-1234-1234-123456789012"', + region='"tor1"', + tags=["example string"], + workspace_uuid="123e4567-e89b-12d3-a456-426614174000", + ) + assert_matches_type(AgentCreateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentCreateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.agents.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentCreateResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentRetrieveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentRetrieveResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.agents.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentRetrieveResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.agents.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentUpdateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + agent_log_insights_enabled=True, + allowed_domains=["example string"], + anthropic_key_uuid='"12345678-1234-1234-1234-123456789012"', + conversation_logs_enabled=True, + description='"My Agent Description"', + instruction='"You are an agent who thinks deeply about the world"', + k=5, + max_tokens=100, + model_provider_key_uuid='"12345678-1234-1234-1234-123456789012"', + model_uuid='"12345678-1234-1234-1234-123456789012"', + name='"My New Agent Name"', + openai_key_uuid='"12345678-1234-1234-1234-123456789012"', + project_id='"12345678-1234-1234-1234-123456789012"', + provide_citations=True, + retrieval_method="RETRIEVAL_METHOD_UNKNOWN", + tags=["example string"], + temperature=0.7, + top_p=0.9, + body_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(AgentUpdateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.with_raw_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentUpdateResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.agents.with_streaming_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentUpdateResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + await async_client.agents.with_raw_response.update( + path_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.list() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.list( + only_deployed=True, + page=0, + per_page=0, + ) + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.agents.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentDeleteResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentDeleteResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.agents.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentDeleteResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.agents.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_usage(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.retrieve_usage( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentRetrieveUsageResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_usage_with_all_params(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.retrieve_usage( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + start="start", + stop="stop", + ) + assert_matches_type(AgentRetrieveUsageResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve_usage(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.with_raw_response.retrieve_usage( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentRetrieveUsageResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve_usage(self, async_client: AsyncGradient) -> None: + async with async_client.agents.with_streaming_response.retrieve_usage( + uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentRetrieveUsageResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve_usage(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.agents.with_raw_response.retrieve_usage( + uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_status(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.update_status( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(AgentUpdateStatusResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_status_with_all_params(self, async_client: AsyncGradient) -> None: + agent = await async_client.agents.update_status( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + body_uuid='"12345678-1234-1234-1234-123456789012"', + visibility="VISIBILITY_UNKNOWN", + ) + assert_matches_type(AgentUpdateStatusResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update_status(self, async_client: AsyncGradient) -> None: + response = await async_client.agents.with_raw_response.update_status( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentUpdateStatusResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update_status(self, async_client: AsyncGradient) -> None: + async with async_client.agents.with_streaming_response.update_status( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentUpdateStatusResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update_status(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + await async_client.agents.with_raw_response.update_status( + path_uuid="", + ) diff --git a/tests/api_resources/test_gpu_droplets.py b/tests/api_resources/test_gpu_droplets.py new file mode 100644 index 00000000..7d50c037 --- /dev/null +++ b/tests/api_resources/test_gpu_droplets.py @@ -0,0 +1,912 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types import ( + GPUDropletListResponse, + GPUDropletCreateResponse, + GPUDropletRetrieveResponse, + GPUDropletListKernelsResponse, + GPUDropletListFirewallsResponse, + GPUDropletListNeighborsResponse, + GPUDropletListSnapshotsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestGPUDroplets: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_1(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.create( + image="ubuntu-20-04-x64", + name="example.com", + size="s-1vcpu-1gb", + ) + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_1(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.create( + image="ubuntu-20-04-x64", + name="example.com", + size="s-1vcpu-1gb", + backup_policy={ + "hour": 0, + "plan": "daily", + "weekday": "SUN", + }, + backups=True, + ipv6=True, + monitoring=True, + private_networking=True, + region="nyc3", + ssh_keys=[289794, "3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + tags=["env:prod", "web"], + user_data="#cloud-config\nruncmd:\n - touch /test.txt\n", + volumes=["12e97116-7280-11ed-b3d0-0a58ac146812"], + vpc_uuid="760e09ef-dc84-11e8-981e-3cfdfeaae000", + with_droplet_agent=True, + ) + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_1(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.create( + image="ubuntu-20-04-x64", + name="example.com", + size="s-1vcpu-1gb", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_1(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.create( + image="ubuntu-20-04-x64", + name="example.com", + size="s-1vcpu-1gb", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_overload_2(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.create( + image="ubuntu-20-04-x64", + names=["sub-01.example.com", "sub-02.example.com"], + size="s-1vcpu-1gb", + ) + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params_overload_2(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.create( + image="ubuntu-20-04-x64", + names=["sub-01.example.com", "sub-02.example.com"], + size="s-1vcpu-1gb", + backup_policy={ + "hour": 0, + "plan": "daily", + "weekday": "SUN", + }, + backups=True, + ipv6=True, + monitoring=True, + private_networking=True, + region="nyc3", + ssh_keys=[289794, "3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + tags=["env:prod", "web"], + user_data="#cloud-config\nruncmd:\n - touch /test.txt\n", + volumes=["12e97116-7280-11ed-b3d0-0a58ac146812"], + vpc_uuid="760e09ef-dc84-11e8-981e-3cfdfeaae000", + with_droplet_agent=True, + ) + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_overload_2(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.create( + image="ubuntu-20-04-x64", + names=["sub-01.example.com", "sub-02.example.com"], + size="s-1vcpu-1gb", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_overload_2(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.create( + image="ubuntu-20-04-x64", + names=["sub-01.example.com", "sub-02.example.com"], + size="s-1vcpu-1gb", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.retrieve( + 3164444, + ) + assert_matches_type(GPUDropletRetrieveResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.retrieve( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert_matches_type(GPUDropletRetrieveResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.retrieve( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert_matches_type(GPUDropletRetrieveResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list() + assert_matches_type(GPUDropletListResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list( + name="name", + page=1, + per_page=1, + tag_name="tag_name", + type="droplets", + ) + assert_matches_type(GPUDropletListResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.delete( + 3164444, + ) + assert gpu_droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.delete( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert gpu_droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.delete( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert gpu_droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_by_tag(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.delete_by_tag( + tag_name="tag_name", + ) + assert gpu_droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete_by_tag(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.delete_by_tag( + tag_name="tag_name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert gpu_droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete_by_tag(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.delete_by_tag( + tag_name="tag_name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert gpu_droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_firewalls(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list_firewalls( + droplet_id=3164444, + ) + assert_matches_type(GPUDropletListFirewallsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_firewalls_with_all_params(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list_firewalls( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(GPUDropletListFirewallsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_firewalls(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.list_firewalls( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListFirewallsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_firewalls(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.list_firewalls( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListFirewallsResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_kernels(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list_kernels( + droplet_id=3164444, + ) + assert_matches_type(GPUDropletListKernelsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_kernels_with_all_params(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list_kernels( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(GPUDropletListKernelsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_kernels(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.list_kernels( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListKernelsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_kernels(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.list_kernels( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListKernelsResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_neighbors(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list_neighbors( + 3164444, + ) + assert_matches_type(GPUDropletListNeighborsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_neighbors(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.list_neighbors( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListNeighborsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_neighbors(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.list_neighbors( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListNeighborsResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_snapshots(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list_snapshots( + droplet_id=3164444, + ) + assert_matches_type(GPUDropletListSnapshotsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_snapshots_with_all_params(self, client: Gradient) -> None: + gpu_droplet = client.gpu_droplets.list_snapshots( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(GPUDropletListSnapshotsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_snapshots(self, client: Gradient) -> None: + response = client.gpu_droplets.with_raw_response.list_snapshots( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListSnapshotsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_snapshots(self, client: Gradient) -> None: + with client.gpu_droplets.with_streaming_response.list_snapshots( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = response.parse() + assert_matches_type(GPUDropletListSnapshotsResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncGPUDroplets: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.create( + image="ubuntu-20-04-x64", + name="example.com", + size="s-1vcpu-1gb", + ) + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.create( + image="ubuntu-20-04-x64", + name="example.com", + size="s-1vcpu-1gb", + backup_policy={ + "hour": 0, + "plan": "daily", + "weekday": "SUN", + }, + backups=True, + ipv6=True, + monitoring=True, + private_networking=True, + region="nyc3", + ssh_keys=[289794, "3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + tags=["env:prod", "web"], + user_data="#cloud-config\nruncmd:\n - touch /test.txt\n", + volumes=["12e97116-7280-11ed-b3d0-0a58ac146812"], + vpc_uuid="760e09ef-dc84-11e8-981e-3cfdfeaae000", + with_droplet_agent=True, + ) + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.create( + image="ubuntu-20-04-x64", + name="example.com", + size="s-1vcpu-1gb", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.create( + image="ubuntu-20-04-x64", + name="example.com", + size="s-1vcpu-1gb", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.create( + image="ubuntu-20-04-x64", + names=["sub-01.example.com", "sub-02.example.com"], + size="s-1vcpu-1gb", + ) + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.create( + image="ubuntu-20-04-x64", + names=["sub-01.example.com", "sub-02.example.com"], + size="s-1vcpu-1gb", + backup_policy={ + "hour": 0, + "plan": "daily", + "weekday": "SUN", + }, + backups=True, + ipv6=True, + monitoring=True, + private_networking=True, + region="nyc3", + ssh_keys=[289794, "3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"], + tags=["env:prod", "web"], + user_data="#cloud-config\nruncmd:\n - touch /test.txt\n", + volumes=["12e97116-7280-11ed-b3d0-0a58ac146812"], + vpc_uuid="760e09ef-dc84-11e8-981e-3cfdfeaae000", + with_droplet_agent=True, + ) + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.create( + image="ubuntu-20-04-x64", + names=["sub-01.example.com", "sub-02.example.com"], + size="s-1vcpu-1gb", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.create( + image="ubuntu-20-04-x64", + names=["sub-01.example.com", "sub-02.example.com"], + size="s-1vcpu-1gb", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletCreateResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.retrieve( + 3164444, + ) + assert_matches_type(GPUDropletRetrieveResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.retrieve( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletRetrieveResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.retrieve( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletRetrieveResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list() + assert_matches_type(GPUDropletListResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list( + name="name", + page=1, + per_page=1, + tag_name="tag_name", + type="droplets", + ) + assert_matches_type(GPUDropletListResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.delete( + 3164444, + ) + assert gpu_droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.delete( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert gpu_droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.delete( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert gpu_droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_by_tag(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.delete_by_tag( + tag_name="tag_name", + ) + assert gpu_droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete_by_tag(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.delete_by_tag( + tag_name="tag_name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert gpu_droplet is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete_by_tag(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.delete_by_tag( + tag_name="tag_name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert gpu_droplet is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_firewalls(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list_firewalls( + droplet_id=3164444, + ) + assert_matches_type(GPUDropletListFirewallsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_firewalls_with_all_params(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list_firewalls( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(GPUDropletListFirewallsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_firewalls(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.list_firewalls( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListFirewallsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_firewalls(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.list_firewalls( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListFirewallsResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_kernels(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list_kernels( + droplet_id=3164444, + ) + assert_matches_type(GPUDropletListKernelsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_kernels_with_all_params(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list_kernels( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(GPUDropletListKernelsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_kernels(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.list_kernels( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListKernelsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_kernels(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.list_kernels( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListKernelsResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_neighbors(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list_neighbors( + 3164444, + ) + assert_matches_type(GPUDropletListNeighborsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_neighbors(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.list_neighbors( + 3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListNeighborsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_neighbors(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.list_neighbors( + 3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListNeighborsResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_snapshots(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list_snapshots( + droplet_id=3164444, + ) + assert_matches_type(GPUDropletListSnapshotsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_snapshots_with_all_params(self, async_client: AsyncGradient) -> None: + gpu_droplet = await async_client.gpu_droplets.list_snapshots( + droplet_id=3164444, + page=1, + per_page=1, + ) + assert_matches_type(GPUDropletListSnapshotsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_snapshots(self, async_client: AsyncGradient) -> None: + response = await async_client.gpu_droplets.with_raw_response.list_snapshots( + droplet_id=3164444, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListSnapshotsResponse, gpu_droplet, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_snapshots(self, async_client: AsyncGradient) -> None: + async with async_client.gpu_droplets.with_streaming_response.list_snapshots( + droplet_id=3164444, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_droplet = await response.parse() + assert_matches_type(GPUDropletListSnapshotsResponse, gpu_droplet, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py new file mode 100644 index 00000000..47428d02 --- /dev/null +++ b/tests/api_resources/test_images.py @@ -0,0 +1,240 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types import ImageGenerateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestImages: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_generate_overload_1(self, client: Gradient) -> None: + image = client.images.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + ) + assert_matches_type(ImageGenerateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_generate_with_all_params_overload_1(self, client: Gradient) -> None: + image = client.images.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + background="auto", + model="openai-gpt-image-1", + moderation="auto", + n=1, + output_compression=100, + output_format="png", + partial_images=1, + quality="auto", + size="auto", + stream=False, + user="user-1234", + ) + assert_matches_type(ImageGenerateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_generate_overload_1(self, client: Gradient) -> None: + response = client.images.with_raw_response.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImageGenerateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_generate_overload_1(self, client: Gradient) -> None: + with client.images.with_streaming_response.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImageGenerateResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_generate_overload_2(self, client: Gradient) -> None: + image_stream = client.images.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + stream=True, + ) + image_stream.response.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_generate_with_all_params_overload_2(self, client: Gradient) -> None: + image_stream = client.images.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + stream=True, + background="auto", + model="openai-gpt-image-1", + moderation="auto", + n=1, + output_compression=100, + output_format="png", + partial_images=1, + quality="auto", + size="auto", + user="user-1234", + ) + image_stream.response.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_generate_overload_2(self, client: Gradient) -> None: + response = client.images.with_raw_response.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_generate_overload_2(self, client: Gradient) -> None: + with client.images.with_streaming_response.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + +class TestAsyncImages: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_generate_overload_1(self, async_client: AsyncGradient) -> None: + image = await async_client.images.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + ) + assert_matches_type(ImageGenerateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_generate_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + image = await async_client.images.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + background="auto", + model="openai-gpt-image-1", + moderation="auto", + n=1, + output_compression=100, + output_format="png", + partial_images=1, + quality="auto", + size="auto", + stream=False, + user="user-1234", + ) + assert_matches_type(ImageGenerateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_generate_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.images.with_raw_response.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(ImageGenerateResponse, image, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_generate_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.images.with_streaming_response.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImageGenerateResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_generate_overload_2(self, async_client: AsyncGradient) -> None: + image_stream = await async_client.images.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + stream=True, + ) + await image_stream.response.aclose() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_generate_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + image_stream = await async_client.images.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + stream=True, + background="auto", + model="openai-gpt-image-1", + moderation="auto", + n=1, + output_compression=100, + output_format="png", + partial_images=1, + quality="auto", + size="auto", + user="user-1234", + ) + await image_stream.response.aclose() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_generate_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.images.with_raw_response.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_generate_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.images.with_streaming_response.generate( + prompt="A cute baby sea otter floating on its back in calm blue water", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_knowledge_bases.py b/tests/api_resources/test_knowledge_bases.py new file mode 100644 index 00000000..55ffdc93 --- /dev/null +++ b/tests/api_resources/test_knowledge_bases.py @@ -0,0 +1,629 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types import ( + KnowledgeBaseListResponse, + KnowledgeBaseCreateResponse, + KnowledgeBaseDeleteResponse, + KnowledgeBaseUpdateResponse, + KnowledgeBaseRetrieveResponse, + KnowledgeBaseListIndexingJobsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestKnowledgeBases: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.create() + assert_matches_type(KnowledgeBaseCreateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.create( + database_id='"12345678-1234-1234-1234-123456789012"', + datasources=[ + { + "aws_data_source": { + "bucket_name": "example name", + "item_path": "example string", + "key_id": "123e4567-e89b-12d3-a456-426614174000", + "region": "example string", + "secret_key": "example string", + }, + "bucket_name": "example name", + "bucket_region": "example string", + "dropbox_data_source": { + "folder": "example string", + "refresh_token": "example string", + }, + "file_upload_data_source": { + "original_file_name": "example name", + "size_in_bytes": "12345", + "stored_object_key": "example string", + }, + "google_drive_data_source": { + "folder_id": "123e4567-e89b-12d3-a456-426614174000", + "refresh_token": "example string", + }, + "item_path": "example string", + "spaces_data_source": { + "bucket_name": "example name", + "item_path": "example string", + "region": "example string", + }, + "web_crawler_data_source": { + "base_url": "example string", + "crawling_option": "UNKNOWN", + "embed_media": True, + "exclude_tags": ["example string"], + }, + } + ], + embedding_model_uuid='"12345678-1234-1234-1234-123456789012"', + name='"My Knowledge Base"', + project_id='"12345678-1234-1234-1234-123456789012"', + region='"tor1"', + tags=["example string"], + vpc_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(KnowledgeBaseCreateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.knowledge_bases.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseCreateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.knowledge_bases.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseCreateResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseRetrieveResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.knowledge_bases.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseRetrieveResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.knowledge_bases.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseRetrieveResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.knowledge_bases.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseUpdateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + database_id='"12345678-1234-1234-1234-123456789012"', + embedding_model_uuid='"12345678-1234-1234-1234-123456789012"', + name='"My Knowledge Base"', + project_id='"12345678-1234-1234-1234-123456789012"', + tags=["example string"], + body_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(KnowledgeBaseUpdateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gradient) -> None: + response = client.knowledge_bases.with_raw_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseUpdateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gradient) -> None: + with client.knowledge_bases.with_streaming_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseUpdateResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + client.knowledge_bases.with_raw_response.update( + path_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.list() + assert_matches_type(KnowledgeBaseListResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.list( + page=0, + per_page=0, + ) + assert_matches_type(KnowledgeBaseListResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.knowledge_bases.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseListResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.knowledge_bases.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseListResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseDeleteResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.knowledge_bases.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseDeleteResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.knowledge_bases.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseDeleteResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + client.knowledge_bases.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_indexing_jobs(self, client: Gradient) -> None: + knowledge_base = client.knowledge_bases.list_indexing_jobs( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseListIndexingJobsResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_indexing_jobs(self, client: Gradient) -> None: + response = client.knowledge_bases.with_raw_response.list_indexing_jobs( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseListIndexingJobsResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_indexing_jobs(self, client: Gradient) -> None: + with client.knowledge_bases.with_streaming_response.list_indexing_jobs( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = response.parse() + assert_matches_type(KnowledgeBaseListIndexingJobsResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_list_indexing_jobs(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + client.knowledge_bases.with_raw_response.list_indexing_jobs( + "", + ) + + +class TestAsyncKnowledgeBases: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.create() + assert_matches_type(KnowledgeBaseCreateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.create( + database_id='"12345678-1234-1234-1234-123456789012"', + datasources=[ + { + "aws_data_source": { + "bucket_name": "example name", + "item_path": "example string", + "key_id": "123e4567-e89b-12d3-a456-426614174000", + "region": "example string", + "secret_key": "example string", + }, + "bucket_name": "example name", + "bucket_region": "example string", + "dropbox_data_source": { + "folder": "example string", + "refresh_token": "example string", + }, + "file_upload_data_source": { + "original_file_name": "example name", + "size_in_bytes": "12345", + "stored_object_key": "example string", + }, + "google_drive_data_source": { + "folder_id": "123e4567-e89b-12d3-a456-426614174000", + "refresh_token": "example string", + }, + "item_path": "example string", + "spaces_data_source": { + "bucket_name": "example name", + "item_path": "example string", + "region": "example string", + }, + "web_crawler_data_source": { + "base_url": "example string", + "crawling_option": "UNKNOWN", + "embed_media": True, + "exclude_tags": ["example string"], + }, + } + ], + embedding_model_uuid='"12345678-1234-1234-1234-123456789012"', + name='"My Knowledge Base"', + project_id='"12345678-1234-1234-1234-123456789012"', + region='"tor1"', + tags=["example string"], + vpc_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(KnowledgeBaseCreateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseCreateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseCreateResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseRetrieveResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.with_raw_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseRetrieveResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.with_streaming_response.retrieve( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseRetrieveResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.knowledge_bases.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseUpdateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + database_id='"12345678-1234-1234-1234-123456789012"', + embedding_model_uuid='"12345678-1234-1234-1234-123456789012"', + name='"My Knowledge Base"', + project_id='"12345678-1234-1234-1234-123456789012"', + tags=["example string"], + body_uuid='"12345678-1234-1234-1234-123456789012"', + ) + assert_matches_type(KnowledgeBaseUpdateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.with_raw_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseUpdateResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.with_streaming_response.update( + path_uuid='"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseUpdateResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_uuid` but received ''"): + await async_client.knowledge_bases.with_raw_response.update( + path_uuid="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.list() + assert_matches_type(KnowledgeBaseListResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.list( + page=0, + per_page=0, + ) + assert_matches_type(KnowledgeBaseListResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseListResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseListResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseDeleteResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.with_raw_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseDeleteResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.with_streaming_response.delete( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseDeleteResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `uuid` but received ''"): + await async_client.knowledge_bases.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_indexing_jobs(self, async_client: AsyncGradient) -> None: + knowledge_base = await async_client.knowledge_bases.list_indexing_jobs( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + assert_matches_type(KnowledgeBaseListIndexingJobsResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_indexing_jobs(self, async_client: AsyncGradient) -> None: + response = await async_client.knowledge_bases.with_raw_response.list_indexing_jobs( + '"123e4567-e89b-12d3-a456-426614174000"', + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseListIndexingJobsResponse, knowledge_base, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_indexing_jobs(self, async_client: AsyncGradient) -> None: + async with async_client.knowledge_bases.with_streaming_response.list_indexing_jobs( + '"123e4567-e89b-12d3-a456-426614174000"', + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + knowledge_base = await response.parse() + assert_matches_type(KnowledgeBaseListIndexingJobsResponse, knowledge_base, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_list_indexing_jobs(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_uuid` but received ''"): + await async_client.knowledge_bases.with_raw_response.list_indexing_jobs( + "", + ) diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py new file mode 100644 index 00000000..8e6edaef --- /dev/null +++ b/tests/api_resources/test_models.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types import ModelListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestModels: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + model = client.models.list() + assert_matches_type(ModelListResponse, model, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + model = client.models.list( + page=0, + per_page=0, + public_only=True, + usecases=["MODEL_USECASE_UNKNOWN"], + ) + assert_matches_type(ModelListResponse, model, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.models.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model = response.parse() + assert_matches_type(ModelListResponse, model, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.models.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model = response.parse() + assert_matches_type(ModelListResponse, model, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncModels: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + model = await async_client.models.list() + assert_matches_type(ModelListResponse, model, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + model = await async_client.models.list( + page=0, + per_page=0, + public_only=True, + usecases=["MODEL_USECASE_UNKNOWN"], + ) + assert_matches_type(ModelListResponse, model, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.models.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model = await response.parse() + assert_matches_type(ModelListResponse, model, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.models.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model = await response.parse() + assert_matches_type(ModelListResponse, model, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_nfs.py b/tests/api_resources/test_nfs.py new file mode 100644 index 00000000..f2749330 --- /dev/null +++ b/tests/api_resources/test_nfs.py @@ -0,0 +1,611 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types import ( + NfListResponse, + NfCreateResponse, + NfRetrieveResponse, + NfInitiateActionResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestNfs: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gradient) -> None: + nf = client.nfs.create( + name="sammy-share-drive", + region="atl1", + size_gib=1024, + vpc_ids=["796c6fe3-2a1d-4da2-9f3e-38239827dc91"], + ) + assert_matches_type(NfCreateResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gradient) -> None: + response = client.nfs.with_raw_response.create( + name="sammy-share-drive", + region="atl1", + size_gib=1024, + vpc_ids=["796c6fe3-2a1d-4da2-9f3e-38239827dc91"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = response.parse() + assert_matches_type(NfCreateResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gradient) -> None: + with client.nfs.with_streaming_response.create( + name="sammy-share-drive", + region="atl1", + size_gib=1024, + vpc_ids=["796c6fe3-2a1d-4da2-9f3e-38239827dc91"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = response.parse() + assert_matches_type(NfCreateResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gradient) -> None: + nf = client.nfs.retrieve( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + assert_matches_type(NfRetrieveResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gradient) -> None: + response = client.nfs.with_raw_response.retrieve( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = response.parse() + assert_matches_type(NfRetrieveResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gradient) -> None: + with client.nfs.with_streaming_response.retrieve( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = response.parse() + assert_matches_type(NfRetrieveResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_id` but received ''"): + client.nfs.with_raw_response.retrieve( + nfs_id="", + region="region", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + nf = client.nfs.list( + region="region", + ) + assert_matches_type(NfListResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.nfs.with_raw_response.list( + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = response.parse() + assert_matches_type(NfListResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.nfs.with_streaming_response.list( + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = response.parse() + assert_matches_type(NfListResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gradient) -> None: + nf = client.nfs.delete( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + assert nf is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gradient) -> None: + response = client.nfs.with_raw_response.delete( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = response.parse() + assert nf is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gradient) -> None: + with client.nfs.with_streaming_response.delete( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = response.parse() + assert nf is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_id` but received ''"): + client.nfs.with_raw_response.delete( + nfs_id="", + region="region", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_action_overload_1(self, client: Gradient) -> None: + nf = client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_action_with_all_params_overload_1(self, client: Gradient) -> None: + nf = client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + params={"size_gib": 2048}, + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_action_overload_1(self, client: Gradient) -> None: + response = client.nfs.with_raw_response.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = response.parse() + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_action_overload_1(self, client: Gradient) -> None: + with client.nfs.with_streaming_response.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = response.parse() + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_initiate_action_overload_1(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_id` but received ''"): + client.nfs.with_raw_response.initiate_action( + nfs_id="", + region="atl1", + type="resize", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_action_overload_2(self, client: Gradient) -> None: + nf = client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_action_with_all_params_overload_2(self, client: Gradient) -> None: + nf = client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + params={"name": "daily-backup"}, + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_action_overload_2(self, client: Gradient) -> None: + response = client.nfs.with_raw_response.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = response.parse() + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_initiate_action_overload_2(self, client: Gradient) -> None: + with client.nfs.with_streaming_response.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = response.parse() + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_initiate_action_overload_2(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_id` but received ''"): + client.nfs.with_raw_response.initiate_action( + nfs_id="", + region="atl1", + type="resize", + ) + + +class TestAsyncNfs: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.create( + name="sammy-share-drive", + region="atl1", + size_gib=1024, + vpc_ids=["796c6fe3-2a1d-4da2-9f3e-38239827dc91"], + ) + assert_matches_type(NfCreateResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.with_raw_response.create( + name="sammy-share-drive", + region="atl1", + size_gib=1024, + vpc_ids=["796c6fe3-2a1d-4da2-9f3e-38239827dc91"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = await response.parse() + assert_matches_type(NfCreateResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.with_streaming_response.create( + name="sammy-share-drive", + region="atl1", + size_gib=1024, + vpc_ids=["796c6fe3-2a1d-4da2-9f3e-38239827dc91"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = await response.parse() + assert_matches_type(NfCreateResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.retrieve( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + assert_matches_type(NfRetrieveResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.with_raw_response.retrieve( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = await response.parse() + assert_matches_type(NfRetrieveResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.with_streaming_response.retrieve( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = await response.parse() + assert_matches_type(NfRetrieveResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_id` but received ''"): + await async_client.nfs.with_raw_response.retrieve( + nfs_id="", + region="region", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.list( + region="region", + ) + assert_matches_type(NfListResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.with_raw_response.list( + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = await response.parse() + assert_matches_type(NfListResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.with_streaming_response.list( + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = await response.parse() + assert_matches_type(NfListResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.delete( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + assert nf is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.with_raw_response.delete( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = await response.parse() + assert nf is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.with_streaming_response.delete( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="region", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = await response.parse() + assert nf is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_id` but received ''"): + await async_client.nfs.with_raw_response.delete( + nfs_id="", + region="region", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_action_overload_1(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_action_with_all_params_overload_1(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + params={"size_gib": 2048}, + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_action_overload_1(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.with_raw_response.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = await response.parse() + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_action_overload_1(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.with_streaming_response.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = await response.parse() + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_initiate_action_overload_1(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_id` but received ''"): + await async_client.nfs.with_raw_response.initiate_action( + nfs_id="", + region="atl1", + type="resize", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_action_overload_2(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_action_with_all_params_overload_2(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + params={"name": "daily-backup"}, + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_action_overload_2(self, async_client: AsyncGradient) -> None: + response = await async_client.nfs.with_raw_response.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + nf = await response.parse() + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_initiate_action_overload_2(self, async_client: AsyncGradient) -> None: + async with async_client.nfs.with_streaming_response.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + nf = await response.parse() + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_initiate_action_overload_2(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `nfs_id` but received ''"): + await async_client.nfs.with_raw_response.initiate_action( + nfs_id="", + region="atl1", + type="resize", + ) diff --git a/tests/api_resources/test_regions.py b/tests/api_resources/test_regions.py new file mode 100644 index 00000000..8cbf6afb --- /dev/null +++ b/tests/api_resources/test_regions.py @@ -0,0 +1,98 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gradient import Gradient, AsyncGradient +from tests.utils import assert_matches_type +from gradient.types import RegionListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRegions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gradient) -> None: + region = client.regions.list() + assert_matches_type(RegionListResponse, region, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gradient) -> None: + region = client.regions.list( + page=1, + per_page=1, + ) + assert_matches_type(RegionListResponse, region, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gradient) -> None: + response = client.regions.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + region = response.parse() + assert_matches_type(RegionListResponse, region, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gradient) -> None: + with client.regions.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + region = response.parse() + assert_matches_type(RegionListResponse, region, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncRegions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGradient) -> None: + region = await async_client.regions.list() + assert_matches_type(RegionListResponse, region, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGradient) -> None: + region = await async_client.regions.list( + page=1, + per_page=1, + ) + assert_matches_type(RegionListResponse, region, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGradient) -> None: + response = await async_client.regions.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + region = await response.parse() + assert_matches_type(RegionListResponse, region, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGradient) -> None: + async with async_client.regions.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + region = await response.parse() + assert_matches_type(RegionListResponse, region, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..0a1890ae --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,84 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +import logging +from typing import TYPE_CHECKING, Iterator, AsyncIterator + +import httpx +import pytest +from pytest_asyncio import is_async_test + +from gradient import Gradient, AsyncGradient, DefaultAioHttpClient +from gradient._utils import is_dict + +if TYPE_CHECKING: + from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] + +pytest.register_assert_rewrite("tests.utils") + +logging.getLogger("gradient").setLevel(logging.DEBUG) + + +# automatically add `pytest.mark.asyncio()` to all of our async tests +# so we don't have to add that boilerplate everywhere +def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: + pytest_asyncio_tests = (item for item in items if is_async_test(item)) + session_scope_marker = pytest.mark.asyncio(loop_scope="session") + for async_test in pytest_asyncio_tests: + async_test.add_marker(session_scope_marker, append=False) + + # We skip tests that use both the aiohttp client and respx_mock as respx_mock + # doesn't support custom transports. + for item in items: + if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + continue + + if not hasattr(item, "callspec"): + continue + + async_client_param = item.callspec.params.get("async_client") + if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": + item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) + + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + +access_token = "My Access Token" + + +@pytest.fixture(scope="session") +def client(request: FixtureRequest) -> Iterator[Gradient]: + strict = getattr(request, "param", True) + if not isinstance(strict, bool): + raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + + with Gradient(base_url=base_url, access_token=access_token, _strict_response_validation=strict) as client: + yield client + + +@pytest.fixture(scope="session") +async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncGradient]: + param = getattr(request, "param", True) + + # defaults + strict = True + http_client: None | httpx.AsyncClient = None + + if isinstance(param, bool): + strict = param + elif is_dict(param): + strict = param.get("strict", True) + assert isinstance(strict, bool) + + http_client_type = param.get("http_client", "httpx") + if http_client_type == "aiohttp": + http_client = DefaultAioHttpClient() + else: + raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") + + async with AsyncGradient( + base_url=base_url, access_token=access_token, _strict_response_validation=strict, http_client=http_client + ) as client: + yield client diff --git a/tests/sample_file.txt b/tests/sample_file.txt new file mode 100644 index 00000000..af5626b4 --- /dev/null +++ b/tests/sample_file.txt @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/test_client.py b/tests/test_client.py new file mode 100644 index 00000000..7b421637 --- /dev/null +++ b/tests/test_client.py @@ -0,0 +1,1890 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import gc +import os +import sys +import json +import asyncio +import inspect +import tracemalloc +from typing import Any, Union, cast +from unittest import mock +from typing_extensions import Literal + +import httpx +import pytest +from respx import MockRouter +from pydantic import ValidationError + +from gradient import Gradient, AsyncGradient, APIResponseValidationError +from gradient._types import Omit +from gradient._utils import asyncify +from gradient._models import BaseModel, FinalRequestOptions +from gradient._streaming import Stream, AsyncStream +from gradient._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError +from gradient._base_client import ( + DEFAULT_TIMEOUT, + HTTPX_DEFAULT_TIMEOUT, + BaseClient, + OtherPlatform, + DefaultHttpxClient, + DefaultAsyncHttpxClient, + get_platform, + make_request_options, +) + +from .utils import update_env + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") +access_token = "My Access Token" + + +def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + return dict(url.params) + + +def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: + return 0.1 + + +def _get_open_connections(client: Gradient | AsyncGradient) -> int: + transport = client._client._transport + assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) + + pool = transport._pool + return len(pool._requests) + + +class TestGradient: + @pytest.mark.respx(base_url=base_url) + def test_raw_response(self, respx_mock: MockRouter, client: Gradient) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + @pytest.mark.respx(base_url=base_url) + def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Gradient) -> None: + respx_mock.post("/foo").mock( + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') + ) + + response = client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + def test_copy(self, client: Gradient) -> None: + copied = client.copy() + assert id(copied) != id(client) + + copied = client.copy(access_token="another My Access Token") + assert copied.access_token == "another My Access Token" + assert client.access_token == "My Access Token" + + def test_copy_default_options(self, client: Gradient) -> None: + # options that have a default are overridden correctly + copied = client.copy(max_retries=7) + assert copied.max_retries == 7 + assert client.max_retries == 2 + + copied2 = copied.copy(max_retries=6) + assert copied2.max_retries == 6 + assert copied.max_retries == 7 + + # timeout + assert isinstance(client.timeout, httpx.Timeout) + copied = client.copy(timeout=None) + assert copied.timeout is None + assert isinstance(client.timeout, httpx.Timeout) + + def test_copy_default_headers(self) -> None: + client = Gradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, + ) + assert client.default_headers["X-Foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert copied.default_headers["X-Foo"] == "bar" + + # merges already given headers + copied = client.copy(default_headers={"X-Bar": "stainless"}) + assert copied.default_headers["X-Foo"] == "bar" + assert copied.default_headers["X-Bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_headers={"X-Foo": "stainless"}) + assert copied.default_headers["X-Foo"] == "stainless" + + # set_default_headers + + # completely overrides already set values + copied = client.copy(set_default_headers={}) + assert copied.default_headers.get("X-Foo") is None + + copied = client.copy(set_default_headers={"X-Bar": "Robert"}) + assert copied.default_headers["X-Bar"] == "Robert" + + with pytest.raises( + ValueError, + match="`default_headers` and `set_default_headers` arguments are mutually exclusive", + ): + client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + client.close() + + def test_copy_default_query(self) -> None: + client = Gradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, default_query={"foo": "bar"} + ) + assert _get_params(client)["foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert _get_params(copied)["foo"] == "bar" + + # merges already given params + copied = client.copy(default_query={"bar": "stainless"}) + params = _get_params(copied) + assert params["foo"] == "bar" + assert params["bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_query={"foo": "stainless"}) + assert _get_params(copied)["foo"] == "stainless" + + # set_default_query + + # completely overrides already set values + copied = client.copy(set_default_query={}) + assert _get_params(copied) == {} + + copied = client.copy(set_default_query={"bar": "Robert"}) + assert _get_params(copied)["bar"] == "Robert" + + with pytest.raises( + ValueError, + # TODO: update + match="`default_query` and `set_default_query` arguments are mutually exclusive", + ): + client.copy(set_default_query={}, default_query={"foo": "Bar"}) + + client.close() + + def test_copy_signature(self, client: Gradient) -> None: + # ensure the same parameters that can be passed to the client are defined in the `.copy()` method + init_signature = inspect.signature( + # mypy doesn't like that we access the `__init__` property. + client.__init__, # type: ignore[misc] + ) + copy_signature = inspect.signature(client.copy) + exclude_params = {"transport", "proxies", "_strict_response_validation"} + + for name in init_signature.parameters.keys(): + if name in exclude_params: + continue + + copy_param = copy_signature.parameters.get(name) + assert copy_param is not None, f"copy() signature is missing the {name} param" + + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") + def test_copy_build_request(self, client: Gradient) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client_copy = client.copy() + client_copy._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + + gc.collect() + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "gradient/_legacy_response.py", + "gradient/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "gradient/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + + def test_request_timeout(self, client: Gradient) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(100.0) + + def test_client_timeout_option(self) -> None: + client = Gradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, timeout=httpx.Timeout(0) + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(0) + + client.close() + + def test_http_client_timeout_option(self) -> None: + # custom timeout given to the httpx client should be used + with httpx.Client(timeout=None) as http_client: + client = Gradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(None) + + client.close() + + # no timeout given to the httpx client should not use the httpx default + with httpx.Client() as http_client: + client = Gradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + client.close() + + # explicitly passing the default timeout currently results in it being ignored + with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: + client = Gradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT # our default + + client.close() + + async def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + async with httpx.AsyncClient() as http_client: + Gradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + + def test_default_headers_option(self) -> None: + test_client = Gradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, + ) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "bar" + assert request.headers.get("x-stainless-lang") == "python" + + test_client2 = Gradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + test_client.close() + test_client2.close() + + def test_validate_headers(self) -> None: + client = Gradient(base_url=base_url, access_token=access_token, _strict_response_validation=True) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("Authorization") == f"Bearer {access_token}" + + with update_env(**{"DIGITALOCEAN_ACCESS_TOKEN": Omit()}): + client2 = Gradient(base_url=base_url, access_token=None, _strict_response_validation=True) + + with pytest.raises( + TypeError, + match="Could not resolve authentication method. Expected one of access_token, model_access_key or agent_access_key to be set. Or for one of the `Authorization`, `Authorization` or `Authorization` headers to be explicitly omitted", + ): + client2._build_request(FinalRequestOptions(method="get", url="/foo")) + + request2 = client2._build_request( + FinalRequestOptions(method="get", url="/foo", headers={"Authorization": Omit()}) + ) + assert request2.headers.get("Authorization") is None + + def test_default_query_option(self) -> None: + client = Gradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_query={"query_param": "bar"}, + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + assert dict(url.params) == {"query_param": "bar"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo", + params={"foo": "baz", "query_param": "overridden"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} + + client.close() + + def test_request_extra_json(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": False} + + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"baz": False} + + # `extra_json` takes priority over `json_data` when keys clash + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar", "baz": True}, + extra_json={"baz": None}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": None} + + def test_request_extra_headers(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options(extra_headers={"X-Foo": "Foo"}), + ), + ) + assert request.headers.get("X-Foo") == "Foo" + + # `extra_headers` takes priority over `default_headers` when keys clash + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_headers={"X-Bar": "false"}, + ), + ), + ) + assert request.headers.get("X-Bar") == "false" + + def test_request_extra_query(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_query={"my_query_param": "Foo"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"my_query_param": "Foo"} + + # if both `query` and `extra_query` are given, they are merged + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"bar": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"bar": "1", "foo": "2"} + + # `extra_query` takes priority over `query` when keys clash + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"foo": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"foo": "2"} + + def test_multipart_repeating_array(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions.construct( + method="post", + url="/foo", + headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, + json_data={"array": ["foo", "bar"]}, + files=[("foo.txt", b"hello world")], + ) + ) + + assert request.read().split(b"\r\n") == [ + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"foo", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"bar", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', + b"Content-Type: application/octet-stream", + b"", + b"hello world", + b"--6b7ba517decee4a450543ea6ae821c82--", + b"", + ] + + @pytest.mark.respx(base_url=base_url) + def test_basic_union_response(self, respx_mock: MockRouter, client: Gradient) -> None: + class Model1(BaseModel): + name: str + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + @pytest.mark.respx(base_url=base_url) + def test_union_response_different_types(self, respx_mock: MockRouter, client: Gradient) -> None: + """Union of objects with the same field name using a different type""" + + class Model1(BaseModel): + foo: int + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) + + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model1) + assert response.foo == 1 + + @pytest.mark.respx(base_url=base_url) + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Gradient) -> None: + """ + Response that sets Content-Type to something other than application/json but returns json data + """ + + class Model(BaseModel): + foo: int + + respx_mock.get("/foo").mock( + return_value=httpx.Response( + 200, + content=json.dumps({"foo": 2}), + headers={"Content-Type": "application/text"}, + ) + ) + + response = client.get("/foo", cast_to=Model) + assert isinstance(response, Model) + assert response.foo == 2 + + def test_base_url_setter(self) -> None: + client = Gradient( + base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True + ) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + + client.close() + + def test_base_url_env(self) -> None: + with update_env(GRADIENT_BASE_URL="http://localhost:5000/from/env"): + client = Gradient(access_token=access_token, _strict_response_validation=True) + assert client.base_url == "http://localhost:5000/from/env/" + + @pytest.mark.parametrize( + "client", + [ + Gradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + ), + Gradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_trailing_slash(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + client.close() + + @pytest.mark.parametrize( + "client", + [ + Gradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + ), + Gradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_no_trailing_slash(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + client.close() + + @pytest.mark.parametrize( + "client", + [ + Gradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + ), + Gradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_absolute_request_url(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="https://myapi.com/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "https://myapi.com/foo" + client.close() + + def test_copied_client_does_not_close_http(self) -> None: + test_client = Gradient(base_url=base_url, access_token=access_token, _strict_response_validation=True) + assert not test_client.is_closed() + + copied = test_client.copy() + assert copied is not test_client + + del copied + + assert not test_client.is_closed() + + def test_client_context_manager(self) -> None: + test_client = Gradient(base_url=base_url, access_token=access_token, _strict_response_validation=True) + with test_client as c2: + assert c2 is test_client + assert not c2.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() + + @pytest.mark.respx(base_url=base_url) + def test_client_response_validation_error(self, respx_mock: MockRouter, client: Gradient) -> None: + class Model(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) + + with pytest.raises(APIResponseValidationError) as exc: + client.get("/foo", cast_to=Model) + + assert isinstance(exc.value.__cause__, ValidationError) + + def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + Gradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + max_retries=cast(Any, None), + ) + + @pytest.mark.respx(base_url=base_url) + def test_default_stream_cls(self, respx_mock: MockRouter, client: Gradient) -> None: + class Model(BaseModel): + name: str + + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + stream = client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model]) + assert isinstance(stream, Stream) + stream.response.close() + + @pytest.mark.respx(base_url=base_url) + def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) + + strict_client = Gradient(base_url=base_url, access_token=access_token, _strict_response_validation=True) + + with pytest.raises(APIResponseValidationError): + strict_client.get("/foo", cast_to=Model) + + non_strict_client = Gradient(base_url=base_url, access_token=access_token, _strict_response_validation=False) + + response = non_strict_client.get("/foo", cast_to=Model) + assert isinstance(response, str) # type: ignore[unreachable] + + strict_client.close() + non_strict_client.close() + + @pytest.mark.parametrize( + "remaining_retries,retry_after,timeout", + [ + [3, "20", 20], + [3, "0", 0.5], + [3, "-10", 0.5], + [3, "60", 60], + [3, "61", 0.5], + [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], + [-1100, "", 8], # test large number potentially overflowing + ], + ) + @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, client: Gradient + ) -> None: + headers = httpx.Headers({"retry-after": retry_after}) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) + calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Gradient) -> None: + respx_mock.post("/chat/completions").mock(side_effect=httpx.TimeoutException("Test timeout error")) + + with pytest.raises(APITimeoutError): + client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ).__enter__() + + assert _get_open_connections(client) == 0 + + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Gradient) -> None: + respx_mock.post("/chat/completions").mock(return_value=httpx.Response(500)) + + with pytest.raises(APIStatusError): + client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ).__enter__() + assert _get_open_connections(client) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + def test_retries_taken( + self, + client: Gradient, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_omit_retry_count_header( + self, client: Gradient, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + extra_headers={"x-stainless-retry-count": Omit()}, + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_overwrite_retry_count_header( + self, client: Gradient, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + extra_headers={"x-stainless-retry-count": "42"}, + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + + def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + + client = DefaultHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects(self, respx_mock: MockRouter, client: Gradient) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Gradient) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" + + +class TestAsyncGradient: + @pytest.mark.respx(base_url=base_url) + async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + respx_mock.post("/foo").mock( + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') + ) + + response = await async_client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + def test_copy(self, async_client: AsyncGradient) -> None: + copied = async_client.copy() + assert id(copied) != id(async_client) + + copied = async_client.copy(access_token="another My Access Token") + assert copied.access_token == "another My Access Token" + assert async_client.access_token == "My Access Token" + + def test_copy_default_options(self, async_client: AsyncGradient) -> None: + # options that have a default are overridden correctly + copied = async_client.copy(max_retries=7) + assert copied.max_retries == 7 + assert async_client.max_retries == 2 + + copied2 = copied.copy(max_retries=6) + assert copied2.max_retries == 6 + assert copied.max_retries == 7 + + # timeout + assert isinstance(async_client.timeout, httpx.Timeout) + copied = async_client.copy(timeout=None) + assert copied.timeout is None + assert isinstance(async_client.timeout, httpx.Timeout) + + async def test_copy_default_headers(self) -> None: + client = AsyncGradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, + ) + assert client.default_headers["X-Foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert copied.default_headers["X-Foo"] == "bar" + + # merges already given headers + copied = client.copy(default_headers={"X-Bar": "stainless"}) + assert copied.default_headers["X-Foo"] == "bar" + assert copied.default_headers["X-Bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_headers={"X-Foo": "stainless"}) + assert copied.default_headers["X-Foo"] == "stainless" + + # set_default_headers + + # completely overrides already set values + copied = client.copy(set_default_headers={}) + assert copied.default_headers.get("X-Foo") is None + + copied = client.copy(set_default_headers={"X-Bar": "Robert"}) + assert copied.default_headers["X-Bar"] == "Robert" + + with pytest.raises( + ValueError, + match="`default_headers` and `set_default_headers` arguments are mutually exclusive", + ): + client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + await client.close() + + async def test_copy_default_query(self) -> None: + client = AsyncGradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, default_query={"foo": "bar"} + ) + assert _get_params(client)["foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert _get_params(copied)["foo"] == "bar" + + # merges already given params + copied = client.copy(default_query={"bar": "stainless"}) + params = _get_params(copied) + assert params["foo"] == "bar" + assert params["bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_query={"foo": "stainless"}) + assert _get_params(copied)["foo"] == "stainless" + + # set_default_query + + # completely overrides already set values + copied = client.copy(set_default_query={}) + assert _get_params(copied) == {} + + copied = client.copy(set_default_query={"bar": "Robert"}) + assert _get_params(copied)["bar"] == "Robert" + + with pytest.raises( + ValueError, + # TODO: update + match="`default_query` and `set_default_query` arguments are mutually exclusive", + ): + client.copy(set_default_query={}, default_query={"foo": "Bar"}) + + await client.close() + + def test_copy_signature(self, async_client: AsyncGradient) -> None: + # ensure the same parameters that can be passed to the client are defined in the `.copy()` method + init_signature = inspect.signature( + # mypy doesn't like that we access the `__init__` property. + async_client.__init__, # type: ignore[misc] + ) + copy_signature = inspect.signature(async_client.copy) + exclude_params = {"transport", "proxies", "_strict_response_validation"} + + for name in init_signature.parameters.keys(): + if name in exclude_params: + continue + + copy_param = copy_signature.parameters.get(name) + assert copy_param is not None, f"copy() signature is missing the {name} param" + + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") + def test_copy_build_request(self, async_client: AsyncGradient) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client_copy = async_client.copy() + client_copy._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + + gc.collect() + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "gradient/_legacy_response.py", + "gradient/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "gradient/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + + async def test_request_timeout(self, async_client: AsyncGradient) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + request = async_client._build_request( + FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) + ) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(100.0) + + async def test_client_timeout_option(self) -> None: + client = AsyncGradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, timeout=httpx.Timeout(0) + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(0) + + await client.close() + + async def test_http_client_timeout_option(self) -> None: + # custom timeout given to the httpx client should be used + async with httpx.AsyncClient(timeout=None) as http_client: + client = AsyncGradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(None) + + await client.close() + + # no timeout given to the httpx client should not use the httpx default + async with httpx.AsyncClient() as http_client: + client = AsyncGradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + await client.close() + + # explicitly passing the default timeout currently results in it being ignored + async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: + client = AsyncGradient( + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT # our default + + await client.close() + + def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + with httpx.Client() as http_client: + AsyncGradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + + async def test_default_headers_option(self) -> None: + test_client = AsyncGradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, + ) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "bar" + assert request.headers.get("x-stainless-lang") == "python" + + test_client2 = AsyncGradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + await test_client.close() + await test_client2.close() + + def test_validate_headers(self) -> None: + client = AsyncGradient(base_url=base_url, access_token=access_token, _strict_response_validation=True) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("Authorization") == f"Bearer {access_token}" + + with update_env(**{"DIGITALOCEAN_ACCESS_TOKEN": Omit()}): + client2 = AsyncGradient(base_url=base_url, access_token=None, _strict_response_validation=True) + + with pytest.raises( + TypeError, + match="Could not resolve authentication method. Expected one of access_token, model_access_key or agent_access_key to be set. Or for one of the `Authorization`, `Authorization` or `Authorization` headers to be explicitly omitted", + ): + client2._build_request(FinalRequestOptions(method="get", url="/foo")) + + request2 = client2._build_request( + FinalRequestOptions(method="get", url="/foo", headers={"Authorization": Omit()}) + ) + assert request2.headers.get("Authorization") is None + + async def test_default_query_option(self) -> None: + client = AsyncGradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_query={"query_param": "bar"}, + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + assert dict(url.params) == {"query_param": "bar"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo", + params={"foo": "baz", "query_param": "overridden"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} + + await client.close() + + def test_request_extra_json(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": False} + + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"baz": False} + + # `extra_json` takes priority over `json_data` when keys clash + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar", "baz": True}, + extra_json={"baz": None}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": None} + + def test_request_extra_headers(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options(extra_headers={"X-Foo": "Foo"}), + ), + ) + assert request.headers.get("X-Foo") == "Foo" + + # `extra_headers` takes priority over `default_headers` when keys clash + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_headers={"X-Bar": "false"}, + ), + ), + ) + assert request.headers.get("X-Bar") == "false" + + def test_request_extra_query(self, client: Gradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_query={"my_query_param": "Foo"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"my_query_param": "Foo"} + + # if both `query` and `extra_query` are given, they are merged + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"bar": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"bar": "1", "foo": "2"} + + # `extra_query` takes priority over `query` when keys clash + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"foo": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"foo": "2"} + + def test_multipart_repeating_array(self, async_client: AsyncGradient) -> None: + request = async_client._build_request( + FinalRequestOptions.construct( + method="post", + url="/foo", + headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, + json_data={"array": ["foo", "bar"]}, + files=[("foo.txt", b"hello world")], + ) + ) + + assert request.read().split(b"\r\n") == [ + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"foo", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"bar", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', + b"Content-Type: application/octet-stream", + b"", + b"hello world", + b"--6b7ba517decee4a450543ea6ae821c82--", + b"", + ] + + @pytest.mark.respx(base_url=base_url) + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + class Model1(BaseModel): + name: str + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + @pytest.mark.respx(base_url=base_url) + async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + """Union of objects with the same field name using a different type""" + + class Model1(BaseModel): + foo: int + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) + + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model1) + assert response.foo == 1 + + @pytest.mark.respx(base_url=base_url) + async def test_non_application_json_content_type_for_json_data( + self, respx_mock: MockRouter, async_client: AsyncGradient + ) -> None: + """ + Response that sets Content-Type to something other than application/json but returns json data + """ + + class Model(BaseModel): + foo: int + + respx_mock.get("/foo").mock( + return_value=httpx.Response( + 200, + content=json.dumps({"foo": 2}), + headers={"Content-Type": "application/text"}, + ) + ) + + response = await async_client.get("/foo", cast_to=Model) + assert isinstance(response, Model) + assert response.foo == 2 + + async def test_base_url_setter(self) -> None: + client = AsyncGradient( + base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True + ) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + + await client.close() + + async def test_base_url_env(self) -> None: + with update_env(GRADIENT_BASE_URL="http://localhost:5000/from/env"): + client = AsyncGradient(access_token=access_token, _strict_response_validation=True) + assert client.base_url == "http://localhost:5000/from/env/" + + @pytest.mark.parametrize( + "client", + [ + AsyncGradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + ), + AsyncGradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + async def test_base_url_trailing_slash(self, client: AsyncGradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() + + @pytest.mark.parametrize( + "client", + [ + AsyncGradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + ), + AsyncGradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + async def test_base_url_no_trailing_slash(self, client: AsyncGradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() + + @pytest.mark.parametrize( + "client", + [ + AsyncGradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + ), + AsyncGradient( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + async def test_absolute_request_url(self, client: AsyncGradient) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="https://myapi.com/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "https://myapi.com/foo" + await client.close() + + async def test_copied_client_does_not_close_http(self) -> None: + test_client = AsyncGradient(base_url=base_url, access_token=access_token, _strict_response_validation=True) + assert not test_client.is_closed() + + copied = test_client.copy() + assert copied is not test_client + + del copied + + await asyncio.sleep(0.2) + assert not test_client.is_closed() + + async def test_client_context_manager(self) -> None: + test_client = AsyncGradient(base_url=base_url, access_token=access_token, _strict_response_validation=True) + async with test_client as c2: + assert c2 is test_client + assert not c2.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() + + @pytest.mark.respx(base_url=base_url) + async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + class Model(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) + + with pytest.raises(APIResponseValidationError) as exc: + await async_client.get("/foo", cast_to=Model) + + assert isinstance(exc.value.__cause__, ValidationError) + + async def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + AsyncGradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + max_retries=cast(Any, None), + ) + + @pytest.mark.respx(base_url=base_url) + async def test_default_stream_cls(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + class Model(BaseModel): + name: str + + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + stream = await async_client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model]) + assert isinstance(stream, AsyncStream) + await stream.response.aclose() + + @pytest.mark.respx(base_url=base_url) + async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) + + strict_client = AsyncGradient(base_url=base_url, access_token=access_token, _strict_response_validation=True) + + with pytest.raises(APIResponseValidationError): + await strict_client.get("/foo", cast_to=Model) + + non_strict_client = AsyncGradient( + base_url=base_url, access_token=access_token, _strict_response_validation=False + ) + + response = await non_strict_client.get("/foo", cast_to=Model) + assert isinstance(response, str) # type: ignore[unreachable] + + await strict_client.close() + await non_strict_client.close() + + @pytest.mark.parametrize( + "remaining_retries,retry_after,timeout", + [ + [3, "20", 20], + [3, "0", 0.5], + [3, "-10", 0.5], + [3, "60", 60], + [3, "61", 0.5], + [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], + [-1100, "", 8], # test large number potentially overflowing + ], + ) + @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncGradient + ) -> None: + headers = httpx.Headers({"retry-after": retry_after}) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) + calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_retrying_timeout_errors_doesnt_leak( + self, respx_mock: MockRouter, async_client: AsyncGradient + ) -> None: + respx_mock.post("/chat/completions").mock(side_effect=httpx.TimeoutException("Test timeout error")) + + with pytest.raises(APITimeoutError): + await async_client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ).__aenter__() + + assert _get_open_connections(async_client) == 0 + + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_retrying_status_errors_doesnt_leak( + self, respx_mock: MockRouter, async_client: AsyncGradient + ) -> None: + respx_mock.post("/chat/completions").mock(return_value=httpx.Response(500)) + + with pytest.raises(APIStatusError): + await async_client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ).__aenter__() + assert _get_open_connections(async_client) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + async def test_retries_taken( + self, + async_client: AsyncGradient, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = await client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + ) + + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_omit_retry_count_header( + self, async_client: AsyncGradient, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = await client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + extra_headers={"x-stainless-retry-count": Omit()}, + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("gradient._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_overwrite_retry_count_header( + self, async_client: AsyncGradient, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = await client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="llama3-8b-instruct", + extra_headers={"x-stainless-retry-count": "42"}, + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + + async def test_get_platform(self) -> None: + platform = await asyncify(get_platform)() + assert isinstance(platform, (str, OtherPlatform)) + + async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + + client = DefaultAsyncHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + async def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultAsyncHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + await async_client.post( + "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response + ) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py new file mode 100644 index 00000000..b5520a27 --- /dev/null +++ b/tests/test_deepcopy.py @@ -0,0 +1,58 @@ +from gradient._utils import deepcopy_minimal + + +def assert_different_identities(obj1: object, obj2: object) -> None: + assert obj1 == obj2 + assert id(obj1) != id(obj2) + + +def test_simple_dict() -> None: + obj1 = {"foo": "bar"} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + + +def test_nested_dict() -> None: + obj1 = {"foo": {"bar": True}} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1["foo"], obj2["foo"]) + + +def test_complex_nested_dict() -> None: + obj1 = {"foo": {"bar": [{"hello": "world"}]}} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1["foo"], obj2["foo"]) + assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"]) + assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0]) + + +def test_simple_list() -> None: + obj1 = ["a", "b", "c"] + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + + +def test_nested_list() -> None: + obj1 = ["a", [1, 2, 3]] + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1[1], obj2[1]) + + +class MyObject: ... + + +def test_ignores_other_types() -> None: + # custom classes + my_obj = MyObject() + obj1 = {"foo": my_obj} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert obj1["foo"] is my_obj + + # tuples + obj3 = ("a", "b") + obj4 = deepcopy_minimal(obj3) + assert obj3 is obj4 diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py new file mode 100644 index 00000000..9514d242 --- /dev/null +++ b/tests/test_extract_files.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import Sequence + +import pytest + +from gradient._types import FileTypes +from gradient._utils import extract_files + + +def test_removes_files_from_input() -> None: + query = {"foo": "bar"} + assert extract_files(query, paths=[]) == [] + assert query == {"foo": "bar"} + + query2 = {"foo": b"Bar", "hello": "world"} + assert extract_files(query2, paths=[["foo"]]) == [("foo", b"Bar")] + assert query2 == {"hello": "world"} + + query3 = {"foo": {"foo": {"bar": b"Bar"}}, "hello": "world"} + assert extract_files(query3, paths=[["foo", "foo", "bar"]]) == [("foo[foo][bar]", b"Bar")] + assert query3 == {"foo": {"foo": {}}, "hello": "world"} + + query4 = {"foo": {"bar": b"Bar", "baz": "foo"}, "hello": "world"} + assert extract_files(query4, paths=[["foo", "bar"]]) == [("foo[bar]", b"Bar")] + assert query4 == {"hello": "world", "foo": {"baz": "foo"}} + + +def test_multiple_files() -> None: + query = {"documents": [{"file": b"My first file"}, {"file": b"My second file"}]} + assert extract_files(query, paths=[["documents", "", "file"]]) == [ + ("documents[][file]", b"My first file"), + ("documents[][file]", b"My second file"), + ] + assert query == {"documents": [{}, {}]} + + +@pytest.mark.parametrize( + "query,paths,expected", + [ + [ + {"foo": {"bar": "baz"}}, + [["foo", "", "bar"]], + [], + ], + [ + {"foo": ["bar", "baz"]}, + [["foo", "bar"]], + [], + ], + [ + {"foo": {"bar": "baz"}}, + [["foo", "foo"]], + [], + ], + ], + ids=["dict expecting array", "array expecting dict", "unknown keys"], +) +def test_ignores_incorrect_paths( + query: dict[str, object], + paths: Sequence[Sequence[str]], + expected: list[tuple[str, FileTypes]], +) -> None: + assert extract_files(query, paths=paths) == expected diff --git a/tests/test_files.py b/tests/test_files.py new file mode 100644 index 00000000..4d9f4066 --- /dev/null +++ b/tests/test_files.py @@ -0,0 +1,51 @@ +from pathlib import Path + +import anyio +import pytest +from dirty_equals import IsDict, IsList, IsBytes, IsTuple + +from gradient._files import to_httpx_files, async_to_httpx_files + +readme_path = Path(__file__).parent.parent.joinpath("README.md") + + +def test_pathlib_includes_file_name() -> None: + result = to_httpx_files({"file": readme_path}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +def test_tuple_input() -> None: + result = to_httpx_files([("file", readme_path)]) + print(result) + assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) + + +@pytest.mark.asyncio +async def test_async_pathlib_includes_file_name() -> None: + result = await async_to_httpx_files({"file": readme_path}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +@pytest.mark.asyncio +async def test_async_supports_anyio_path() -> None: + result = await async_to_httpx_files({"file": anyio.Path(readme_path)}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +@pytest.mark.asyncio +async def test_async_tuple_input() -> None: + result = await async_to_httpx_files([("file", readme_path)]) + print(result) + assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) + + +def test_string_not_allowed() -> None: + with pytest.raises(TypeError, match="Expected file types input to be a FileContent type or to be a tuple"): + to_httpx_files( + { + "file": "foo", # type: ignore + } + ) diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 00000000..ba635571 --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,963 @@ +import json +from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast +from datetime import datetime, timezone +from typing_extensions import Literal, Annotated, TypeAliasType + +import pytest +import pydantic +from pydantic import Field + +from gradient._utils import PropertyInfo +from gradient._compat import PYDANTIC_V1, parse_obj, model_dump, model_json +from gradient._models import DISCRIMINATOR_CACHE, BaseModel, construct_type + + +class BasicModel(BaseModel): + foo: str + + +@pytest.mark.parametrize("value", ["hello", 1], ids=["correct type", "mismatched"]) +def test_basic(value: object) -> None: + m = BasicModel.construct(foo=value) + assert m.foo == value + + +def test_directly_nested_model() -> None: + class NestedModel(BaseModel): + nested: BasicModel + + m = NestedModel.construct(nested={"foo": "Foo!"}) + assert m.nested.foo == "Foo!" + + # mismatched types + m = NestedModel.construct(nested="hello!") + assert cast(Any, m.nested) == "hello!" + + +def test_optional_nested_model() -> None: + class NestedModel(BaseModel): + nested: Optional[BasicModel] + + m1 = NestedModel.construct(nested=None) + assert m1.nested is None + + m2 = NestedModel.construct(nested={"foo": "bar"}) + assert m2.nested is not None + assert m2.nested.foo == "bar" + + # mismatched types + m3 = NestedModel.construct(nested={"foo"}) + assert isinstance(cast(Any, m3.nested), set) + assert cast(Any, m3.nested) == {"foo"} + + +def test_list_nested_model() -> None: + class NestedModel(BaseModel): + nested: List[BasicModel] + + m = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) + assert m.nested is not None + assert isinstance(m.nested, list) + assert len(m.nested) == 2 + assert m.nested[0].foo == "bar" + assert m.nested[1].foo == "2" + + # mismatched types + m = NestedModel.construct(nested=True) + assert cast(Any, m.nested) is True + + m = NestedModel.construct(nested=[False]) + assert cast(Any, m.nested) == [False] + + +def test_optional_list_nested_model() -> None: + class NestedModel(BaseModel): + nested: Optional[List[BasicModel]] + + m1 = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) + assert m1.nested is not None + assert isinstance(m1.nested, list) + assert len(m1.nested) == 2 + assert m1.nested[0].foo == "bar" + assert m1.nested[1].foo == "2" + + m2 = NestedModel.construct(nested=None) + assert m2.nested is None + + # mismatched types + m3 = NestedModel.construct(nested={1}) + assert cast(Any, m3.nested) == {1} + + m4 = NestedModel.construct(nested=[False]) + assert cast(Any, m4.nested) == [False] + + +def test_list_optional_items_nested_model() -> None: + class NestedModel(BaseModel): + nested: List[Optional[BasicModel]] + + m = NestedModel.construct(nested=[None, {"foo": "bar"}]) + assert m.nested is not None + assert isinstance(m.nested, list) + assert len(m.nested) == 2 + assert m.nested[0] is None + assert m.nested[1] is not None + assert m.nested[1].foo == "bar" + + # mismatched types + m3 = NestedModel.construct(nested="foo") + assert cast(Any, m3.nested) == "foo" + + m4 = NestedModel.construct(nested=[False]) + assert cast(Any, m4.nested) == [False] + + +def test_list_mismatched_type() -> None: + class NestedModel(BaseModel): + nested: List[str] + + m = NestedModel.construct(nested=False) + assert cast(Any, m.nested) is False + + +def test_raw_dictionary() -> None: + class NestedModel(BaseModel): + nested: Dict[str, str] + + m = NestedModel.construct(nested={"hello": "world"}) + assert m.nested == {"hello": "world"} + + # mismatched types + m = NestedModel.construct(nested=False) + assert cast(Any, m.nested) is False + + +def test_nested_dictionary_model() -> None: + class NestedModel(BaseModel): + nested: Dict[str, BasicModel] + + m = NestedModel.construct(nested={"hello": {"foo": "bar"}}) + assert isinstance(m.nested, dict) + assert m.nested["hello"].foo == "bar" + + # mismatched types + m = NestedModel.construct(nested={"hello": False}) + assert cast(Any, m.nested["hello"]) is False + + +def test_unknown_fields() -> None: + m1 = BasicModel.construct(foo="foo", unknown=1) + assert m1.foo == "foo" + assert cast(Any, m1).unknown == 1 + + m2 = BasicModel.construct(foo="foo", unknown={"foo_bar": True}) + assert m2.foo == "foo" + assert cast(Any, m2).unknown == {"foo_bar": True} + + assert model_dump(m2) == {"foo": "foo", "unknown": {"foo_bar": True}} + + +def test_strict_validation_unknown_fields() -> None: + class Model(BaseModel): + foo: str + + model = parse_obj(Model, dict(foo="hello!", user="Robert")) + assert model.foo == "hello!" + assert cast(Any, model).user == "Robert" + + assert model_dump(model) == {"foo": "hello!", "user": "Robert"} + + +def test_aliases() -> None: + class Model(BaseModel): + my_field: int = Field(alias="myField") + + m = Model.construct(myField=1) + assert m.my_field == 1 + + # mismatched types + m = Model.construct(myField={"hello": False}) + assert cast(Any, m.my_field) == {"hello": False} + + +def test_repr() -> None: + model = BasicModel(foo="bar") + assert str(model) == "BasicModel(foo='bar')" + assert repr(model) == "BasicModel(foo='bar')" + + +def test_repr_nested_model() -> None: + class Child(BaseModel): + name: str + age: int + + class Parent(BaseModel): + name: str + child: Child + + model = Parent(name="Robert", child=Child(name="Foo", age=5)) + assert str(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" + assert repr(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" + + +def test_optional_list() -> None: + class Submodel(BaseModel): + name: str + + class Model(BaseModel): + items: Optional[List[Submodel]] + + m = Model.construct(items=None) + assert m.items is None + + m = Model.construct(items=[]) + assert m.items == [] + + m = Model.construct(items=[{"name": "Robert"}]) + assert m.items is not None + assert len(m.items) == 1 + assert m.items[0].name == "Robert" + + +def test_nested_union_of_models() -> None: + class Submodel1(BaseModel): + bar: bool + + class Submodel2(BaseModel): + thing: str + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2] + + m = Model.construct(foo={"thing": "hello"}) + assert isinstance(m.foo, Submodel2) + assert m.foo.thing == "hello" + + +def test_nested_union_of_mixed_types() -> None: + class Submodel1(BaseModel): + bar: bool + + class Model(BaseModel): + foo: Union[Submodel1, Literal[True], Literal["CARD_HOLDER"]] + + m = Model.construct(foo=True) + assert m.foo is True + + m = Model.construct(foo="CARD_HOLDER") + assert m.foo == "CARD_HOLDER" + + m = Model.construct(foo={"bar": False}) + assert isinstance(m.foo, Submodel1) + assert m.foo.bar is False + + +def test_nested_union_multiple_variants() -> None: + class Submodel1(BaseModel): + bar: bool + + class Submodel2(BaseModel): + thing: str + + class Submodel3(BaseModel): + foo: int + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2, None, Submodel3] + + m = Model.construct(foo={"thing": "hello"}) + assert isinstance(m.foo, Submodel2) + assert m.foo.thing == "hello" + + m = Model.construct(foo=None) + assert m.foo is None + + m = Model.construct() + assert m.foo is None + + m = Model.construct(foo={"foo": "1"}) + assert isinstance(m.foo, Submodel3) + assert m.foo.foo == 1 + + +def test_nested_union_invalid_data() -> None: + class Submodel1(BaseModel): + level: int + + class Submodel2(BaseModel): + name: str + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2] + + m = Model.construct(foo=True) + assert cast(bool, m.foo) is True + + m = Model.construct(foo={"name": 3}) + if PYDANTIC_V1: + assert isinstance(m.foo, Submodel2) + assert m.foo.name == "3" + else: + assert isinstance(m.foo, Submodel1) + assert m.foo.name == 3 # type: ignore + + +def test_list_of_unions() -> None: + class Submodel1(BaseModel): + level: int + + class Submodel2(BaseModel): + name: str + + class Model(BaseModel): + items: List[Union[Submodel1, Submodel2]] + + m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) + assert len(m.items) == 2 + assert isinstance(m.items[0], Submodel1) + assert m.items[0].level == 1 + assert isinstance(m.items[1], Submodel2) + assert m.items[1].name == "Robert" + + m = Model.construct(items=[{"level": -1}, 156]) + assert len(m.items) == 2 + assert isinstance(m.items[0], Submodel1) + assert m.items[0].level == -1 + assert cast(Any, m.items[1]) == 156 + + +def test_union_of_lists() -> None: + class SubModel1(BaseModel): + level: int + + class SubModel2(BaseModel): + name: str + + class Model(BaseModel): + items: Union[List[SubModel1], List[SubModel2]] + + # with one valid entry + m = Model.construct(items=[{"name": "Robert"}]) + assert len(m.items) == 1 + assert isinstance(m.items[0], SubModel2) + assert m.items[0].name == "Robert" + + # with two entries pointing to different types + m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) + assert len(m.items) == 2 + assert isinstance(m.items[0], SubModel1) + assert m.items[0].level == 1 + assert isinstance(m.items[1], SubModel1) + assert cast(Any, m.items[1]).name == "Robert" + + # with two entries pointing to *completely* different types + m = Model.construct(items=[{"level": -1}, 156]) + assert len(m.items) == 2 + assert isinstance(m.items[0], SubModel1) + assert m.items[0].level == -1 + assert cast(Any, m.items[1]) == 156 + + +def test_dict_of_union() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + foo: str + + class Model(BaseModel): + data: Dict[str, Union[SubModel1, SubModel2]] + + m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) + assert len(list(m.data.keys())) == 2 + assert isinstance(m.data["hello"], SubModel1) + assert m.data["hello"].name == "there" + assert isinstance(m.data["foo"], SubModel2) + assert m.data["foo"].foo == "bar" + + # TODO: test mismatched type + + +def test_double_nested_union() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + bar: str + + class Model(BaseModel): + data: Dict[str, List[Union[SubModel1, SubModel2]]] + + m = Model.construct(data={"foo": [{"bar": "baz"}, {"name": "Robert"}]}) + assert len(m.data["foo"]) == 2 + + entry1 = m.data["foo"][0] + assert isinstance(entry1, SubModel2) + assert entry1.bar == "baz" + + entry2 = m.data["foo"][1] + assert isinstance(entry2, SubModel1) + assert entry2.name == "Robert" + + # TODO: test mismatched type + + +def test_union_of_dict() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + foo: str + + class Model(BaseModel): + data: Union[Dict[str, SubModel1], Dict[str, SubModel2]] + + m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) + assert len(list(m.data.keys())) == 2 + assert isinstance(m.data["hello"], SubModel1) + assert m.data["hello"].name == "there" + assert isinstance(m.data["foo"], SubModel1) + assert cast(Any, m.data["foo"]).foo == "bar" + + +def test_iso8601_datetime() -> None: + class Model(BaseModel): + created_at: datetime + + expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) + + if PYDANTIC_V1: + expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' + else: + expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' + + model = Model.construct(created_at="2019-12-27T18:11:19.117Z") + assert model.created_at == expected + assert model_json(model) == expected_json + + model = parse_obj(Model, dict(created_at="2019-12-27T18:11:19.117Z")) + assert model.created_at == expected + assert model_json(model) == expected_json + + +def test_does_not_coerce_int() -> None: + class Model(BaseModel): + bar: int + + assert Model.construct(bar=1).bar == 1 + assert Model.construct(bar=10.9).bar == 10.9 + assert Model.construct(bar="19").bar == "19" # type: ignore[comparison-overlap] + assert Model.construct(bar=False).bar is False + + +def test_int_to_float_safe_conversion() -> None: + class Model(BaseModel): + float_field: float + + m = Model.construct(float_field=10) + assert m.float_field == 10.0 + assert isinstance(m.float_field, float) + + m = Model.construct(float_field=10.12) + assert m.float_field == 10.12 + assert isinstance(m.float_field, float) + + # number too big + m = Model.construct(float_field=2**53 + 1) + assert m.float_field == 2**53 + 1 + assert isinstance(m.float_field, int) + + +def test_deprecated_alias() -> None: + class Model(BaseModel): + resource_id: str = Field(alias="model_id") + + @property + def model_id(self) -> str: + return self.resource_id + + m = Model.construct(model_id="id") + assert m.model_id == "id" + assert m.resource_id == "id" + assert m.resource_id is m.model_id + + m = parse_obj(Model, {"model_id": "id"}) + assert m.model_id == "id" + assert m.resource_id == "id" + assert m.resource_id is m.model_id + + +def test_omitted_fields() -> None: + class Model(BaseModel): + resource_id: Optional[str] = None + + m = Model.construct() + assert m.resource_id is None + assert "resource_id" not in m.model_fields_set + + m = Model.construct(resource_id=None) + assert m.resource_id is None + assert "resource_id" in m.model_fields_set + + m = Model.construct(resource_id="foo") + assert m.resource_id == "foo" + assert "resource_id" in m.model_fields_set + + +def test_to_dict() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.to_dict() == {"FOO": "hello"} + assert m.to_dict(use_api_names=False) == {"foo": "hello"} + + m2 = Model() + assert m2.to_dict() == {} + assert m2.to_dict(exclude_unset=False) == {"FOO": None} + assert m2.to_dict(exclude_unset=False, exclude_none=True) == {} + assert m2.to_dict(exclude_unset=False, exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.to_dict() == {"FOO": None} + assert m3.to_dict(exclude_none=True) == {} + assert m3.to_dict(exclude_defaults=True) == {} + + class Model2(BaseModel): + created_at: datetime + + time_str = "2024-03-21T11:39:01.275859" + m4 = Model2.construct(created_at=time_str) + assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} + assert m4.to_dict(mode="json") == {"created_at": time_str} + + if PYDANTIC_V1: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_dict(warnings=False) + + +def test_forwards_compat_model_dump_method() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.model_dump() == {"foo": "hello"} + assert m.model_dump(include={"bar"}) == {} + assert m.model_dump(exclude={"foo"}) == {} + assert m.model_dump(by_alias=True) == {"FOO": "hello"} + + m2 = Model() + assert m2.model_dump() == {"foo": None} + assert m2.model_dump(exclude_unset=True) == {} + assert m2.model_dump(exclude_none=True) == {} + assert m2.model_dump(exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.model_dump() == {"foo": None} + assert m3.model_dump(exclude_none=True) == {} + + if PYDANTIC_V1: + with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): + m.model_dump(round_trip=True) + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.model_dump(warnings=False) + + +def test_compat_method_no_error_for_warnings() -> None: + class Model(BaseModel): + foo: Optional[str] + + m = Model(foo="hello") + assert isinstance(model_dump(m, warnings=False), dict) + + +def test_to_json() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.to_json()) == {"FOO": "hello"} + assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} + + if PYDANTIC_V1: + assert m.to_json(indent=None) == '{"FOO": "hello"}' + else: + assert m.to_json(indent=None) == '{"FOO":"hello"}' + + m2 = Model() + assert json.loads(m2.to_json()) == {} + assert json.loads(m2.to_json(exclude_unset=False)) == {"FOO": None} + assert json.loads(m2.to_json(exclude_unset=False, exclude_none=True)) == {} + assert json.loads(m2.to_json(exclude_unset=False, exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.to_json()) == {"FOO": None} + assert json.loads(m3.to_json(exclude_none=True)) == {} + + if PYDANTIC_V1: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_json(warnings=False) + + +def test_forwards_compat_model_dump_json_method() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.model_dump_json()) == {"foo": "hello"} + assert json.loads(m.model_dump_json(include={"bar"})) == {} + assert json.loads(m.model_dump_json(include={"foo"})) == {"foo": "hello"} + assert json.loads(m.model_dump_json(by_alias=True)) == {"FOO": "hello"} + + assert m.model_dump_json(indent=2) == '{\n "foo": "hello"\n}' + + m2 = Model() + assert json.loads(m2.model_dump_json()) == {"foo": None} + assert json.loads(m2.model_dump_json(exclude_unset=True)) == {} + assert json.loads(m2.model_dump_json(exclude_none=True)) == {} + assert json.loads(m2.model_dump_json(exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.model_dump_json()) == {"foo": None} + assert json.loads(m3.model_dump_json(exclude_none=True)) == {} + + if PYDANTIC_V1: + with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): + m.model_dump_json(round_trip=True) + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.model_dump_json(warnings=False) + + +def test_type_compat() -> None: + # our model type can be assigned to Pydantic's model type + + def takes_pydantic(model: pydantic.BaseModel) -> None: # noqa: ARG001 + ... + + class OurModel(BaseModel): + foo: Optional[str] = None + + takes_pydantic(OurModel()) + + +def test_annotated_types() -> None: + class Model(BaseModel): + value: str + + m = construct_type( + value={"value": "foo"}, + type_=cast(Any, Annotated[Model, "random metadata"]), + ) + assert isinstance(m, Model) + assert m.value == "foo" + + +def test_discriminated_unions_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, A) + assert m.type == "a" + if PYDANTIC_V1: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] + + +def test_discriminated_unions_unknown_variant() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "c", "data": None, "new_thing": "bar"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + + # just chooses the first variant + assert isinstance(m, A) + assert m.type == "c" # type: ignore[comparison-overlap] + assert m.data == None # type: ignore[unreachable] + assert m.new_thing == "bar" + + +def test_discriminated_unions_invalid_data_nested_unions() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + class C(BaseModel): + type: Literal["c"] + + data: bool + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "c", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, C) + assert m.type == "c" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_with_aliases_invalid_data() -> None: + class A(BaseModel): + foo_type: Literal["a"] = Field(alias="type") + + data: str + + class B(BaseModel): + foo_type: Literal["b"] = Field(alias="type") + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, B) + assert m.foo_type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, A) + assert m.foo_type == "a" + if PYDANTIC_V1: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] + + +def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["a"] + + data: int + + m = construct_type( + value={"type": "a", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "a" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_invalid_data_uses_cache() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + UnionType = cast(Any, Union[A, B]) + + assert not DISCRIMINATOR_CACHE.get(UnionType) + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + discriminator = DISCRIMINATOR_CACHE.get(UnionType) + assert discriminator is not None + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + # if the discriminator details object stays the same between invocations then + # we hit the cache + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator + + +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") +def test_type_alias_type() -> None: + Alias = TypeAliasType("Alias", str) # pyright: ignore + + class Model(BaseModel): + alias: Alias + union: Union[int, Alias] + + m = construct_type(value={"alias": "foo", "union": "bar"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.alias, str) + assert m.alias == "foo" + assert isinstance(m.union, str) + assert m.union == "bar" + + +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") +def test_field_named_cls() -> None: + class Model(BaseModel): + cls: str + + m = construct_type(value={"cls": "foo"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.cls, str) + + +def test_discriminated_union_case() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["b"] + + data: List[Union[A, object]] + + class ModelA(BaseModel): + type: Literal["modelA"] + + data: int + + class ModelB(BaseModel): + type: Literal["modelB"] + + required: str + + data: Union[A, B] + + # when constructing ModelA | ModelB, value data doesn't match ModelB exactly - missing `required` + m = construct_type( + value={"type": "modelB", "data": {"type": "a", "data": True}}, + type_=cast(Any, Annotated[Union[ModelA, ModelB], PropertyInfo(discriminator="type")]), + ) + + assert isinstance(m, ModelB) + + +def test_nested_discriminated_union() -> None: + class InnerType1(BaseModel): + type: Literal["type_1"] + + class InnerModel(BaseModel): + inner_value: str + + class InnerType2(BaseModel): + type: Literal["type_2"] + some_inner_model: InnerModel + + class Type1(BaseModel): + base_type: Literal["base_type_1"] + value: Annotated[ + Union[ + InnerType1, + InnerType2, + ], + PropertyInfo(discriminator="type"), + ] + + class Type2(BaseModel): + base_type: Literal["base_type_2"] + + T = Annotated[ + Union[ + Type1, + Type2, + ], + PropertyInfo(discriminator="base_type"), + ] + + model = construct_type( + type_=T, + value={ + "base_type": "base_type_1", + "value": { + "type": "type_2", + }, + }, + ) + assert isinstance(model, Type1) + assert isinstance(model.value, InnerType2) + + +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now") +def test_extra_properties() -> None: + class Item(BaseModel): + prop: int + + class Model(BaseModel): + __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + other: str + + if TYPE_CHECKING: + + def __getattr__(self, attr: str) -> Item: ... + + model = construct_type( + type_=Model, + value={ + "a": {"prop": 1}, + "other": "foo", + }, + ) + assert isinstance(model, Model) + assert model.a.prop == 1 + assert isinstance(model.a, Item) + assert model.other == "foo" diff --git a/tests/test_qs.py b/tests/test_qs.py new file mode 100644 index 00000000..32fb2091 --- /dev/null +++ b/tests/test_qs.py @@ -0,0 +1,78 @@ +from typing import Any, cast +from functools import partial +from urllib.parse import unquote + +import pytest + +from gradient._qs import Querystring, stringify + + +def test_empty() -> None: + assert stringify({}) == "" + assert stringify({"a": {}}) == "" + assert stringify({"a": {"b": {"c": {}}}}) == "" + + +def test_basic() -> None: + assert stringify({"a": 1}) == "a=1" + assert stringify({"a": "b"}) == "a=b" + assert stringify({"a": True}) == "a=true" + assert stringify({"a": False}) == "a=false" + assert stringify({"a": 1.23456}) == "a=1.23456" + assert stringify({"a": None}) == "" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_nested_dotted(method: str) -> None: + if method == "class": + serialise = Querystring(nested_format="dots").stringify + else: + serialise = partial(stringify, nested_format="dots") + + assert unquote(serialise({"a": {"b": "c"}})) == "a.b=c" + assert unquote(serialise({"a": {"b": "c", "d": "e", "f": "g"}})) == "a.b=c&a.d=e&a.f=g" + assert unquote(serialise({"a": {"b": {"c": {"d": "e"}}}})) == "a.b.c.d=e" + assert unquote(serialise({"a": {"b": True}})) == "a.b=true" + + +def test_nested_brackets() -> None: + assert unquote(stringify({"a": {"b": "c"}})) == "a[b]=c" + assert unquote(stringify({"a": {"b": "c", "d": "e", "f": "g"}})) == "a[b]=c&a[d]=e&a[f]=g" + assert unquote(stringify({"a": {"b": {"c": {"d": "e"}}}})) == "a[b][c][d]=e" + assert unquote(stringify({"a": {"b": True}})) == "a[b]=true" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_array_comma(method: str) -> None: + if method == "class": + serialise = Querystring(array_format="comma").stringify + else: + serialise = partial(stringify, array_format="comma") + + assert unquote(serialise({"in": ["foo", "bar"]})) == "in=foo,bar" + assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b]=true,false" + assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b]=true,false,true" + + +def test_array_repeat() -> None: + assert unquote(stringify({"in": ["foo", "bar"]})) == "in=foo&in=bar" + assert unquote(stringify({"a": {"b": [True, False]}})) == "a[b]=true&a[b]=false" + assert unquote(stringify({"a": {"b": [True, False, None, True]}})) == "a[b]=true&a[b]=false&a[b]=true" + assert unquote(stringify({"in": ["foo", {"b": {"c": ["d", "e"]}}]})) == "in=foo&in[b][c]=d&in[b][c]=e" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_array_brackets(method: str) -> None: + if method == "class": + serialise = Querystring(array_format="brackets").stringify + else: + serialise = partial(stringify, array_format="brackets") + + assert unquote(serialise({"in": ["foo", "bar"]})) == "in[]=foo&in[]=bar" + assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b][]=true&a[b][]=false" + assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b][]=true&a[b][]=false&a[b][]=true" + + +def test_unknown_array_format() -> None: + with pytest.raises(NotImplementedError, match="Unknown array_format value: foo, choose from comma, repeat"): + stringify({"a": ["foo", "bar"]}, array_format=cast(Any, "foo")) diff --git a/tests/test_required_args.py b/tests/test_required_args.py new file mode 100644 index 00000000..3956dc02 --- /dev/null +++ b/tests/test_required_args.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +import pytest + +from gradient._utils import required_args + + +def test_too_many_positional_params() -> None: + @required_args(["a"]) + def foo(a: str | None = None) -> str | None: + return a + + with pytest.raises(TypeError, match=r"foo\(\) takes 1 argument\(s\) but 2 were given"): + foo("a", "b") # type: ignore + + +def test_positional_param() -> None: + @required_args(["a"]) + def foo(a: str | None = None) -> str | None: + return a + + assert foo("a") == "a" + assert foo(None) is None + assert foo(a="b") == "b" + + with pytest.raises(TypeError, match="Missing required argument: 'a'"): + foo() + + +def test_keyword_only_param() -> None: + @required_args(["a"]) + def foo(*, a: str | None = None) -> str | None: + return a + + assert foo(a="a") == "a" + assert foo(a=None) is None + assert foo(a="b") == "b" + + with pytest.raises(TypeError, match="Missing required argument: 'a'"): + foo() + + +def test_multiple_params() -> None: + @required_args(["a", "b", "c"]) + def foo(a: str = "", *, b: str = "", c: str = "") -> str | None: + return f"{a} {b} {c}" + + assert foo(a="a", b="b", c="c") == "a b c" + + error_message = r"Missing required arguments.*" + + with pytest.raises(TypeError, match=error_message): + foo() + + with pytest.raises(TypeError, match=error_message): + foo(a="a") + + with pytest.raises(TypeError, match=error_message): + foo(b="b") + + with pytest.raises(TypeError, match=error_message): + foo(c="c") + + with pytest.raises(TypeError, match=r"Missing required argument: 'a'"): + foo(b="a", c="c") + + with pytest.raises(TypeError, match=r"Missing required argument: 'b'"): + foo("a", c="c") + + +def test_multiple_variants() -> None: + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: str | None = None) -> str | None: + return a if a is not None else b + + assert foo(a="foo") == "foo" + assert foo(b="bar") == "bar" + assert foo(a=None) is None + assert foo(b=None) is None + + # TODO: this error message could probably be improved + with pytest.raises( + TypeError, + match=r"Missing required arguments; Expected either \('a'\) or \('b'\) arguments to be given", + ): + foo() + + +def test_multiple_params_multiple_variants() -> None: + @required_args(["a", "b"], ["c"]) + def foo(*, a: str | None = None, b: str | None = None, c: str | None = None) -> str | None: + if a is not None: + return a + if b is not None: + return b + return c + + error_message = r"Missing required arguments; Expected either \('a' and 'b'\) or \('c'\) arguments to be given" + + with pytest.raises(TypeError, match=error_message): + foo(a="foo") + + with pytest.raises(TypeError, match=error_message): + foo(b="bar") + + with pytest.raises(TypeError, match=error_message): + foo() + + assert foo(a=None, b="bar") == "bar" + assert foo(c=None) is None + assert foo(c="foo") == "foo" diff --git a/tests/test_response.py b/tests/test_response.py new file mode 100644 index 00000000..6dd53185 --- /dev/null +++ b/tests/test_response.py @@ -0,0 +1,277 @@ +import json +from typing import Any, List, Union, cast +from typing_extensions import Annotated + +import httpx +import pytest +import pydantic + +from gradient import Gradient, BaseModel, AsyncGradient +from gradient._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + BinaryAPIResponse, + AsyncBinaryAPIResponse, + extract_response_type, +) +from gradient._streaming import Stream +from gradient._base_client import FinalRequestOptions + + +class ConcreteBaseAPIResponse(APIResponse[bytes]): ... + + +class ConcreteAPIResponse(APIResponse[List[str]]): ... + + +class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): ... + + +def test_extract_response_type_direct_classes() -> None: + assert extract_response_type(BaseAPIResponse[str]) == str + assert extract_response_type(APIResponse[str]) == str + assert extract_response_type(AsyncAPIResponse[str]) == str + + +def test_extract_response_type_direct_class_missing_type_arg() -> None: + with pytest.raises( + RuntimeError, + match="Expected type to have a type argument at index 0 but it did not", + ): + extract_response_type(AsyncAPIResponse) + + +def test_extract_response_type_concrete_subclasses() -> None: + assert extract_response_type(ConcreteBaseAPIResponse) == bytes + assert extract_response_type(ConcreteAPIResponse) == List[str] + assert extract_response_type(ConcreteAsyncAPIResponse) == httpx.Response + + +def test_extract_response_type_binary_response() -> None: + assert extract_response_type(BinaryAPIResponse) == bytes + assert extract_response_type(AsyncBinaryAPIResponse) == bytes + + +class PydanticModel(pydantic.BaseModel): ... + + +def test_response_parse_mismatched_basemodel(client: Gradient) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from gradient import BaseModel`", + ): + response.parse(to=PydanticModel) + + +@pytest.mark.asyncio +async def test_async_response_parse_mismatched_basemodel(async_client: AsyncGradient) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from gradient import BaseModel`", + ): + await response.parse(to=PydanticModel) + + +def test_response_parse_custom_stream(client: Gradient) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = response.parse(to=Stream[int]) + assert stream._cast_to == int + + +@pytest.mark.asyncio +async def test_async_response_parse_custom_stream(async_client: AsyncGradient) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=async_client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = await response.parse(to=Stream[int]) + assert stream._cast_to == int + + +class CustomModel(BaseModel): + foo: str + bar: int + + +def test_response_parse_custom_model(client: Gradient) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +@pytest.mark.asyncio +async def test_async_response_parse_custom_model(async_client: AsyncGradient) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +def test_response_parse_annotated_type(client: Gradient) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +async def test_async_response_parse_annotated_type(async_client: AsyncGradient) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +def test_response_parse_bool(client: Gradient, content: str, expected: bool) -> None: + response = APIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = response.parse(to=bool) + assert result is expected + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +async def test_async_response_parse_bool(client: AsyncGradient, content: str, expected: bool) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = await response.parse(to=bool) + assert result is expected + + +class OtherModel(BaseModel): + a: str + + +@pytest.mark.parametrize("client", [False], indirect=True) # loose validation +def test_response_parse_expect_model_union_non_json_content(client: Gradient) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation +async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncGradient) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" diff --git a/tests/test_streaming.py b/tests/test_streaming.py new file mode 100644 index 00000000..c4a8e46f --- /dev/null +++ b/tests/test_streaming.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +from typing import Iterator, AsyncIterator + +import httpx +import pytest + +from gradient import Gradient, AsyncGradient +from gradient._streaming import Stream, AsyncStream, ServerSentEvent + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_basic(sync: bool, client: Gradient, async_client: AsyncGradient) -> None: + def body() -> Iterator[bytes]: + yield b"event: completion\n" + yield b'data: {"foo":true}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_missing_event(sync: bool, client: Gradient, async_client: AsyncGradient) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"foo":true}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_event_missing_data(sync: bool, client: Gradient, async_client: AsyncGradient) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.data == "" + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events(sync: bool, client: Gradient, async_client: AsyncGradient) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + yield b"event: completion\n" + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.data == "" + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.data == "" + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events_with_data(sync: bool, client: Gradient, async_client: AsyncGradient) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo":true}\n' + yield b"\n" + yield b"event: completion\n" + yield b'data: {"bar":false}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.json() == {"bar": False} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines_with_empty_line(sync: bool, client: Gradient, async_client: AsyncGradient) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: \n" + yield b"data:\n" + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + assert sse.data == '{\n"foo":\n\n\ntrue}' + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_json_escaped_double_new_line(sync: bool, client: Gradient, async_client: AsyncGradient) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo": "my long\\n\\ncontent"}' + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": "my long\n\ncontent"} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines(sync: bool, client: Gradient, async_client: AsyncGradient) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_special_new_line_character( + sync: bool, + client: Gradient, + async_client: AsyncGradient, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":" culpa"}\n' + yield b"\n" + yield b'data: {"content":" \xe2\x80\xa8"}\n' + yield b"\n" + yield b'data: {"content":"foo"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " culpa"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " 
"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "foo"} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multi_byte_character_multiple_chunks( + sync: bool, + client: Gradient, + async_client: AsyncGradient, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":"' + # bytes taken from the string 'известни' and arbitrarily split + # so that some multi-byte characters span multiple chunks + yield b"\xd0" + yield b"\xb8\xd0\xb7\xd0" + yield b"\xb2\xd0\xb5\xd1\x81\xd1\x82\xd0\xbd\xd0\xb8" + yield b'"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "известни"} + + +async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]: + for chunk in iter: + yield chunk + + +async def iter_next(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> ServerSentEvent: + if isinstance(iter, AsyncIterator): + return await iter.__anext__() + + return next(iter) + + +async def assert_empty_iter(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> None: + with pytest.raises((StopAsyncIteration, RuntimeError)): + await iter_next(iter) + + +def make_event_iterator( + content: Iterator[bytes], + *, + sync: bool, + client: Gradient, + async_client: AsyncGradient, +) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]: + if sync: + return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events() + + return AsyncStream( + cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content)) + )._iter_events() diff --git a/tests/test_transform.py b/tests/test_transform.py new file mode 100644 index 00000000..098015a9 --- /dev/null +++ b/tests/test_transform.py @@ -0,0 +1,460 @@ +from __future__ import annotations + +import io +import pathlib +from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast +from datetime import date, datetime +from typing_extensions import Required, Annotated, TypedDict + +import pytest + +from gradient._types import Base64FileInput, omit, not_given +from gradient._utils import ( + PropertyInfo, + transform as _transform, + parse_datetime, + async_transform as _async_transform, +) +from gradient._compat import PYDANTIC_V1 +from gradient._models import BaseModel + +_T = TypeVar("_T") + +SAMPLE_FILE_PATH = pathlib.Path(__file__).parent.joinpath("sample_file.txt") + + +async def transform( + data: _T, + expected_type: object, + use_async: bool, +) -> _T: + if use_async: + return await _async_transform(data, expected_type=expected_type) + + return _transform(data, expected_type=expected_type) + + +parametrize = pytest.mark.parametrize("use_async", [False, True], ids=["sync", "async"]) + + +class Foo1(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +@parametrize +@pytest.mark.asyncio +async def test_top_level_alias(use_async: bool) -> None: + assert await transform({"foo_bar": "hello"}, expected_type=Foo1, use_async=use_async) == {"fooBar": "hello"} + + +class Foo2(TypedDict): + bar: Bar2 + + +class Bar2(TypedDict): + this_thing: Annotated[int, PropertyInfo(alias="this__thing")] + baz: Annotated[Baz2, PropertyInfo(alias="Baz")] + + +class Baz2(TypedDict): + my_baz: Annotated[str, PropertyInfo(alias="myBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_recursive_typeddict(use_async: bool) -> None: + assert await transform({"bar": {"this_thing": 1}}, Foo2, use_async) == {"bar": {"this__thing": 1}} + assert await transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2, use_async) == {"bar": {"Baz": {"myBaz": "foo"}}} + + +class Foo3(TypedDict): + things: List[Bar3] + + +class Bar3(TypedDict): + my_field: Annotated[str, PropertyInfo(alias="myField")] + + +@parametrize +@pytest.mark.asyncio +async def test_list_of_typeddict(use_async: bool) -> None: + result = await transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, Foo3, use_async) + assert result == {"things": [{"myField": "foo"}, {"myField": "foo2"}]} + + +class Foo4(TypedDict): + foo: Union[Bar4, Baz4] + + +class Bar4(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz4(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_union_of_typeddict(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo4, use_async) == {"foo": {"fooBar": "bar"}} + assert await transform({"foo": {"foo_baz": "baz"}}, Foo4, use_async) == {"foo": {"fooBaz": "baz"}} + assert await transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4, use_async) == { + "foo": {"fooBaz": "baz", "fooBar": "bar"} + } + + +class Foo5(TypedDict): + foo: Annotated[Union[Bar4, List[Baz4]], PropertyInfo(alias="FOO")] + + +class Bar5(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz5(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_union_of_list(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo5, use_async) == {"FOO": {"fooBar": "bar"}} + assert await transform( + { + "foo": [ + {"foo_baz": "baz"}, + {"foo_baz": "baz"}, + ] + }, + Foo5, + use_async, + ) == {"FOO": [{"fooBaz": "baz"}, {"fooBaz": "baz"}]} + + +class Foo6(TypedDict): + bar: Annotated[str, PropertyInfo(alias="Bar")] + + +@parametrize +@pytest.mark.asyncio +async def test_includes_unknown_keys(use_async: bool) -> None: + assert await transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6, use_async) == { + "Bar": "bar", + "baz_": {"FOO": 1}, + } + + +class Foo7(TypedDict): + bar: Annotated[List[Bar7], PropertyInfo(alias="bAr")] + foo: Bar7 + + +class Bar7(TypedDict): + foo: str + + +@parametrize +@pytest.mark.asyncio +async def test_ignores_invalid_input(use_async: bool) -> None: + assert await transform({"bar": ""}, Foo7, use_async) == {"bAr": ""} + assert await transform({"foo": ""}, Foo7, use_async) == {"foo": ""} + + +class DatetimeDict(TypedDict, total=False): + foo: Annotated[datetime, PropertyInfo(format="iso8601")] + + bar: Annotated[Optional[datetime], PropertyInfo(format="iso8601")] + + required: Required[Annotated[Optional[datetime], PropertyInfo(format="iso8601")]] + + list_: Required[Annotated[Optional[List[datetime]], PropertyInfo(format="iso8601")]] + + union: Annotated[Union[int, datetime], PropertyInfo(format="iso8601")] + + +class DateDict(TypedDict, total=False): + foo: Annotated[date, PropertyInfo(format="iso8601")] + + +class DatetimeModel(BaseModel): + foo: datetime + + +class DateModel(BaseModel): + foo: Optional[date] + + +@parametrize +@pytest.mark.asyncio +async def test_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + tz = "+00:00" if PYDANTIC_V1 else "Z" + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] + + dt = dt.replace(tzinfo=None) + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + + assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=None), Any, use_async) == {"foo": None} # type: ignore + assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=date.fromisoformat("2023-02-23")), DateDict, use_async) == { + "foo": "2023-02-23" + } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_optional_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"bar": dt}, DatetimeDict, use_async) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + + assert await transform({"bar": None}, DatetimeDict, use_async) == {"bar": None} + + +@parametrize +@pytest.mark.asyncio +async def test_required_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"required": dt}, DatetimeDict, use_async) == { + "required": "2023-02-23T14:16:36.337692+00:00" + } # type: ignore[comparison-overlap] + + assert await transform({"required": None}, DatetimeDict, use_async) == {"required": None} + + +@parametrize +@pytest.mark.asyncio +async def test_union_datetime(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"union": dt}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] + "union": "2023-02-23T14:16:36.337692+00:00" + } + + assert await transform({"union": "foo"}, DatetimeDict, use_async) == {"union": "foo"} + + +@parametrize +@pytest.mark.asyncio +async def test_nested_list_iso6801_format(use_async: bool) -> None: + dt1 = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + dt2 = parse_datetime("2022-01-15T06:34:23Z") + assert await transform({"list_": [dt1, dt2]}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] + "list_": ["2023-02-23T14:16:36.337692+00:00", "2022-01-15T06:34:23+00:00"] + } + + +@parametrize +@pytest.mark.asyncio +async def test_datetime_custom_format(use_async: bool) -> None: + dt = parse_datetime("2022-01-15T06:34:23Z") + + result = await transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], use_async) + assert result == "06" # type: ignore[comparison-overlap] + + +class DateDictWithRequiredAlias(TypedDict, total=False): + required_prop: Required[Annotated[date, PropertyInfo(format="iso8601", alias="prop")]] + + +@parametrize +@pytest.mark.asyncio +async def test_datetime_with_alias(use_async: bool) -> None: + assert await transform({"required_prop": None}, DateDictWithRequiredAlias, use_async) == {"prop": None} # type: ignore[comparison-overlap] + assert await transform( + {"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias, use_async + ) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] + + +class MyModel(BaseModel): + foo: str + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_model_to_dictionary(use_async: bool) -> None: + assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_empty_model(use_async: bool) -> None: + assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_unknown_field(use_async: bool) -> None: + assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == { + "my_untyped_field": True + } + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_types(use_async: bool) -> None: + model = MyModel.construct(foo=True) + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: + with pytest.warns(UserWarning): + params = await transform(model, Any, use_async) + assert cast(Any, params) == {"foo": True} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_object_type(use_async: bool) -> None: + model = MyModel.construct(foo=MyModel.construct(hello="world")) + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: + with pytest.warns(UserWarning): + params = await transform(model, Any, use_async) + assert cast(Any, params) == {"foo": {"hello": "world"}} + + +class ModelNestedObjects(BaseModel): + nested: MyModel + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_nested_objects(use_async: bool) -> None: + model = ModelNestedObjects.construct(nested={"foo": "stainless"}) + assert isinstance(model.nested, MyModel) + assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}} + + +class ModelWithDefaultField(BaseModel): + foo: str + with_none_default: Union[str, None] = None + with_str_default: str = "foo" + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_default_field(use_async: bool) -> None: + # should be excluded when defaults are used + model = ModelWithDefaultField.construct() + assert model.with_none_default is None + assert model.with_str_default == "foo" + assert cast(Any, await transform(model, Any, use_async)) == {} + + # should be included when the default value is explicitly given + model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") + assert model.with_none_default is None + assert model.with_str_default == "foo" + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"} + + # should be included when a non-default value is explicitly given + model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") + assert model.with_none_default == "bar" + assert model.with_str_default == "baz" + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} + + +class TypedDictIterableUnion(TypedDict): + foo: Annotated[Union[Bar8, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +class Bar8(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz8(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_iterable_of_dictionaries(use_async: bool) -> None: + assert await transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "bar"}] + } + assert cast(Any, await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async)) == { + "FOO": [{"fooBaz": "bar"}] + } + + def my_iter() -> Iterable[Baz8]: + yield {"foo_baz": "hello"} + yield {"foo_baz": "world"} + + assert await transform({"foo": my_iter()}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}] + } + + +@parametrize +@pytest.mark.asyncio +async def test_dictionary_items(use_async: bool) -> None: + class DictItems(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}} + + +class TypedDictIterableUnionStr(TypedDict): + foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +@parametrize +@pytest.mark.asyncio +async def test_iterable_union_str(use_async: bool) -> None: + assert await transform({"foo": "bar"}, TypedDictIterableUnionStr, use_async) == {"FOO": "bar"} + assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [ + {"fooBaz": "bar"} + ] + + +class TypedDictBase64Input(TypedDict): + foo: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] + + +@parametrize +@pytest.mark.asyncio +async def test_base64_file_input(use_async: bool) -> None: + # strings are left as-is + assert await transform({"foo": "bar"}, TypedDictBase64Input, use_async) == {"foo": "bar"} + + # pathlib.Path is automatically converted to base64 + assert await transform({"foo": SAMPLE_FILE_PATH}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQo=" + } # type: ignore[comparison-overlap] + + # io instances are automatically converted to base64 + assert await transform({"foo": io.StringIO("Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] + assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_transform_skipping(use_async: bool) -> None: + # lists of ints are left as-is + data = [1, 2, 3] + assert await transform(data, List[int], use_async) is data + + # iterables of ints are converted to a list + data = iter([1, 2, 3]) + assert await transform(data, Iterable[int], use_async) == [1, 2, 3] + + +@parametrize +@pytest.mark.asyncio +async def test_strips_notgiven(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_strips_omit(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": omit}, Foo1, use_async) == {} diff --git a/tests/test_utils/test_datetime_parse.py b/tests/test_utils/test_datetime_parse.py new file mode 100644 index 00000000..6cbb1b6f --- /dev/null +++ b/tests/test_utils/test_datetime_parse.py @@ -0,0 +1,110 @@ +""" +Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py +with modifications so it works without pydantic v1 imports. +""" + +from typing import Type, Union +from datetime import date, datetime, timezone, timedelta + +import pytest + +from gradient._utils import parse_date, parse_datetime + + +def create_tz(minutes: int) -> timezone: + return timezone(timedelta(minutes=minutes)) + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + ("1494012444.883309", date(2017, 5, 5)), + (b"1494012444.883309", date(2017, 5, 5)), + (1_494_012_444.883_309, date(2017, 5, 5)), + ("1494012444", date(2017, 5, 5)), + (1_494_012_444, date(2017, 5, 5)), + (0, date(1970, 1, 1)), + ("2012-04-23", date(2012, 4, 23)), + (b"2012-04-23", date(2012, 4, 23)), + ("2012-4-9", date(2012, 4, 9)), + (date(2012, 4, 9), date(2012, 4, 9)), + (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)), + # Invalid inputs + ("x20120423", ValueError), + ("2012-04-56", ValueError), + (19_999_999_999, date(2603, 10, 11)), # just before watershed + (20_000_000_001, date(1970, 8, 20)), # just after watershed + (1_549_316_052, date(2019, 2, 4)), # nowish in s + (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms + (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in μs + (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns + ("infinity", date(9999, 12, 31)), + ("inf", date(9999, 12, 31)), + (float("inf"), date(9999, 12, 31)), + ("infinity ", date(9999, 12, 31)), + (int("1" + "0" * 100), date(9999, 12, 31)), + (1e1000, date(9999, 12, 31)), + ("-infinity", date(1, 1, 1)), + ("-inf", date(1, 1, 1)), + ("nan", ValueError), + ], +) +def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_date(value) + else: + assert parse_date(value) == result + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + # values in seconds + ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + # values in ms + ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)), + ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)), + (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)), + ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)), + ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)), + ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))), + ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))), + ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))), + ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (datetime(2017, 5, 5), datetime(2017, 5, 5)), + (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)), + # Invalid inputs + ("x20120423091500", ValueError), + ("2012-04-56T09:15:90", ValueError), + ("2012-04-23T11:05:00-25:00", ValueError), + (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed + (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed + (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s + (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms + (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in μs + (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns + ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)), + (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)), + (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("-infinity", datetime(1, 1, 1, 0, 0)), + ("-inf", datetime(1, 1, 1, 0, 0)), + ("nan", ValueError), + ], +) +def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_datetime(value) + else: + assert parse_datetime(value) == result diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py new file mode 100644 index 00000000..af6d092a --- /dev/null +++ b/tests/test_utils/test_proxy.py @@ -0,0 +1,34 @@ +import operator +from typing import Any +from typing_extensions import override + +from gradient._utils import LazyProxy + + +class RecursiveLazyProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + return self + + def __call__(self, *_args: Any, **_kwds: Any) -> Any: + raise RuntimeError("This should never be called!") + + +def test_recursive_proxy() -> None: + proxy = RecursiveLazyProxy() + assert repr(proxy) == "RecursiveLazyProxy" + assert str(proxy) == "RecursiveLazyProxy" + assert dir(proxy) == [] + assert type(proxy).__name__ == "RecursiveLazyProxy" + assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" + + +def test_isinstance_does_not_error() -> None: + class AlwaysErrorProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + raise RuntimeError("Mocking missing dependency") + + proxy = AlwaysErrorProxy() + assert not isinstance(proxy, dict) + assert isinstance(proxy, LazyProxy) diff --git a/tests/test_utils/test_typing.py b/tests/test_utils/test_typing.py new file mode 100644 index 00000000..5f9711a2 --- /dev/null +++ b/tests/test_utils/test_typing.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from typing import Generic, TypeVar, cast + +from gradient._utils import extract_type_var_from_base + +_T = TypeVar("_T") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") + + +class BaseGeneric(Generic[_T]): ... + + +class SubclassGeneric(BaseGeneric[_T]): ... + + +class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): ... + + +class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): ... + + +class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): ... + + +def test_extract_type_var() -> None: + assert ( + extract_type_var_from_base( + BaseGeneric[int], + index=0, + generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), + ) + == int + ) + + +def test_extract_type_var_generic_subclass() -> None: + assert ( + extract_type_var_from_base( + SubclassGeneric[int], + index=0, + generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), + ) + == int + ) + + +def test_extract_type_var_multiple() -> None: + typ = BaseGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) + + +def test_extract_type_var_generic_subclass_multiple() -> None: + typ = SubclassGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) + + +def test_extract_type_var_generic_subclass_different_ordering_multiple() -> None: + typ = SubclassDifferentOrderGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 00000000..8d9112d6 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,167 @@ +from __future__ import annotations + +import os +import inspect +import traceback +import contextlib +from typing import Any, TypeVar, Iterator, Sequence, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, get_origin, assert_type + +from gradient._types import Omit, NoneType +from gradient._utils import ( + is_dict, + is_list, + is_list_type, + is_union_type, + extract_type_arg, + is_sequence_type, + is_annotated_type, + is_type_alias_type, +) +from gradient._compat import PYDANTIC_V1, field_outer_type, get_model_fields +from gradient._models import BaseModel + +BaseModelT = TypeVar("BaseModelT", bound=BaseModel) + + +def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: + for name, field in get_model_fields(model).items(): + field_value = getattr(value, name) + if PYDANTIC_V1: + # in v1 nullability was structured differently + # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields + allow_none = getattr(field, "allow_none", False) + else: + allow_none = False + + assert_matches_type( + field_outer_type(field), + field_value, + path=[*path, name], + allow_none=allow_none, + ) + + return True + + +# Note: the `path` argument is only used to improve error messages when `--showlocals` is used +def assert_matches_type( + type_: Any, + value: object, + *, + path: list[str], + allow_none: bool = False, +) -> None: + if is_type_alias_type(type_): + type_ = type_.__value__ + + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(type_): + type_ = extract_type_arg(type_, 0) + + if allow_none and value is None: + return + + if type_ is None or type_ is NoneType: + assert value is None + return + + origin = get_origin(type_) or type_ + + if is_list_type(type_): + return _assert_list_type(type_, value) + + if is_sequence_type(type_): + assert isinstance(value, Sequence) + inner_type = get_args(type_)[0] + for entry in value: # type: ignore + assert_type(inner_type, entry) # type: ignore + return + + if origin == str: + assert isinstance(value, str) + elif origin == int: + assert isinstance(value, int) + elif origin == bool: + assert isinstance(value, bool) + elif origin == float: + assert isinstance(value, float) + elif origin == bytes: + assert isinstance(value, bytes) + elif origin == datetime: + assert isinstance(value, datetime) + elif origin == date: + assert isinstance(value, date) + elif origin == object: + # nothing to do here, the expected type is unknown + pass + elif origin == Literal: + assert value in get_args(type_) + elif origin == dict: + assert is_dict(value) + + args = get_args(type_) + key_type = args[0] + items_type = args[1] + + for key, item in value.items(): + assert_matches_type(key_type, key, path=[*path, ""]) + assert_matches_type(items_type, item, path=[*path, ""]) + elif is_union_type(type_): + variants = get_args(type_) + + try: + none_index = variants.index(type(None)) + except ValueError: + pass + else: + # special case Optional[T] for better error messages + if len(variants) == 2: + if value is None: + # valid + return + + return assert_matches_type(type_=variants[not none_index], value=value, path=path) + + for i, variant in enumerate(variants): + try: + assert_matches_type(variant, value, path=[*path, f"variant {i}"]) + return + except AssertionError: + traceback.print_exc() + continue + + raise AssertionError("Did not match any variants") + elif issubclass(origin, BaseModel): + assert isinstance(value, type_) + assert assert_matches_model(type_, cast(Any, value), path=path) + elif inspect.isclass(origin) and origin.__name__ == "HttpxBinaryResponseContent": + assert value.__class__.__name__ == "HttpxBinaryResponseContent" + else: + assert None, f"Unhandled field type: {type_}" + + +def _assert_list_type(type_: type[object], value: object) -> None: + assert is_list(value) + + inner_type = get_args(type_)[0] + for entry in value: + assert_type(inner_type, entry) # type: ignore + + +@contextlib.contextmanager +def update_env(**new_env: str | Omit) -> Iterator[None]: + old = os.environ.copy() + + try: + for name, value in new_env.items(): + if isinstance(value, Omit): + os.environ.pop(name, None) + else: + os.environ[name] = value + + yield None + finally: + os.environ.clear() + os.environ.update(old)