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..cc4da81f --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "3.10.1" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 00000000..d321100e --- /dev/null +++ b/.stats.yml @@ -0,0 +1,4 @@ +configured_endpoints: 189 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/digitalocean%2Fgradient-0778b2e9d56c826f92ee69ef081d8d73fd94c139b85e11becaa88bf1cbe95fb9.yml +openapi_spec_hash: 49daca0dd735cad7200ca1c741a5dd43 +config_hash: fad48c8ac796b240fe3b90181586d1a4 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..330fe3fb --- /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 2026 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..972eb6bf 100644 --- a/README.md +++ b/README.md @@ -1 +1,498 @@ -# 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 os +import asyncio +from gradient import DefaultAioHttpClient +from gradient import AsyncGradient + + +async def main() -> None: + async with AsyncGradient( + model_access_key=os.environ.get( + "GRADIENT_MODEL_ACCESS_KEY" + ), # This is the default and can be omitted + 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..caf241a4 --- /dev/null +++ b/api.md @@ -0,0 +1,1041 @@ +# 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 + +# Retrieve + +Types: + +```python +from gradient.types import RetrieveDocumentsResponse +``` + +Methods: + +- client.retrieve.documents(knowledge_base_id, \*\*params) -> RetrieveDocumentsResponse 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..64a55024 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,269 @@ +[project] +name = "gradient" +version = "3.10.1" +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==1.17", + "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..63b7bd64 --- /dev/null +++ b/requirements-dev.lock @@ -0,0 +1,149 @@ +# 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.13.2 + # via gradient + # via httpx-aiohttp +aiosignal==1.4.0 + # via aiohttp +annotated-types==0.7.0 + # via pydantic +anyio==4.12.0 + # via gradient + # via httpx +argcomplete==3.6.3 + # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.4.0 + # via aiohttp + # via nox +backports-asyncio-runner==1.2.0 + # via pytest-asyncio +certifi==2025.11.12 + # via httpcore + # via httpx +colorlog==6.10.1 + # via nox +dependency-groups==1.3.1 + # via nox +dirty-equals==0.11 +distlib==0.4.0 + # via virtualenv +distro==1.9.0 + # via gradient +exceptiongroup==1.3.1 + # via anyio + # via pytest +execnet==2.1.2 + # via pytest-xdist +filelock==3.19.1 + # via virtualenv +frozenlist==1.8.0 + # 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 +humanize==4.13.0 + # via nox +idna==3.11 + # via anyio + # via httpx + # via yarl +importlib-metadata==8.7.0 +iniconfig==2.1.0 + # via pytest +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py +multidict==6.7.0 + # via aiohttp + # via yarl +mypy==1.17.0 +mypy-extensions==1.1.0 + # via mypy +nodeenv==1.9.1 + # via pyright +nox==2025.11.12 +packaging==25.0 + # via dependency-groups + # via nox + # via pytest +pathspec==0.12.1 + # via mypy +platformdirs==4.4.0 + # via virtualenv +pluggy==1.6.0 + # via pytest +propcache==0.4.1 + # via aiohttp + # via yarl +pydantic==2.12.5 + # via gradient +pydantic-core==2.41.5 + # via pydantic +pygments==2.19.2 + # via pytest + # via rich +pyright==1.1.399 +pytest==8.4.2 + # via pytest-asyncio + # via pytest-xdist +pytest-asyncio==1.2.0 +pytest-xdist==3.8.0 +python-dateutil==2.9.0.post0 + # via time-machine +respx==0.22.0 +rich==14.2.0 +ruff==0.14.7 +six==1.17.0 + # via python-dateutil +sniffio==1.3.1 + # via gradient +time-machine==2.19.0 +tomli==2.3.0 + # via dependency-groups + # via mypy + # via nox + # via pytest +typing-extensions==4.15.0 + # via aiosignal + # via anyio + # via exceptiongroup + # via gradient + # via multidict + # via mypy + # via pydantic + # via pydantic-core + # via pyright + # via pytest-asyncio + # via typing-inspection + # via virtualenv +typing-inspection==0.4.2 + # via pydantic +virtualenv==20.35.4 + # via nox +yarl==1.22.0 + # via aiohttp +zipp==3.23.0 + # via importlib-metadata diff --git a/requirements.lock b/requirements.lock new file mode 100644 index 00000000..b2623a7b --- /dev/null +++ b/requirements.lock @@ -0,0 +1,76 @@ +# 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.13.2 + # via gradient + # via httpx-aiohttp +aiosignal==1.4.0 + # via aiohttp +annotated-types==0.7.0 + # via pydantic +anyio==4.12.0 + # via gradient + # via httpx +async-timeout==5.0.1 + # via aiohttp +attrs==25.4.0 + # via aiohttp +certifi==2025.11.12 + # via httpcore + # via httpx +distro==1.9.0 + # via gradient +exceptiongroup==1.3.1 + # via anyio +frozenlist==1.8.0 + # 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.11 + # via anyio + # via httpx + # via yarl +multidict==6.7.0 + # via aiohttp + # via yarl +propcache==0.4.1 + # via aiohttp + # via yarl +pydantic==2.12.5 + # via gradient +pydantic-core==2.41.5 + # via pydantic +sniffio==1.3.1 + # via gradient +typing-extensions==4.15.0 + # via aiosignal + # via anyio + # via exceptiongroup + # via gradient + # via multidict + # via pydantic + # via pydantic-core + # via typing-inspection +typing-inspection==0.4.2 + # via pydantic +yarl==1.22.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..bc51411f --- /dev/null +++ b/scripts/lint @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [ "$1" = "--fix" ]; then + echo "==> Running lints with --fix" + rye run fix:ruff +else + echo "==> Running lints" + rye run lint +fi + +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..3a49a013 --- /dev/null +++ b/src/gradient/_base_client.py @@ -0,0 +1,2124 @@ +from __future__ import annotations + +import sys +import json +import time +import uuid +import email +import asyncio +import inspect +import logging +import platform +import warnings +import email.utils +from types import TracebackType +from random import random +from typing import ( + TYPE_CHECKING, + Any, + Dict, + 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, + BinaryTypes, + RequestFiles, + HttpxSendArgs, + RequestOptions, + AsyncBinaryTypes, + HttpxRequestFiles, + ModelBuilderProtocol, + not_given, +) +from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping +from ._compat import PYDANTIC_V1, model_copy, model_dump +from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type +from ._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, + # Pydantic v1 can't dump every type we support in content, so we exclude it for now. + exclude={ + "content", + } + if PYDANTIC_V1 + else {}, + ), + ) + kwargs: dict[str, Any] = {} + + json_data = options.json_data + if options.extra_json is not None: + if json_data is None: + json_data = cast(Body, options.extra_json) + elif is_mapping(json_data): + json_data = _merge_mappings(json_data, options.extra_json) + else: + raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") + + headers = self._build_headers(options, retries_taken=retries_taken) + params = _merge_mappings(self.default_query, options.params) + content_type = headers.get("Content-Type") + files = options.files + + # If the given Content-Type header is multipart/form-data then it + # has to be removed so that httpx can generate the header with + # additional information for us as it has to be in this form + # for the server to be able to correctly parse the request: + # multipart/form-data; boundary=---abc-- + if content_type is not None and content_type.startswith("multipart/form-data"): + if "boundary" not in content_type: + # only remove the header if the boundary hasn't been explicitly set + # as the caller doesn't want httpx to come up with their own boundary + headers.pop("Content-Type") + + # As we are now sending multipart/form-data instead of application/json + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding + if json_data: + if not is_dict(json_data): + raise TypeError( + f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." + ) + kwargs["data"] = self._serialize_multipartform(json_data) + + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + + prepared_url = self._prepare_url(options.url) + if "_" in prepared_url.host: + # work around https://github.com/encode/httpx/discussions/2880 + kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + + is_body_allowed = options.method.lower() != "get" + + if is_body_allowed: + if options.content is not None and json_data is not None: + raise TypeError("Passing both `content` and `json_data` is not supported") + if options.content is not None and files is not None: + raise TypeError("Passing both `content` and `files` is not supported") + if options.content is not None: + kwargs["content"] = options.content + elif isinstance(json_data, bytes): + kwargs["content"] = json_data + 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, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) + return self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[object], + page: Type[SyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> SyncPageT: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +class _DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + 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, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + async def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="patch", + url=path, + json_data=body, + content=content, + files=await async_to_httpx_files(files), + **options, + ) + return await self.request(cast_to, opts) + + async def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts) + + async def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) + return await self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[_T], + page: Type[AsyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> AsyncPaginator[_T, AsyncPageT]: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +def make_request_options( + *, + query: Query | None = None, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + idempotency_key: str | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, +) -> RequestOptions: + """Create a dict of type RequestOptions without keys of NotGiven values.""" + options: RequestOptions = {} + if extra_headers is not None: + options["headers"] = extra_headers + + if extra_body is not None: + options["extra_json"] = cast(AnyMapping, extra_body) + + if query is not None: + options["params"] = query + + if extra_query is not None: + options["params"] = {**options.get("params", {}), **extra_query} + + if not isinstance(timeout, NotGiven): + options["timeout"] = timeout + + if idempotency_key is not None: + options["idempotency_key"] = idempotency_key + + if is_given(post_parser): + # internal + options["post_parser"] = post_parser # type: ignore + + return options + + +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + +class OtherPlatform: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"Other:{self.name}" + + +Platform = Union[ + OtherPlatform, + Literal[ + "MacOS", + "Linux", + "Windows", + "FreeBSD", + "OpenBSD", + "iOS", + "Android", + "Unknown", + ], +] + + +def get_platform() -> Platform: + try: + system = platform.system().lower() + platform_name = platform.platform().lower() + except Exception: + return "Unknown" + + if "iphone" in platform_name or "ipad" in platform_name: + # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 + # system is Darwin and platform_name is a string like: + # - Darwin-21.6.0-iPhone12,1-64bit + # - Darwin-21.6.0-iPad7,11-64bit + return "iOS" + + if system == "darwin": + return "MacOS" + + if system == "windows": + return "Windows" + + if "android" in platform_name: + # Tested using Pydroid 3 + # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' + return "Android" + + if system == "linux": + # https://distro.readthedocs.io/en/latest/#distro.id + distro_id = distro.id() + if distro_id == "freebsd": + return "FreeBSD" + + if distro_id == "openbsd": + return "OpenBSD" + + return "Linux" + + if platform_name: + return OtherPlatform(platform_name) + + return "Unknown" + + +@lru_cache(maxsize=None) +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(platform or get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), + } + + +class OtherArch: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"other:{self.name}" + + +Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] + + +def get_python_runtime() -> str: + try: + return platform.python_implementation() + except Exception: + return "unknown" + + +def get_python_version() -> str: + try: + return platform.python_version() + except Exception: + return "unknown" + + +def get_architecture() -> Arch: + try: + machine = platform.machine().lower() + except Exception: + return "unknown" + + if machine in ("arm64", "aarch64"): + return "arm64" + + # TODO: untested + if machine == "arm": + return "arm" + + if machine == "x86_64": + return "x64" + + # TODO: untested + if sys.maxsize <= 2**32: + return "x32" + + if machine: + return OtherArch(machine) + + return "unknown" + + +def _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..a9bc97d4 --- /dev/null +++ b/src/gradient/_client.py @@ -0,0 +1,986 @@ +# 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, + retrieve, + 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.retrieve import RetrieveResource, AsyncRetrieveResource + 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 + kbass_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, + kbass_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` + - `kbass_endpoint` from `GRADIENT_KBASS_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 kbass_endpoint is None: + kbass_endpoint = os.environ.get("GRADIENT_KBASS_ENDPOINT") or "kbaas.do-ai.run" + self.kbass_endpoint = kbass_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 retrieve(self) -> RetrieveResource: + from .resources.retrieve import RetrieveResource + + return RetrieveResource(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 headers.get("Authorization") or 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, + kbass_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, + kbass_endpoint=kbass_endpoint or self.kbass_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 + kbass_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, + kbass_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` + - `kbass_endpoint` from `GRADIENT_KBASS_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 kbass_endpoint is None: + kbass_endpoint = os.environ.get("GRADIENT_KBASS_ENDPOINT") or "kbaas.do-ai.run" + self.kbass_endpoint = kbass_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 retrieve(self) -> AsyncRetrieveResource: + from .resources.retrieve import AsyncRetrieveResource + + return AsyncRetrieveResource(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 headers.get("Authorization") or 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, + kbass_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, + kbass_endpoint=kbass_endpoint or self.kbass_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) + + @cached_property + def retrieve(self) -> retrieve.RetrieveResourceWithRawResponse: + from .resources.retrieve import RetrieveResourceWithRawResponse + + return RetrieveResourceWithRawResponse(self._client.retrieve) + + +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) + + @cached_property + def retrieve(self) -> retrieve.AsyncRetrieveResourceWithRawResponse: + from .resources.retrieve import AsyncRetrieveResourceWithRawResponse + + return AsyncRetrieveResourceWithRawResponse(self._client.retrieve) + + +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) + + @cached_property + def retrieve(self) -> retrieve.RetrieveResourceWithStreamingResponse: + from .resources.retrieve import RetrieveResourceWithStreamingResponse + + return RetrieveResourceWithStreamingResponse(self._client.retrieve) + + +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) + + @cached_property + def retrieve(self) -> retrieve.AsyncRetrieveResourceWithStreamingResponse: + from .resources.retrieve import AsyncRetrieveResourceWithStreamingResponse + + return AsyncRetrieveResourceWithStreamingResponse(self._client.retrieve) + + +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..29070e05 --- /dev/null +++ b/src/gradient/_models.py @@ -0,0 +1,872 @@ +from __future__ import annotations + +import os +import inspect +import weakref +from typing import ( + IO, + TYPE_CHECKING, + Any, + Type, + Union, + Generic, + TypeVar, + Callable, + Iterable, + Optional, + AsyncIterable, + cast, +) +from datetime import date, datetime +from typing_extensions import ( + List, + Unpack, + Literal, + ClassVar, + Protocol, + Required, + 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 + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] + json_data: Body + extra_json: AnyMapping + follow_redirects: bool + + +@final +class FinalRequestOptions(pydantic.BaseModel): + method: str + url: str + params: Query = {} + headers: Union[Headers, NotGiven] = NotGiven() + max_retries: Union[int, NotGiven] = NotGiven() + timeout: Union[float, Timeout, None, NotGiven] = NotGiven() + files: Union[HttpxRequestFiles, None] = None + idempotency_key: Union[str, None] = None + post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + follow_redirects: Union[bool, None] = None + + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None + # It should be noted that we cannot use `json` here as that would override + # a BaseModel method in an incompatible fashion. + json_data: Union[Body, None] = None + extra_json: Union[AnyMapping, None] = None + + if PYDANTIC_V1: + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) + + def get_max_retries(self, max_retries: int) -> int: + if isinstance(self.max_retries, NotGiven): + return max_retries + return self.max_retries + + def _strip_raw_response_header(self) -> None: + if not is_given(self.headers): + return + + if self.headers.get(RAW_RESPONSE_HEADER): + self.headers = {**self.headers} + self.headers.pop(RAW_RESPONSE_HEADER) + + # override the `construct` method so that we can run custom transformations. + # this is necessary as we don't want to do any actual runtime type checking + # (which means we can't use validators) but we do want to ensure that `NotGiven` + # values are not present + # + # type ignore required because we're adding explicit types to `**values` + @classmethod + def construct( # type: ignore + cls, + _fields_set: set[str] | None = None, + **values: Unpack[FinalRequestOptionsInput], + ) -> FinalRequestOptions: + kwargs: dict[str, Any] = { + # we unconditionally call `strip_not_given` on any value + # as it will just ignore any non-mapping types + key: strip_not_given(value) + for key, value in values.items() + } + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + model_construct = construct 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..f0516264 --- /dev/null +++ b/src/gradient/_streaming.py @@ -0,0 +1,370 @@ +# 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() + + try: + 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) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.response.close() + + +class 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() + + try: + 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) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.response.aclose() + + +class ServerSentEvent: + def __init__( + self, + *, + event: str | None = None, + data: str | None = None, + id: str | None = None, + retry: int | None = None, + ) -> 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..338a463d --- /dev/null +++ b/src/gradient/_types.py @@ -0,0 +1,270 @@ +from __future__ import annotations + +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + TypeVar, + Callable, + Iterable, + Iterator, + Optional, + Sequence, + AsyncIterable, +) +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) + +import httpx +import pydantic +from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport + +if TYPE_CHECKING: + from ._models import BaseModel + from ._response import APIResponse, AsyncAPIResponse + +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. + + +# Used for sending raw binary data / streaming data in request bodies +# e.g. for file uploads without multipart encoding +BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] +AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] + +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +# duplicate of the above but without our custom file support +HttpxFileContent = Union[IO[bytes], bytes] +HttpxFileTypes = Union[ + # file (or bytes) + HttpxFileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], HttpxFileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], HttpxFileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], +] +HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] + +# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT +# where ResponseT includes `None`. In order to support directly +# passing `None`, overloads would have to be defined for every +# method that uses `ResponseT` which would lead to an unacceptable +# amount of code duplication and make it unreadable. See _base_client.py +# for example usage. +# +# This unfortunately means that you will either have +# to import this type and pass it explicitly: +# +# from 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 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence 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..f1eb0021 --- /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.10.1" # 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..f668bb06 --- /dev/null +++ b/src/gradient/resources/__init__.py @@ -0,0 +1,159 @@ +# 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 .retrieve import ( + RetrieveResource, + AsyncRetrieveResource, + RetrieveResourceWithRawResponse, + AsyncRetrieveResourceWithRawResponse, + RetrieveResourceWithStreamingResponse, + AsyncRetrieveResourceWithStreamingResponse, +) +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", + "RetrieveResource", + "AsyncRetrieveResource", + "RetrieveResourceWithRawResponse", + "AsyncRetrieveResourceWithRawResponse", + "RetrieveResourceWithStreamingResponse", + "AsyncRetrieveResourceWithStreamingResponse", +] 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..8268e066 --- /dev/null +++ b/src/gradient/resources/agents/chat/completions.py @@ -0,0 +1,1004 @@ +# 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", + 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", + 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..63da2a28 --- /dev/null +++ b/src/gradient/resources/chat/completions.py @@ -0,0 +1,1004 @@ +# 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", + 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", + 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..763dc1cb --- /dev/null +++ b/src/gradient/resources/images.py @@ -0,0 +1,682 @@ +# 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", + 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", + 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..a46df265 --- /dev/null +++ b/src/gradient/resources/nfs/nfs.py @@ -0,0 +1,960 @@ +# 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 | + | `attach` | Attaches an NFS share to a VPC. Set the vpc_id attribute to the desired VPC ID | + | `detach` | Detaches an NFS share from a VPC. Set the vpc_id attribute to the desired VPC ID | + + 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 | + | `attach` | Attaches an NFS share to a VPC. Set the vpc_id attribute to the desired VPC ID | + | `detach` | Detaches an NFS share from a VPC. Set the vpc_id attribute to the desired VPC ID | + + 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.NfsActionAttachParams | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this 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 | + | `attach` | Attaches an NFS share to a VPC. Set the vpc_id attribute to the desired VPC ID | + | `detach` | Detaches an NFS share from a VPC. Set the vpc_id attribute to the desired VPC ID | + + 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.NfsActionDetachParams | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this 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 | + | `attach` | Attaches an NFS share to a VPC. Set the vpc_id attribute to the desired VPC ID | + | `detach` | Detaches an NFS share from a VPC. Set the vpc_id attribute to the desired VPC ID | + + 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 + | nf_initiate_action_params.NfsActionAttachParams + | nf_initiate_action_params.NfsActionDetachParams + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this 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 | + | `attach` | Attaches an NFS share to a VPC. Set the vpc_id attribute to the desired VPC ID | + | `detach` | Detaches an NFS share from a VPC. Set the vpc_id attribute to the desired VPC ID | + + 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 | + | `attach` | Attaches an NFS share to a VPC. Set the vpc_id attribute to the desired VPC ID | + | `detach` | Detaches an NFS share from a VPC. Set the vpc_id attribute to the desired VPC ID | + + 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.NfsActionAttachParams | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this 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 | + | `attach` | Attaches an NFS share to a VPC. Set the vpc_id attribute to the desired VPC ID | + | `detach` | Detaches an NFS share from a VPC. Set the vpc_id attribute to the desired VPC ID | + + 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.NfsActionDetachParams | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this 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 | + | `attach` | Attaches an NFS share to a VPC. Set the vpc_id attribute to the desired VPC ID | + | `detach` | Detaches an NFS share from a VPC. Set the vpc_id attribute to the desired VPC ID | + + 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 + | nf_initiate_action_params.NfsActionAttachParams + | nf_initiate_action_params.NfsActionDetachParams + | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this 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/resources/retrieve.py b/src/gradient/resources/retrieve.py new file mode 100644 index 00000000..9932c9da --- /dev/null +++ b/src/gradient/resources/retrieve.py @@ -0,0 +1,237 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import retrieve_documents_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.retrieve_documents_response import RetrieveDocumentsResponse + +__all__ = ["RetrieveResource", "AsyncRetrieveResource"] + + +class RetrieveResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RetrieveResourceWithRawResponse: + """ + 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 RetrieveResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RetrieveResourceWithStreamingResponse: + """ + 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 RetrieveResourceWithStreamingResponse(self) + + def documents( + self, + knowledge_base_id: str, + *, + num_results: int, + query: str, + alpha: float | Omit = omit, + filters: retrieve_documents_params.Filters | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RetrieveDocumentsResponse: + """ + Retrieve relevant documents from a knowledge base using semantic search. + + This endpoint: + + 1. Authenticates the request using the provided bearer token + 2. Generates embeddings for the query using the knowledge base's configured + model + 3. Performs vector similarity search in the knowledge base + 4. Returns the most relevant document chunks + + Args: + num_results: Number of results to return + + query: The search query text + + alpha: + Weight for hybrid search (0-1): + + - 0 = pure keyword search (BM25) + - 1 = pure vector search (default) + - 0.5 = balanced hybrid search + + filters: Metadata filters to apply to the search + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the 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_id: + raise ValueError(f"Expected a non-empty value for `knowledge_base_id` but received {knowledge_base_id!r}") + return self._post( + f"/{knowledge_base_id}/retrieve" + if self._client._base_url_overridden + else f"https://kbaas.do-ai.run/v1/{knowledge_base_id}/retrieve", + body=maybe_transform( + { + "num_results": num_results, + "query": query, + "alpha": alpha, + "filters": filters, + }, + retrieve_documents_params.RetrieveDocumentsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RetrieveDocumentsResponse, + ) + + +class AsyncRetrieveResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRetrieveResourceWithRawResponse: + """ + 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 AsyncRetrieveResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRetrieveResourceWithStreamingResponse: + """ + 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 AsyncRetrieveResourceWithStreamingResponse(self) + + async def documents( + self, + knowledge_base_id: str, + *, + num_results: int, + query: str, + alpha: float | Omit = omit, + filters: retrieve_documents_params.Filters | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RetrieveDocumentsResponse: + """ + Retrieve relevant documents from a knowledge base using semantic search. + + This endpoint: + + 1. Authenticates the request using the provided bearer token + 2. Generates embeddings for the query using the knowledge base's configured + model + 3. Performs vector similarity search in the knowledge base + 4. Returns the most relevant document chunks + + Args: + num_results: Number of results to return + + query: The search query text + + alpha: + Weight for hybrid search (0-1): + + - 0 = pure keyword search (BM25) + - 1 = pure vector search (default) + - 0.5 = balanced hybrid search + + filters: Metadata filters to apply to the search + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the 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_id: + raise ValueError(f"Expected a non-empty value for `knowledge_base_id` but received {knowledge_base_id!r}") + return await self._post( + f"/{knowledge_base_id}/retrieve" + if self._client._base_url_overridden + else f"https://kbaas.do-ai.run/v1/{knowledge_base_id}/retrieve", + body=await async_maybe_transform( + { + "num_results": num_results, + "query": query, + "alpha": alpha, + "filters": filters, + }, + retrieve_documents_params.RetrieveDocumentsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RetrieveDocumentsResponse, + ) + + +class RetrieveResourceWithRawResponse: + def __init__(self, retrieve: RetrieveResource) -> None: + self._retrieve = retrieve + + self.documents = to_raw_response_wrapper( + retrieve.documents, + ) + + +class AsyncRetrieveResourceWithRawResponse: + def __init__(self, retrieve: AsyncRetrieveResource) -> None: + self._retrieve = retrieve + + self.documents = async_to_raw_response_wrapper( + retrieve.documents, + ) + + +class RetrieveResourceWithStreamingResponse: + def __init__(self, retrieve: RetrieveResource) -> None: + self._retrieve = retrieve + + self.documents = to_streamed_response_wrapper( + retrieve.documents, + ) + + +class AsyncRetrieveResourceWithStreamingResponse: + def __init__(self, retrieve: AsyncRetrieveResource) -> None: + self._retrieve = retrieve + + self.documents = async_to_streamed_response_wrapper( + retrieve.documents, + ) diff --git a/src/gradient/types/__init__.py b/src/gradient/types/__init__.py new file mode 100644 index 00000000..fb930d8d --- /dev/null +++ b/src/gradient/types/__init__.py @@ -0,0 +1,176 @@ +# 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 .retrieve_documents_params import RetrieveDocumentsParams as RetrieveDocumentsParams +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 .retrieve_documents_response import RetrieveDocumentsResponse as RetrieveDocumentsResponse +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..a9138a04 --- /dev/null +++ b/src/gradient/types/agent_create_response.py @@ -0,0 +1,19 @@ +# 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): + """Information about a newly created Agent""" + + 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..c16ea9fc --- /dev/null +++ b/src/gradient/types/agent_delete_response.py @@ -0,0 +1,19 @@ +# 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): + """Info about a deleted agent""" + + 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..a3b5cf6c --- /dev/null +++ b/src/gradient/types/agent_list_response.py @@ -0,0 +1,288 @@ +# 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): + """A Chatbot""" + + 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""" + + agent_chatbot_identifier: Optional[str] = None + """Agent chatbot identifier""" + + +class AgentDeployment(BaseModel): + """Description of deployment""" + + 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): + """Represents an AgentTemplate entity""" + + 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): + """A GenAI Agent's configuration""" + + 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): + """List of Agents""" + + 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..c8b25e0b --- /dev/null +++ b/src/gradient/types/agent_retrieve_response.py @@ -0,0 +1,19 @@ +# 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): + """One Agent""" + + 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..f4622ec8 --- /dev/null +++ b/src/gradient/types/agent_retrieve_usage_response.py @@ -0,0 +1,58 @@ +# 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): + """Usage Measurement Description""" + + tokens: Optional[int] = None + + usage_type: Optional[str] = None + + +class LogInsightsUsage(BaseModel): + """Resource Usage Description""" + + measurements: Optional[List[LogInsightsUsageMeasurement]] = None + + resource_uuid: Optional[str] = None + + start: Optional[datetime] = None + + stop: Optional[datetime] = None + + +class UsageMeasurement(BaseModel): + """Usage Measurement Description""" + + tokens: Optional[int] = None + + usage_type: Optional[str] = None + + +class Usage(BaseModel): + """Resource Usage Description""" + + measurements: Optional[List[UsageMeasurement]] = None + + resource_uuid: Optional[str] = None + + start: Optional[datetime] = None + + stop: Optional[datetime] = None + + +class AgentRetrieveUsageResponse(BaseModel): + """Agent usage""" + + 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..fb232225 --- /dev/null +++ b/src/gradient/types/agent_update_response.py @@ -0,0 +1,19 @@ +# 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): + """Information about an updated agent""" + + 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..a562915e --- /dev/null +++ b/src/gradient/types/agent_update_status_response.py @@ -0,0 +1,19 @@ +# 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): + """UpdateAgentDeploymentVisbilityOutput description""" + + 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..84c3ea0a --- /dev/null +++ b/src/gradient/types/agents/api_evaluation_metric.py @@ -0,0 +1,53 @@ +# 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): + category: Optional[ + Literal[ + "METRIC_CATEGORY_UNSPECIFIED", + "METRIC_CATEGORY_CORRECTNESS", + "METRIC_CATEGORY_USER_OUTCOMES", + "METRIC_CATEGORY_SAFETY_AND_SECURITY", + "METRIC_CATEGORY_CONTEXT_QUALITY", + "METRIC_CATEGORY_MODEL_FIT", + ] + ] = None + + description: Optional[str] = None + + inverted: Optional[bool] = None + """If true, the metric is inverted, meaning that a lower value is better.""" + + is_metric_goal: Optional[bool] = None + + metric_name: Optional[str] = None + + metric_rank: Optional[int] = 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..d59f2677 --- /dev/null +++ b/src/gradient/types/agents/api_link_knowledge_base_output.py @@ -0,0 +1,19 @@ +# 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): + """Information about a linked knowledge base""" + + 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..d238f8e1 --- /dev/null +++ b/src/gradient/types/agents/chat/completion_create_params.py @@ -0,0 +1,409 @@ +# 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): + """Content part with type and text""" + + 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): + """ + System-provided instructions that the model should follow, regardless of + messages sent by the user. + """ + + 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): + """Content part with type and text""" + + 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): + """ + Developer-provided instructions that the model should follow, regardless of + messages sent by the user. + """ + + 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): + """Content part with type and text""" + + 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): + """ + Messages sent by an end user, containing prompts or additional context + information. + """ + + 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): + """Content part with type and text""" + + 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): + """The function that the model called.""" + + 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): + """Messages sent by the model in response to user messages.""" + + 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): + """Options for streaming response. Only set this when you set `stream: true`.""" + + 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): + """Specifies a tool the model should use. + + Use to force the model to call a specific function. + """ + + 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..88c64763 --- /dev/null +++ b/src/gradient/types/agents/chat/completion_create_response.py @@ -0,0 +1,118 @@ +# 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): + """Log probability information for the choice.""" + + 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): + """The function that the model called.""" + + 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): + """A chat completion message generated by the model.""" + + 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): + """ + Represents a chat completion response returned by model, based on the provided input. + """ + + 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..9412b46c --- /dev/null +++ b/src/gradient/types/agents/evaluation_dataset_create_file_upload_presigned_urls_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 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): + """A single file’s metadata in the request.""" + + 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..3648a9ed --- /dev/null +++ b/src/gradient/types/agents/evaluation_dataset_create_file_upload_presigned_urls_response.py @@ -0,0 +1,34 @@ +# 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): + """Detailed info about each presigned URL returned to the client.""" + + 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): + """Response with pre-signed urls to upload files.""" + + 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..4e5f8c9b --- /dev/null +++ b/src/gradient/types/agents/evaluation_dataset_create_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__ = ["EvaluationDatasetCreateResponse"] + + +class EvaluationDatasetCreateResponse(BaseModel): + """Output for creating an agent evaluation dataset""" + + 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..dc07a7ef --- /dev/null +++ b/src/gradient/types/agents/evaluation_metric_list_regions_response.py @@ -0,0 +1,33 @@ +# 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): + """Description for a specific Region""" + + 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): + """Region Codes""" + + 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..34babe47 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_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 ....._models import BaseModel +from ....api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["KeyCreateResponse"] + + +class KeyCreateResponse(BaseModel): + """ + CreateAnthropicAPIKeyOutput is used to return the newly created Anthropic API key. + """ + + 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..c2796b36 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_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 +from ....api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["KeyDeleteResponse"] + + +class KeyDeleteResponse(BaseModel): + """DeleteAnthropicAPIKeyOutput is used to return the deleted Anthropic API key.""" + + 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..34ab7508 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_agents_response.py @@ -0,0 +1,26 @@ +# 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): + """List of Agents that linked to a specific Anthropic Key""" + + 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..21729e57 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_list_response.py @@ -0,0 +1,25 @@ +# 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): + """ + ListAnthropicAPIKeysOutput is used to return the list of Anthropic API keys for a specific agent. + """ + + 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..04d20e9b --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/anthropic/key_update_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 +from ....api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["KeyUpdateResponse"] + + +class KeyUpdateResponse(BaseModel): + """UpdateAnthropicAPIKeyOutput is used to return the updated Anthropic API key.""" + + 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..6277059b --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/oauth2/dropbox_create_tokens_response.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__ = ["DropboxCreateTokensResponse"] + + +class DropboxCreateTokensResponse(BaseModel): + """The dropbox oauth2 token and refresh token""" + + 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..f1e782c4 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/oauth2_generate_url_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__ = ["Oauth2GenerateURLResponse"] + + +class Oauth2GenerateURLResponse(BaseModel): + """The url for the oauth2 flow""" + + 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..f6254e1c --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_create_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 +from ....api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["KeyCreateResponse"] + + +class KeyCreateResponse(BaseModel): + """CreateOpenAIAPIKeyOutput is used to return the newly created OpenAI API key.""" + + 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..1ac937f4 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_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 +from ....api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["KeyDeleteResponse"] + + +class KeyDeleteResponse(BaseModel): + """DeleteOpenAIAPIKeyOutput is used to return the deleted OpenAI API key.""" + + 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..fa2ba7cc --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_list_agents_response.py @@ -0,0 +1,26 @@ +# 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): + """List of Agents that are linked to a specific OpenAI Key""" + + 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..f335cfc9 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_list_response.py @@ -0,0 +1,25 @@ +# 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): + """ + ListOpenAIAPIKeysOutput is used to return the list of OpenAI API keys for a specific agent. + """ + + 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..28b56926 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/openai/key_update_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 +from ....api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["KeyUpdateResponse"] + + +class KeyUpdateResponse(BaseModel): + """UpdateOpenAIAPIKeyOutput is used to return the updated OpenAI API key.""" + + 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..78cb1e73 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_create_response.py @@ -0,0 +1,50 @@ +# 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): + """Metadata for scheduled indexing entries""" + + 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..b359cb18 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_delete_response.py @@ -0,0 +1,50 @@ +# 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): + """Metadata for scheduled indexing entries""" + + 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..4d3840a3 --- /dev/null +++ b/src/gradient/types/agents/evaluation_metrics/scheduled_indexing_retrieve_response.py @@ -0,0 +1,50 @@ +# 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): + """Metadata for scheduled indexing entries""" + + 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..e06bac94 --- /dev/null +++ b/src/gradient/types/agents/evaluation_run_list_results_response.py @@ -0,0 +1,26 @@ +# 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): + """Gets the full results of an evaluation run with all prompts.""" + + 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..335ebac0 --- /dev/null +++ b/src/gradient/types/agents/function_create_response.py @@ -0,0 +1,19 @@ +# 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): + """Information about a newly function linked agent""" + + 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..7490d34d --- /dev/null +++ b/src/gradient/types/agents/function_delete_response.py @@ -0,0 +1,19 @@ +# 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): + """Information about a newly unlinked agent""" + + 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..72399e92 --- /dev/null +++ b/src/gradient/types/agents/function_update_response.py @@ -0,0 +1,19 @@ +# 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): + """The updated agent""" + + 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..c94b99a1 --- /dev/null +++ b/src/gradient/types/agents/knowledge_base_detach_response.py @@ -0,0 +1,19 @@ +# 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): + """Informatinon about a unlinked knowledge base""" + + 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..b1755d54 --- /dev/null +++ b/src/gradient/types/agents/route_add_response.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__ = ["RouteAddResponse"] + + +class RouteAddResponse(BaseModel): + """Information about a newly linked agent""" + + 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..6dcc03b6 --- /dev/null +++ b/src/gradient/types/agents/route_delete_response.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__ = ["RouteDeleteResponse"] + + +class RouteDeleteResponse(BaseModel): + """Information about a removed linkage""" + + 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..dfcec469 --- /dev/null +++ b/src/gradient/types/agents/route_update_response.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__ = ["RouteUpdateResponse"] + + +class RouteUpdateResponse(BaseModel): + """Information about an updated linkage""" + + 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..ddbf6f33 --- /dev/null +++ b/src/gradient/types/agents/route_view_response.py @@ -0,0 +1,19 @@ +# 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): + """Child list for an agent""" + + 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..75c45a95 --- /dev/null +++ b/src/gradient/types/agents/version_list_response.py @@ -0,0 +1,175 @@ +# 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): + """Function represents a function configuration for an agent""" + + 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): + """Agent Guardrail version""" + + 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): + """Represents an AgentVersion entity""" + + 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): + """List of agent versions""" + + 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..ee1188d1 --- /dev/null +++ b/src/gradient/types/agents/version_update_response.py @@ -0,0 +1,33 @@ +# 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): + """An alternative way to provide auth information. for internal use only.""" + + 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..e3fb21f2 --- /dev/null +++ b/src/gradient/types/api_agent.py @@ -0,0 +1,434 @@ +# 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): + """Agent API Key""" + + api_key: Optional[str] = None + """Api key""" + + +class Chatbot(BaseModel): + """A Chatbot""" + + 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""" + + agent_chatbot_identifier: Optional[str] = None + """Agent chatbot identifier""" + + +class Deployment(BaseModel): + """Description of deployment""" + + 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): + """Description missing""" + + 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): + """A Agent Guardrail""" + + 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): + """Represents an AgentTemplate entity""" + + 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): + """An Agent""" + + 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..06bd0fda --- /dev/null +++ b/src/gradient/types/api_agent_api_key_info.py @@ -0,0 +1,29 @@ +# 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): + """Agent API Key Info""" + + 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..e42bb5d5 --- /dev/null +++ b/src/gradient/types/api_agent_model.py @@ -0,0 +1,73 @@ +# 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): + """Description of a Model""" + + 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..8eca3c3c --- /dev/null +++ b/src/gradient/types/api_agreement.py @@ -0,0 +1,19 @@ +# 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): + """Agreement Description""" + + 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..bf13fd60 --- /dev/null +++ b/src/gradient/types/api_anthropic_api_key_info.py @@ -0,0 +1,30 @@ +# 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): + """Anthropic API Key Info""" + + 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..e64f9336 --- /dev/null +++ b/src/gradient/types/api_knowledge_base.py @@ -0,0 +1,49 @@ +# 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): + """Knowledgebase Description""" + + 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..83b1b66a --- /dev/null +++ b/src/gradient/types/api_model.py @@ -0,0 +1,47 @@ +# 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): + """A machine learning model stored on the GenAI platform""" + + 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..3989e256 --- /dev/null +++ b/src/gradient/types/api_model_version.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__ = ["APIModelVersion"] + + +class APIModelVersion(BaseModel): + """Version Information about a Model""" + + 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..69e9b138 --- /dev/null +++ b/src/gradient/types/api_openai_api_key_info.py @@ -0,0 +1,34 @@ +# 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): + """OpenAI API Key Info""" + + 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..e889c5e8 --- /dev/null +++ b/src/gradient/types/chat/completion_create_params.py @@ -0,0 +1,409 @@ +# 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): + """Content part with type and text""" + + 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): + """ + System-provided instructions that the model should follow, regardless of + messages sent by the user. + """ + + 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): + """Content part with type and text""" + + 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): + """ + Developer-provided instructions that the model should follow, regardless of + messages sent by the user. + """ + + 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): + """Content part with type and text""" + + 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): + """ + Messages sent by an end user, containing prompts or additional context + information. + """ + + 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): + """Content part with type and text""" + + 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): + """The function that the model called.""" + + 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): + """Messages sent by the model in response to user messages.""" + + 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): + """Options for streaming response. Only set this when you set `stream: true`.""" + + 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): + """Specifies a tool the model should use. + + Use to force the model to call a specific function. + """ + + 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..13efee40 --- /dev/null +++ b/src/gradient/types/chat/completion_create_response.py @@ -0,0 +1,118 @@ +# 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): + """Log probability information for the choice.""" + + 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): + """The function that the model called.""" + + 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): + """A chat completion message generated by the model.""" + + 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): + """ + Represents a chat completion response returned by model, based on the provided input. + """ + + 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..500c69e2 --- /dev/null +++ b/src/gradient/types/gpu_droplets/associated_resource.py @@ -0,0 +1,23 @@ +# 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): + """An objects containing information about a resource associated with a Droplet.""" + + 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..8dd32c14 --- /dev/null +++ b/src/gradient/types/gpu_droplets/destroy_with_associated_resource_check_status_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 ..._models import BaseModel +from .destroyed_associated_resource import DestroyedAssociatedResource + +__all__ = ["DestroyWithAssociatedResourceCheckStatusResponse", "Resources"] + + +class Resources(BaseModel): + """ + An object containing additional information about resource related to a Droplet requested to be destroyed. + """ + + 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): + """An objects containing information about a resources scheduled for deletion.""" + + 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..fd3784e4 --- /dev/null +++ b/src/gradient/types/gpu_droplets/destroyed_associated_resource.py @@ -0,0 +1,30 @@ +# 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): + """An object containing information about a resource scheduled for deletion.""" + + 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..14d4a0bb --- /dev/null +++ b/src/gradient/types/gpu_droplets/domains.py @@ -0,0 +1,24 @@ +# 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): + """An object specifying domain configurations for a Global load balancer.""" + + 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..44481775 --- /dev/null +++ b/src/gradient/types/gpu_droplets/domains_param.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 TypedDict + +__all__ = ["DomainsParam"] + + +class DomainsParam(TypedDict, total=False): + """An object specifying domain configurations for a Global load balancer.""" + + 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..f592d510 --- /dev/null +++ b/src/gradient/types/gpu_droplets/floating_ip.py @@ -0,0 +1,52 @@ +# 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): + """The region that the floating IP is reserved to. + + When you query a floating IP, the entire region object will be returned. + """ + + 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..a9345e05 --- /dev/null +++ b/src/gradient/types/gpu_droplets/forwarding_rule.py @@ -0,0 +1,51 @@ +# 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): + """An object specifying a forwarding rule for a load balancer.""" + + 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..f81dfd6b --- /dev/null +++ b/src/gradient/types/gpu_droplets/forwarding_rule_param.py @@ -0,0 +1,50 @@ +# 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): + """An object specifying a forwarding rule for a load balancer.""" + + 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..0af332eb --- /dev/null +++ b/src/gradient/types/gpu_droplets/glb_settings.py @@ -0,0 +1,49 @@ +# 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): + """An object specifying CDN configurations for a Global load balancer.""" + + is_enabled: Optional[bool] = None + """A boolean flag to enable CDN caching.""" + + +class GlbSettings(BaseModel): + """An object specifying forwarding configurations for a Global load balancer.""" + + 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..a790d0ee --- /dev/null +++ b/src/gradient/types/gpu_droplets/glb_settings_param.py @@ -0,0 +1,49 @@ +# 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): + """An object specifying CDN configurations for a Global load balancer.""" + + is_enabled: bool + """A boolean flag to enable CDN caching.""" + + +class GlbSettingsParam(TypedDict, total=False): + """An object specifying forwarding configurations for a Global load balancer.""" + + 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..e20cbc65 --- /dev/null +++ b/src/gradient/types/gpu_droplets/health_check.py @@ -0,0 +1,51 @@ +# 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): + """An object specifying health check settings for the load balancer.""" + + 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..47de9e45 --- /dev/null +++ b/src/gradient/types/gpu_droplets/health_check_param.py @@ -0,0 +1,50 @@ +# 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): + """An object specifying health check settings for the load balancer.""" + + 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..d233c642 --- /dev/null +++ b/src/gradient/types/gpu_droplets/lb_firewall.py @@ -0,0 +1,25 @@ +# 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): + """ + An object specifying allow and deny rules to control traffic to the load balancer. + """ + + 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..b15cb32c --- /dev/null +++ b/src/gradient/types/gpu_droplets/lb_firewall_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 TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["LbFirewallParam"] + + +class LbFirewallParam(TypedDict, total=False): + """ + An object specifying allow and deny rules to control traffic to the load balancer. + """ + + 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..1723241a --- /dev/null +++ b/src/gradient/types/gpu_droplets/sticky_sessions.py @@ -0,0 +1,32 @@ +# 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): + """An object specifying sticky sessions settings for the load balancer.""" + + 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..425873dc --- /dev/null +++ b/src/gradient/types/gpu_droplets/sticky_sessions_param.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__ = ["StickySessionsParam"] + + +class StickySessionsParam(TypedDict, total=False): + """An object specifying sticky sessions settings for the load balancer.""" + + 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..324e6038 --- /dev/null +++ b/src/gradient/types/image_generate_response.py @@ -0,0 +1,71 @@ +# 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): + """Represents the content of a generated image from GPT-IMAGE-1""" + + 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): + """Detailed breakdown of input tokens""" + + text_tokens: Optional[int] = None + """Number of text tokens in the input""" + + +class Usage(BaseModel): + """Usage statistics for the image generation request""" + + 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): + """The response from the image generation endpoint""" + + 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..28f96839 --- /dev/null +++ b/src/gradient/types/inference/api_model_api_key_info.py @@ -0,0 +1,29 @@ +# 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): + """Model API Key Info""" + + 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..24cfd98b --- /dev/null +++ b/src/gradient/types/knowledge_base_create_params.py @@ -0,0 +1,106 @@ +# 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): + """Dropbox Data Source""" + + 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): + """Google Drive Data Source""" + + 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..72f0b58c --- /dev/null +++ b/src/gradient/types/knowledge_base_create_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 +from .api_knowledge_base import APIKnowledgeBase + +__all__ = ["KnowledgeBaseCreateResponse"] + + +class KnowledgeBaseCreateResponse(BaseModel): + """Information about a newly created knowledge base""" + + 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..cdf154ba --- /dev/null +++ b/src/gradient/types/knowledge_base_delete_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__ = ["KnowledgeBaseDeleteResponse"] + + +class KnowledgeBaseDeleteResponse(BaseModel): + """Information about a deleted knowledge base""" + + 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..f5376c61 --- /dev/null +++ b/src/gradient/types/knowledge_base_list_indexing_jobs_response.py @@ -0,0 +1,23 @@ +# 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): + """Indexing jobs""" + + 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..3231f643 --- /dev/null +++ b/src/gradient/types/knowledge_base_list_response.py @@ -0,0 +1,23 @@ +# 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): + """List of knowledge bases""" + + 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..712f858c --- /dev/null +++ b/src/gradient/types/knowledge_base_retrieve_response.py @@ -0,0 +1,33 @@ +# 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): + """The knowledge base""" + + 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..0e4ff539 --- /dev/null +++ b/src/gradient/types/knowledge_base_update_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 +from .api_knowledge_base import APIKnowledgeBase + +__all__ = ["KnowledgeBaseUpdateResponse"] + + +class KnowledgeBaseUpdateResponse(BaseModel): + """Information about an updated knowledge base""" + + 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..6aaeb0e7 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_file_upload_data_source.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__ = ["APIFileUploadDataSource"] + + +class APIFileUploadDataSource(BaseModel): + """File to upload as data source for knowledge base.""" + + 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..3cdd34ee --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_file_upload_data_source_param.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 + +__all__ = ["APIFileUploadDataSourceParam"] + + +class APIFileUploadDataSourceParam(TypedDict, total=False): + """File to upload as data source for knowledge base.""" + + 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..d43ddd6e --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_indexing_job.py @@ -0,0 +1,75 @@ +# 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): + """IndexingJob description""" + + 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", + "INDEX_JOB_STATUS_CANCELLED", + ] + ] = None + + tokens: Optional[int] = None + """Number of tokens [This field is deprecated]""" + + total_datasources: Optional[int] = None + """Number of datasources being indexed""" + + 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..b73e325e --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_knowledge_base_data_source.py @@ -0,0 +1,81 @@ +# 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_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): + """AWS S3 Data Source for Display""" + + bucket_name: Optional[str] = None + """Spaces bucket name""" + + item_path: Optional[str] = None + + region: Optional[str] = None + """Region of bucket""" + + +class DropboxDataSource(BaseModel): + """Dropbox Data Source for Display""" + + folder: Optional[str] = None + + +class GoogleDriveDataSource(BaseModel): + """Google Drive Data Source for Display""" + + folder_id: Optional[str] = None + + folder_name: Optional[str] = None + """Name of the selected folder if available""" + + +class APIKnowledgeBaseDataSource(BaseModel): + """Data Source configuration for Knowledge Bases""" + + 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 + + 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..2ac76d69 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_spaces_data_source.py @@ -0,0 +1,19 @@ +# 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): + """Spaces Bucket Data Source""" + + 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..9c3daf03 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_spaces_data_source_param.py @@ -0,0 +1,19 @@ +# 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): + """Spaces Bucket Data Source""" + + 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..ba1ee81f --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_web_crawler_data_source.py @@ -0,0 +1,32 @@ +# 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): + """WebCrawlerDataSource""" + + base_url: Optional[str] = None + """The base url to crawl.""" + + crawling_option: Optional[Literal["UNKNOWN", "SCOPED", "PATH", "DOMAIN", "SUBDOMAINS", "SITEMAP"]] = 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. + - SITEMAP: Crawl URLs discovered in the sitemap. + """ + + 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..ff4f3307 --- /dev/null +++ b/src/gradient/types/knowledge_bases/api_web_crawler_data_source_param.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 Literal, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["APIWebCrawlerDataSourceParam"] + + +class APIWebCrawlerDataSourceParam(TypedDict, total=False): + """WebCrawlerDataSource""" + + base_url: str + """The base url to crawl.""" + + crawling_option: Literal["UNKNOWN", "SCOPED", "PATH", "DOMAIN", "SUBDOMAINS", "SITEMAP"] + """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. + - SITEMAP: Crawl URLs discovered in the sitemap. + """ + + 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..fa99a8c1 --- /dev/null +++ b/src/gradient/types/knowledge_bases/aws_data_source_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__ = ["AwsDataSourceParam"] + + +class AwsDataSourceParam(TypedDict, total=False): + """AWS S3 Data Source""" + + 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..1d27f0ca --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_create_presigned_urls_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 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): + """A single file’s metadata in the request.""" + + 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..daca9865 --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_create_presigned_urls_response.py @@ -0,0 +1,34 @@ +# 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): + """Detailed info about each presigned URL returned to the client.""" + + 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): + """Response with pre-signed urls to upload files.""" + + 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..da49d870 --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_create_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 +from .api_knowledge_base_data_source import APIKnowledgeBaseDataSource + +__all__ = ["DataSourceCreateResponse"] + + +class DataSourceCreateResponse(BaseModel): + """Information about a newly created knowldege base data source""" + + 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..fc3e59da --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_delete_response.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__ = ["DataSourceDeleteResponse"] + + +class DataSourceDeleteResponse(BaseModel): + """Information about a newly deleted knowledge base data source""" + + 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..5de5d372 --- /dev/null +++ b/src/gradient/types/knowledge_bases/data_source_list_response.py @@ -0,0 +1,23 @@ +# 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): + """A list of knowledge base data sources""" + + 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..df7e6911 --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_create_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 +from .api_indexing_job import APIIndexingJob + +__all__ = ["IndexingJobCreateResponse"] + + +class IndexingJobCreateResponse(BaseModel): + """StartKnowledgeBaseIndexingJobOutput description""" + + 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..374533df --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_list_response.py @@ -0,0 +1,23 @@ +# 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): + """Indexing jobs""" + + 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..ea24de65 --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_retrieve_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 +from .api_indexing_job import APIIndexingJob + +__all__ = ["IndexingJobRetrieveResponse"] + + +class IndexingJobRetrieveResponse(BaseModel): + """GetKnowledgeBaseIndexingJobOutput description""" + + 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..2622779b --- /dev/null +++ b/src/gradient/types/knowledge_bases/indexing_job_update_cancel_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 +from .api_indexing_job import APIIndexingJob + +__all__ = ["IndexingJobUpdateCancelResponse"] + + +class IndexingJobUpdateCancelResponse(BaseModel): + """CancelKnowledgeBaseIndexingJobOutput description""" + + 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..48e17809 --- /dev/null +++ b/src/gradient/types/model_list_response.py @@ -0,0 +1,23 @@ +# 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): + """A list of models""" + + 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..0609a486 --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_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 ...._models import BaseModel +from ...api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["AnthropicCreateResponse"] + + +class AnthropicCreateResponse(BaseModel): + """ + CreateAnthropicAPIKeyOutput is used to return the newly created Anthropic API key. + """ + + 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..3ad6a9c6 --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_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 +from ...api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["AnthropicDeleteResponse"] + + +class AnthropicDeleteResponse(BaseModel): + """DeleteAnthropicAPIKeyOutput is used to return the deleted Anthropic API key.""" + + 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..8e2186cb --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_list_agents_response.py @@ -0,0 +1,26 @@ +# 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): + """List of Agents that linked to a specific Anthropic Key""" + + 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..458bd311 --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_list_response.py @@ -0,0 +1,25 @@ +# 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): + """ + ListAnthropicAPIKeysOutput is used to return the list of Anthropic API keys for a specific agent. + """ + + 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..3e24273c --- /dev/null +++ b/src/gradient/types/models/providers/anthropic_update_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 +from ...api_anthropic_api_key_info import APIAnthropicAPIKeyInfo + +__all__ = ["AnthropicUpdateResponse"] + + +class AnthropicUpdateResponse(BaseModel): + """UpdateAnthropicAPIKeyOutput is used to return the updated Anthropic API key.""" + + 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..16aff373 --- /dev/null +++ b/src/gradient/types/models/providers/openai_create_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 +from ...api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["OpenAICreateResponse"] + + +class OpenAICreateResponse(BaseModel): + """CreateOpenAIAPIKeyOutput is used to return the newly created OpenAI API key.""" + + 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..d73a681e --- /dev/null +++ b/src/gradient/types/models/providers/openai_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 +from ...api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["OpenAIDeleteResponse"] + + +class OpenAIDeleteResponse(BaseModel): + """DeleteOpenAIAPIKeyOutput is used to return the deleted OpenAI API key.""" + + 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..825ac890 --- /dev/null +++ b/src/gradient/types/models/providers/openai_list_response.py @@ -0,0 +1,25 @@ +# 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): + """ + ListOpenAIAPIKeysOutput is used to return the list of OpenAI API keys for a specific agent. + """ + + 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..f2266f8f --- /dev/null +++ b/src/gradient/types/models/providers/openai_retrieve_agents_response.py @@ -0,0 +1,26 @@ +# 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): + """List of Agents that are linked to a specific OpenAI Key""" + + 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..b94a8efe --- /dev/null +++ b/src/gradient/types/models/providers/openai_update_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 +from ...api_openai_api_key_info import APIOpenAIAPIKeyInfo + +__all__ = ["OpenAIUpdateResponse"] + + +class OpenAIUpdateResponse(BaseModel): + """UpdateOpenAIAPIKeyOutput is used to return the updated OpenAI API key.""" + + 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..4b297210 --- /dev/null +++ b/src/gradient/types/nf_initiate_action_params.py @@ -0,0 +1,81 @@ +# 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", + "NfsActionAttach", + "NfsActionAttachParams", + "NfsActionDetach", + "NfsActionDetachParams", +] + + +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""" + + +class NfsActionAttach(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: NfsActionAttachParams + + +class NfsActionAttachParams(TypedDict, total=False): + vpc_id: Required[str] + """The ID of the VPC to which the NFS share will be attached""" + + +class NfsActionDetach(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: NfsActionDetachParams + + +class NfsActionDetachParams(TypedDict, total=False): + vpc_id: Required[str] + """The ID of the VPC from which the NFS share will be detached""" + + +NfInitiateActionParams: TypeAlias = Union[NfsActionResize, NfsActionSnapshot, NfsActionAttach, NfsActionDetach] 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..58618450 --- /dev/null +++ b/src/gradient/types/nf_initiate_action_response.py @@ -0,0 +1,37 @@ +# 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): + """The action that was submitted.""" + + 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 response of an NFS share.""" + + 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..a46342ce --- /dev/null +++ b/src/gradient/types/nfs/snapshot_list_response.py @@ -0,0 +1,38 @@ +# 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): + """Represents an NFS snapshot.""" + + 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..12a69d0a --- /dev/null +++ b/src/gradient/types/nfs/snapshot_retrieve_response.py @@ -0,0 +1,39 @@ +# 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): + """Represents an NFS snapshot.""" + + 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/retrieve_documents_params.py b/src/gradient/types/retrieve_documents_params.py new file mode 100644 index 00000000..359d8a07 --- /dev/null +++ b/src/gradient/types/retrieve_documents_params.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["RetrieveDocumentsParams", "Filters", "FiltersMust", "FiltersMustNot", "FiltersShould"] + + +class RetrieveDocumentsParams(TypedDict, total=False): + num_results: Required[int] + """Number of results to return""" + + query: Required[str] + """The search query text""" + + alpha: float + """Weight for hybrid search (0-1): + + - 0 = pure keyword search (BM25) + - 1 = pure vector search (default) + - 0.5 = balanced hybrid search + """ + + filters: Filters + """Metadata filters to apply to the search""" + + +class FiltersMust(TypedDict, total=False): + field: Required[str] + """Metadata field name""" + + operator: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "not_in", "contains"]] + """Comparison operator""" + + value: Required[Union[str, float, bool, SequenceNotStr[str]]] + """Value to compare against (type depends on field)""" + + +class FiltersMustNot(TypedDict, total=False): + field: Required[str] + """Metadata field name""" + + operator: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "not_in", "contains"]] + """Comparison operator""" + + value: Required[Union[str, float, bool, SequenceNotStr[str]]] + """Value to compare against (type depends on field)""" + + +class FiltersShould(TypedDict, total=False): + field: Required[str] + """Metadata field name""" + + operator: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "not_in", "contains"]] + """Comparison operator""" + + value: Required[Union[str, float, bool, SequenceNotStr[str]]] + """Value to compare against (type depends on field)""" + + +class Filters(TypedDict, total=False): + """Metadata filters to apply to the search""" + + must: Iterable[FiltersMust] + """All conditions must match (AND)""" + + must_not: Iterable[FiltersMustNot] + """No conditions should match (NOT)""" + + should: Iterable[FiltersShould] + """At least one condition must match (OR)""" diff --git a/src/gradient/types/retrieve_documents_response.py b/src/gradient/types/retrieve_documents_response.py new file mode 100644 index 00000000..79f3e8eb --- /dev/null +++ b/src/gradient/types/retrieve_documents_response.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List + +from .._models import BaseModel + +__all__ = ["RetrieveDocumentsResponse", "Result"] + + +class Result(BaseModel): + metadata: Dict[str, object] + """Metadata associated with the document""" + + text_content: str + """The text content of the document chunk""" + + +class RetrieveDocumentsResponse(BaseModel): + results: List[Result] + """Array of retrieved document chunks""" + + total_results: int + """Number of results returned""" 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..143c66e3 --- /dev/null +++ b/src/gradient/types/shared/action_link.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__ = ["ActionLink"] + + +class ActionLink(BaseModel): + """The linked actions can be used to check the status of a Droplet's create event.""" + + 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..ce9be06b --- /dev/null +++ b/src/gradient/types/shared/api_links.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 + +__all__ = ["APILinks", "Pages"] + + +class Pages(BaseModel): + """Information about how to reach other pages""" + + 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): + """Links to other pages""" + + 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..1a8cdede --- /dev/null +++ b/src/gradient/types/shared/api_meta.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__ = ["APIMeta"] + + +class APIMeta(BaseModel): + """Meta information about the data set""" + + 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..53d5a563 --- /dev/null +++ b/src/gradient/types/shared/chat_completion_chunk.py @@ -0,0 +1,132 @@ +# 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): + """A chunk of a function that the model called.""" + + 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): + """A chat completion delta generated by streamed model responses.""" + + 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): + """Log probability information for the choice.""" + + 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): + """ + Represents a streamed chunk of a chat completion response returned + by the model, based on the provided input. + """ + + 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..79ce64ee --- /dev/null +++ b/src/gradient/types/shared/completion_usage.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["CompletionUsage"] + + +class CompletionUsage(BaseModel): + """Usage statistics for the completion request.""" + + 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..4ae5bae4 --- /dev/null +++ b/src/gradient/types/shared/droplet.py @@ -0,0 +1,148 @@ +# 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): + """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. + """ + + 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..7f9d7329 --- /dev/null +++ b/src/gradient/types/shared/gpu_info.py @@ -0,0 +1,29 @@ +# 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): + """ + An object containing information about the GPU capabilities of Droplets created with this size. + """ + + 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..de44188f --- /dev/null +++ b/src/gradient/types/shared/image_gen_completed_event.py @@ -0,0 +1,61 @@ +# 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): + """The input tokens detailed information for the image generation.""" + + 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): + """For `gpt-image-1` only, the token usage information for the image generation.""" + + 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): + """Emitted when image generation has completed and the final image is available.""" + + 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..e2740e08 --- /dev/null +++ b/src/gradient/types/shared/image_gen_partial_image_event.py @@ -0,0 +1,35 @@ +# 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): + """Emitted when a partial image is available during image generation streaming.""" + + 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..79091d33 --- /dev/null +++ b/src/gradient/types/shared/kernel.py @@ -0,0 +1,34 @@ +# 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): + """ + **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. + """ + + 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..b7d703df --- /dev/null +++ b/src/gradient/types/shared/meta_properties.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__ = ["MetaProperties"] + + +class MetaProperties(BaseModel): + """Information about the response itself.""" + + 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..6969ee96 --- /dev/null +++ b/tests/api_resources/test_nfs.py @@ -0,0 +1,855 @@ +# 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", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_initiate_action_overload_3(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_3(self, client: Gradient) -> None: + nf = client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + params={"vpc_id": "vpc-id-123"}, + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_action_overload_3(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_3(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_3(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_4(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_4(self, client: Gradient) -> None: + nf = client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + params={"vpc_id": "vpc-id-123"}, + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_initiate_action_overload_4(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_4(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_4(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", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_initiate_action_overload_3(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_3(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + params={"vpc_id": "vpc-id-123"}, + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_action_overload_3(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_3(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_3(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_4(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_4(self, async_client: AsyncGradient) -> None: + nf = await async_client.nfs.initiate_action( + nfs_id="0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", + region="atl1", + type="resize", + params={"vpc_id": "vpc-id-123"}, + ) + assert_matches_type(NfInitiateActionResponse, nf, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_initiate_action_overload_4(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_4(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_4(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/api_resources/test_retrieve.py b/tests/api_resources/test_retrieve.py new file mode 100644 index 00000000..167d2a96 --- /dev/null +++ b/tests/api_resources/test_retrieve.py @@ -0,0 +1,192 @@ +# 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 RetrieveDocumentsResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRetrieve: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_documents(self, client: Gradient) -> None: + retrieve = client.retrieve.documents( + knowledge_base_id="550e8400-e29b-41d4-a716-446655440000", + num_results=5, + query="What are the best practices for deploying machine learning models?", + ) + assert_matches_type(RetrieveDocumentsResponse, retrieve, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_documents_with_all_params(self, client: Gradient) -> None: + retrieve = client.retrieve.documents( + knowledge_base_id="550e8400-e29b-41d4-a716-446655440000", + num_results=5, + query="What are the best practices for deploying machine learning models?", + alpha=0.75, + filters={ + "must": [ + { + "field": "category", + "operator": "eq", + "value": "documentation", + } + ], + "must_not": [ + { + "field": "category", + "operator": "eq", + "value": "documentation", + } + ], + "should": [ + { + "field": "category", + "operator": "eq", + "value": "documentation", + } + ], + }, + ) + assert_matches_type(RetrieveDocumentsResponse, retrieve, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_documents(self, client: Gradient) -> None: + response = client.retrieve.with_raw_response.documents( + knowledge_base_id="550e8400-e29b-41d4-a716-446655440000", + num_results=5, + query="What are the best practices for deploying machine learning models?", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + retrieve = response.parse() + assert_matches_type(RetrieveDocumentsResponse, retrieve, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_documents(self, client: Gradient) -> None: + with client.retrieve.with_streaming_response.documents( + knowledge_base_id="550e8400-e29b-41d4-a716-446655440000", + num_results=5, + query="What are the best practices for deploying machine learning models?", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + retrieve = response.parse() + assert_matches_type(RetrieveDocumentsResponse, retrieve, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_documents(self, client: Gradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_id` but received ''"): + client.retrieve.with_raw_response.documents( + knowledge_base_id="", + num_results=5, + query="What are the best practices for deploying machine learning models?", + ) + + +class TestAsyncRetrieve: + 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_documents(self, async_client: AsyncGradient) -> None: + retrieve = await async_client.retrieve.documents( + knowledge_base_id="550e8400-e29b-41d4-a716-446655440000", + num_results=5, + query="What are the best practices for deploying machine learning models?", + ) + assert_matches_type(RetrieveDocumentsResponse, retrieve, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_documents_with_all_params(self, async_client: AsyncGradient) -> None: + retrieve = await async_client.retrieve.documents( + knowledge_base_id="550e8400-e29b-41d4-a716-446655440000", + num_results=5, + query="What are the best practices for deploying machine learning models?", + alpha=0.75, + filters={ + "must": [ + { + "field": "category", + "operator": "eq", + "value": "documentation", + } + ], + "must_not": [ + { + "field": "category", + "operator": "eq", + "value": "documentation", + } + ], + "should": [ + { + "field": "category", + "operator": "eq", + "value": "documentation", + } + ], + }, + ) + assert_matches_type(RetrieveDocumentsResponse, retrieve, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_documents(self, async_client: AsyncGradient) -> None: + response = await async_client.retrieve.with_raw_response.documents( + knowledge_base_id="550e8400-e29b-41d4-a716-446655440000", + num_results=5, + query="What are the best practices for deploying machine learning models?", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + retrieve = await response.parse() + assert_matches_type(RetrieveDocumentsResponse, retrieve, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_documents(self, async_client: AsyncGradient) -> None: + async with async_client.retrieve.with_streaming_response.documents( + knowledge_base_id="550e8400-e29b-41d4-a716-446655440000", + num_results=5, + query="What are the best practices for deploying machine learning models?", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + retrieve = await response.parse() + assert_matches_type(RetrieveDocumentsResponse, retrieve, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_documents(self, async_client: AsyncGradient) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `knowledge_base_id` but received ''"): + await async_client.retrieve.with_raw_response.documents( + knowledge_base_id="", + num_results=5, + query="What are the best practices for deploying machine learning models?", + ) 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..4f0271a2 --- /dev/null +++ b/tests/test_client.py @@ -0,0 +1,2073 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import gc +import os +import sys +import json +import asyncio +import inspect +import dataclasses +import tracemalloc +from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast +from unittest import mock +from typing_extensions import Literal, AsyncIterator, override + +import httpx +import pytest +from respx import MockRouter +from pydantic import ValidationError + +from 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 + +T = TypeVar("T") +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 mirror_request_content(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, content=request.content) + + +# note: we can't use the httpx.MockTransport class as it consumes the request +# body itself, which means we can't test that the body is read lazily +class MockTransport(httpx.BaseTransport, httpx.AsyncBaseTransport): + def __init__( + self, + handler: Callable[[httpx.Request], httpx.Response] + | Callable[[httpx.Request], Coroutine[Any, Any, httpx.Response]], + ) -> None: + self.handler = handler + + @override + def handle_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert not inspect.iscoroutinefunction(self.handler), "handler must not be a coroutine function" + assert inspect.isfunction(self.handler), "handler must be a function" + return self.handler(request) + + @override + async def handle_async_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert inspect.iscoroutinefunction(self.handler), "handler must be a coroutine function" + return await self.handler(request) + + +@dataclasses.dataclass +class Counter: + value: int = 0 + + +def _make_sync_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> Iterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + +async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> AsyncIterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + +def _get_open_connections(client: 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_binary_content_upload(self, respx_mock: MockRouter, client: Gradient) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + def test_binary_content_upload_with_iterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_sync_iterator([file_content], counter=counter) + + def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=request.read()) + + with Gradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), + ) as client: + response = client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: Gradient) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + @pytest.mark.respx(base_url=base_url) + def test_basic_union_response(self, respx_mock: MockRouter, client: 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_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncGradient) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = await async_client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + async def test_binary_content_upload_with_asynciterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_async_iterator([file_content], counter=counter) + + async def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=await request.aread()) + + async with AsyncGradient( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), + ) as client: + response = await client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + async def test_binary_content_upload_with_body_is_deprecated( + self, respx_mock: MockRouter, async_client: AsyncGradient + ) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = await async_client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + @pytest.mark.respx(base_url=base_url) + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: 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)