From bcf3c02c9433f2fbfc17b4440b2b9b07d4e248e2 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Fri, 27 Mar 2026 17:17:06 -0500 Subject: [PATCH 1/2] Initial Python SDK with generated API models and codegen tooling --- .github/workflows/ci.yml | 41 + .gitignore | 4 + CONTRIBUTING.md | 17 + README.md | 26 +- baseten/__init__.py | 0 baseten/client/__init__.py | 26 + baseten/client/_inference.py | 272 + baseten/client/_management.py | 203 + baseten/client/inferenceapi/__init__.py | 58 + baseten/client/inferenceapi/_client.py | 1366 +++ baseten/client/inferenceapi/_models.py | 207 + baseten/client/managementapi/__init__.py | 379 + baseten/client/managementapi/_client.py | 3339 ++++++++ baseten/client/managementapi/_models.py | 3564 ++++++++ baseten/py.typed | 0 pyproject.toml | 53 + scripts/apigen/__main__.py | 158 + scripts/apigen/clientgen.py | 397 + scripts/apigen/preprocess.py | 100 + scripts/apigen/specs/inference.json | 1491 ++++ scripts/apigen/specs/management.json | 9994 ++++++++++++++++++++++ tests/client/test_client.py | 152 + tests/client/test_inference.py | 113 + tests/client/test_management.py | 131 + tests/conftest.py | 53 + uv.lock | 862 ++ 26 files changed, 23005 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 baseten/__init__.py create mode 100644 baseten/client/__init__.py create mode 100644 baseten/client/_inference.py create mode 100644 baseten/client/_management.py create mode 100644 baseten/client/inferenceapi/__init__.py create mode 100644 baseten/client/inferenceapi/_client.py create mode 100644 baseten/client/inferenceapi/_models.py create mode 100644 baseten/client/managementapi/__init__.py create mode 100644 baseten/client/managementapi/_client.py create mode 100644 baseten/client/managementapi/_models.py create mode 100644 baseten/py.typed create mode 100644 pyproject.toml create mode 100644 scripts/apigen/__main__.py create mode 100644 scripts/apigen/clientgen.py create mode 100644 scripts/apigen/preprocess.py create mode 100644 scripts/apigen/specs/inference.json create mode 100644 scripts/apigen/specs/management.json create mode 100644 tests/client/test_client.py create mode 100644 tests/client/test_inference.py create mode 100644 tests/client/test_management.py create mode 100644 tests/conftest.py create mode 100644 uv.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c3ed7bf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + check: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.14"] + include: + - os: ubuntu-latest + python-version: "3.10" + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v6 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - run: uv sync + + - name: Lint + run: | + uv run poe lint + uv run poe typecheck + + - name: Generate API and check for diff + run: | + uv run poe generate-api + git diff --exit-code + + - name: Test + run: uv run poe test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5e3b0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.pytest_cache +.ruff_cache +.venv +__pycache__ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a1a7069 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,17 @@ +# Contributing + +## Setup + +```bash +uv sync +``` + +## Tasks + +All tasks are run via `uv run poe `: + +- `generate-api` - Regenerate API clients and models from OpenAPI specs (pass `--update-specs` to download latest specs first) +- `format` - Format code and auto-fix lint issues +- `lint` - Check formatting and lint (fails on issues) +- `typecheck` - Run type checker +- `test` - Run tests diff --git a/README.md b/README.md index 3f1009c..3c7c787 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,27 @@ # Baseten Python SDK -Under development. +Python SDK for Baseten. + +⚠️ Under active development. Nothing should be considered stable at this time. + +## Usage + +Current SDK only has barebones client. Here is usage example of the barebones underlying client: + +```python +from baseten.client import ManagementClient + +with ManagementClient(api_key="my-api-key") as client: + for model in client.api.get_models().models: + print(model.name) +``` + +Or for async: + +```python +from baseten.client import AsyncManagementClient + +async with AsyncManagementClient(api_key="my-api-key") as client: + for model in (await client.api.get_models()).models: + print(model.name) +``` \ No newline at end of file diff --git a/baseten/__init__.py b/baseten/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/baseten/client/__init__.py b/baseten/client/__init__.py new file mode 100644 index 0000000..ca1f663 --- /dev/null +++ b/baseten/client/__init__.py @@ -0,0 +1,26 @@ +"""Baseten client library. + +Use :class:`ManagementClient` or :class:`AsyncManagementClient` for the +management API, and :class:`InferenceClient` or :class:`AsyncInferenceClient` +for the inference API. +""" + +from baseten.client._inference import ( + AsyncInferenceClient, + InferenceClient, + InferenceClientOptions, +) +from baseten.client._management import ( + AsyncManagementClient, + ManagementClient, + ManagementClientOptions, +) + +__all__ = [ + "AsyncInferenceClient", + "AsyncManagementClient", + "InferenceClient", + "InferenceClientOptions", + "ManagementClient", + "ManagementClientOptions", +] diff --git a/baseten/client/_inference.py b/baseten/client/_inference.py new file mode 100644 index 0000000..76d0706 --- /dev/null +++ b/baseten/client/_inference.py @@ -0,0 +1,272 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +import httpx + +import baseten.client.inferenceapi + + +@dataclass(frozen=True) +class InferenceClientOptions: + """Options for :class:`InferenceClient` and :class:`AsyncInferenceClient`. + + Obtain via :attr:`InferenceClient.options` to inspect the values a client + was constructed with. + """ + + api_key: str + """API key for authentication.""" + + model_id: str | None = None + """Model ID. Mutually exclusive with *chain_id*.""" + + chain_id: str | None = None + """Chain ID. Mutually exclusive with *model_id*.""" + + environment: str | None = None + """Environment name for regional routing (e.g. ``"production"``). Affects the base URL hostname.""" + + base_url_override: str | None = None + """Explicit base URL override. When set, *model_id*, *chain_id*, and + *environment* are ignored.""" + + @property + def base_url(self) -> str: + """The resolved base URL for the inference API.""" + if self.base_url_override is not None: + return self.base_url_override + return InferenceClient.default_base_url( + model_id=self.model_id, + chain_id=self.chain_id, + environment=self.environment, + ) + + +class InferenceClient: + """Synchronous client for the Baseten Inference API. + + Can be used as a context manager to ensure the underlying HTTP client is + closed on exit. + """ + + @classmethod + def default_base_url( + cls, + *, + model_id: str | None = None, + chain_id: str | None = None, + environment: str | None = None, + ) -> str: + """Compute the default inference base URL. + + Args: + model_id: Model ID. Mutually exclusive with *chain_id*. + chain_id: Chain ID. Mutually exclusive with *model_id*. + environment: Optional environment name. + + Returns: + The computed base URL. + + Raises: + ValueError: If both or neither of *model_id* and *chain_id* are + provided. + """ + if (model_id is None) == (chain_id is None): + raise ValueError("exactly one of model_id or chain_id must be provided") + prefix = f"model-{model_id}" if model_id is not None else f"chain-{chain_id}" + if environment is not None: + return f"https://{prefix}-{environment}.api.baseten.co" + return f"https://{prefix}.api.baseten.co" + + def __init__( + self, + *, + api_key: str, + model_id: str | None = None, + chain_id: str | None = None, + environment: str | None = None, + base_url_override: str | None = None, + http_client: httpx.Client | None = None, + close_http_client_on_close: bool | None = None, + ) -> None: + """Create a new synchronous inference client. + + Args: + api_key: API key for authentication. + model_id: Model ID. Mutually exclusive with *chain_id*. + chain_id: Chain ID. Mutually exclusive with *model_id*. + environment: Environment name for regional routing (e.g. ``"production"``). + base_url_override: Override the computed base URL. When set, + *model_id*, *chain_id*, and *environment* are ignored. + http_client: Pre-configured httpx client. When provided, the + caller is responsible for setting base URL and auth headers. + close_http_client_on_close: Whether :meth:`close` should close + the underlying HTTP client. Defaults to ``True`` when the + client is created internally, ``False`` when *http_client* + is provided. + """ + self._options = InferenceClientOptions( + api_key=api_key, + model_id=model_id, + chain_id=chain_id, + environment=environment, + base_url_override=base_url_override, + ) + if http_client is None: + self._http_client = httpx.Client( + base_url=self._options.base_url, + headers={"Authorization": f"Api-Key {api_key}"}, + ) + self.close_http_client_on_close = ( + True + if close_http_client_on_close is None + else close_http_client_on_close + ) + else: + self._http_client = http_client + self.close_http_client_on_close = ( + False + if close_http_client_on_close is None + else close_http_client_on_close + ) + self._api = baseten.client.inferenceapi.ApiClient(self._http_client) + + @property + def options(self) -> InferenceClientOptions: + """Client options.""" + return self._options + + @property + def http_client(self) -> httpx.Client: + """The underlying HTTP client.""" + return self._http_client + + @property + def api(self) -> baseten.client.inferenceapi.ApiClient: + """The generated API client. + + The generated API surface is not covered by stability guarantees and + may change between versions. + """ + return self._api + + def close(self) -> None: + """Close the client, optionally closing the underlying HTTP client.""" + if self.close_http_client_on_close: + self._http_client.close() + + def __enter__(self) -> InferenceClient: + return self + + def __exit__(self, *args: Any) -> None: + self.close() + + +class AsyncInferenceClient: + """Asynchronous client for the Baseten Inference API. + + Can be used as an async context manager to ensure the underlying HTTP + client is closed on exit. + """ + + @classmethod + def default_base_url( + cls, + *, + model_id: str | None = None, + chain_id: str | None = None, + environment: str | None = None, + ) -> str: + """Compute the default inference base URL. + + See :meth:`InferenceClient.default_base_url` for details. + """ + return InferenceClient.default_base_url( + model_id=model_id, chain_id=chain_id, environment=environment + ) + + def __init__( + self, + *, + api_key: str, + model_id: str | None = None, + chain_id: str | None = None, + environment: str | None = None, + base_url_override: str | None = None, + http_client: httpx.AsyncClient | None = None, + close_http_client_on_close: bool | None = None, + ) -> None: + """Create a new asynchronous inference client. + + Args: + api_key: API key for authentication. + model_id: Model ID. Mutually exclusive with *chain_id*. + chain_id: Chain ID. Mutually exclusive with *model_id*. + environment: Environment name for regional routing (e.g. ``"production"``). + base_url_override: Override the computed base URL. When set, + *model_id*, *chain_id*, and *environment* are ignored. + http_client: Pre-configured httpx async client. When provided, + the caller is responsible for setting base URL and auth + headers. + close_http_client_on_close: Whether :meth:`close` should close + the underlying HTTP client. Defaults to ``True`` when the + client is created internally, ``False`` when *http_client* + is provided. + """ + self._options = InferenceClientOptions( + api_key=api_key, + model_id=model_id, + chain_id=chain_id, + environment=environment, + base_url_override=base_url_override, + ) + if http_client is None: + self._http_client = httpx.AsyncClient( + base_url=self._options.base_url, + headers={"Authorization": f"Api-Key {api_key}"}, + ) + self.close_http_client_on_close = ( + True + if close_http_client_on_close is None + else close_http_client_on_close + ) + else: + self._http_client = http_client + self.close_http_client_on_close = ( + False + if close_http_client_on_close is None + else close_http_client_on_close + ) + self._api = baseten.client.inferenceapi.AsyncApiClient(self._http_client) + + @property + def options(self) -> InferenceClientOptions: + """Client options.""" + return self._options + + @property + def http_client(self) -> httpx.AsyncClient: + """The underlying HTTP client.""" + return self._http_client + + @property + def api(self) -> baseten.client.inferenceapi.AsyncApiClient: + """The generated API client. + + The generated API surface is not covered by stability guarantees and + may change between versions. + """ + return self._api + + async def close(self) -> None: + """Close the client, optionally closing the underlying HTTP client.""" + if self.close_http_client_on_close: + await self._http_client.aclose() + + async def __aenter__(self) -> AsyncInferenceClient: + return self + + async def __aexit__(self, *args: Any) -> None: + await self.close() diff --git a/baseten/client/_management.py b/baseten/client/_management.py new file mode 100644 index 0000000..5bfe315 --- /dev/null +++ b/baseten/client/_management.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +import httpx + +import baseten.client.managementapi + + +@dataclass(frozen=True) +class ManagementClientOptions: + """Options for :class:`ManagementClient` and :class:`AsyncManagementClient`. + + Obtain via :attr:`ManagementClient.options` to inspect the values a client + was constructed with. + """ + + api_key: str + """API key for authentication.""" + + base_url_override: str | None = None + """Explicit base URL override, or ``None`` to use the default.""" + + @property + def base_url(self) -> str: + """The resolved base URL for the management API.""" + if self.base_url_override is not None: + return self.base_url_override + return ManagementClient.default_base_url() + + +class ManagementClient: + """Synchronous client for the Baseten Management API. + + Can be used as a context manager to ensure the underlying HTTP client is + closed on exit. + """ + + @classmethod + def default_base_url(cls) -> str: + """Return the default base URL for the management API.""" + return "https://api.baseten.co" + + def __init__( + self, + *, + api_key: str, + base_url_override: str | None = None, + http_client: httpx.Client | None = None, + close_http_client_on_close: bool | None = None, + ) -> None: + """Create a new synchronous management client. + + Args: + api_key: API key for authentication. + base_url_override: Override the default base URL. When ``None``, + :meth:`default_base_url` is used. + http_client: Pre-configured httpx client. When provided, the + caller is responsible for setting base URL and auth headers. + close_http_client_on_close: Whether :meth:`close` should close + the underlying HTTP client. Defaults to ``True`` when the + client is created internally, ``False`` when *http_client* + is provided. + """ + self._options = ManagementClientOptions( + api_key=api_key, base_url_override=base_url_override + ) + if http_client is None: + self._http_client = httpx.Client( + base_url=self._options.base_url, + headers={"Authorization": f"Api-Key {api_key}"}, + ) + self.close_http_client_on_close = ( + True + if close_http_client_on_close is None + else close_http_client_on_close + ) + else: + self._http_client = http_client + self.close_http_client_on_close = ( + False + if close_http_client_on_close is None + else close_http_client_on_close + ) + self._api = baseten.client.managementapi.ApiClient(self._http_client) + + @property + def options(self) -> ManagementClientOptions: + """Client options.""" + return self._options + + @property + def http_client(self) -> httpx.Client: + """The underlying HTTP client.""" + return self._http_client + + @property + def api(self) -> baseten.client.managementapi.ApiClient: + """The generated API client. + + The generated API surface is not covered by stability guarantees and + may change between versions. + """ + return self._api + + def close(self) -> None: + """Close the client, optionally closing the underlying HTTP client.""" + if self.close_http_client_on_close: + self._http_client.close() + + def __enter__(self) -> ManagementClient: + return self + + def __exit__(self, *args: Any) -> None: + self.close() + + +class AsyncManagementClient: + """Asynchronous client for the Baseten Management API. + + Can be used as an async context manager to ensure the underlying HTTP + client is closed on exit. + """ + + @classmethod + def default_base_url(cls) -> str: + """Return the default base URL for the management API.""" + return ManagementClient.default_base_url() + + def __init__( + self, + *, + api_key: str, + base_url_override: str | None = None, + http_client: httpx.AsyncClient | None = None, + close_http_client_on_close: bool | None = None, + ) -> None: + """Create a new asynchronous management client. + + Args: + api_key: API key for authentication. + base_url_override: Override the default base URL. When ``None``, + :meth:`default_base_url` is used. + http_client: Pre-configured httpx async client. When provided, + the caller is responsible for setting base URL and auth + headers. + close_http_client_on_close: Whether :meth:`close` should close + the underlying HTTP client. Defaults to ``True`` when the + client is created internally, ``False`` when *http_client* + is provided. + """ + self._options = ManagementClientOptions( + api_key=api_key, base_url_override=base_url_override + ) + if http_client is None: + self._http_client = httpx.AsyncClient( + base_url=self._options.base_url, + headers={"Authorization": f"Api-Key {api_key}"}, + ) + self.close_http_client_on_close = ( + True + if close_http_client_on_close is None + else close_http_client_on_close + ) + else: + self._http_client = http_client + self.close_http_client_on_close = ( + False + if close_http_client_on_close is None + else close_http_client_on_close + ) + self._api = baseten.client.managementapi.AsyncApiClient(self._http_client) + + @property + def options(self) -> ManagementClientOptions: + """Client options.""" + return self._options + + @property + def http_client(self) -> httpx.AsyncClient: + """The underlying HTTP client.""" + return self._http_client + + @property + def api(self) -> baseten.client.managementapi.AsyncApiClient: + """The generated API client. + + The generated API surface is not covered by stability guarantees and + may change between versions. + """ + return self._api + + async def close(self) -> None: + """Close the client, optionally closing the underlying HTTP client.""" + if self.close_http_client_on_close: + await self._http_client.aclose() + + async def __aenter__(self) -> AsyncManagementClient: + return self + + async def __aexit__(self, *args: Any) -> None: + await self.close() diff --git a/baseten/client/inferenceapi/__init__.py b/baseten/client/inferenceapi/__init__.py new file mode 100644 index 0000000..4b4fa3e --- /dev/null +++ b/baseten/client/inferenceapi/__init__.py @@ -0,0 +1,58 @@ +# Code generated by apigen. DO NOT EDIT. + +"""Generated client and models for the Baseten Inference API. + +Use :class:`ApiClient` for synchronous access or :class:`AsyncApiClient` for +asynchronous access. + +Types in this module are generated from the OpenAPI specification and are NOT +covered by any stability or compatibility guarantees. They may change without +notice between versions. +""" + +from ._client import ApiClient, AsyncApiClient, ResponseError, ResponseErrorResponse +from ._models import ( + AsyncPredictOutput, + AsyncPredictRequest, + AsyncRequestError, + AsyncRequestStatusResponse, + AsyncRunRemoteInput, + AsyncRunRemoteOutput, + CancelAsyncRequestOutput, + Code, + ErrorCode, + ErrorResponse, + GetAsyncQueueStatusOutput, + InferenceRetryConfig, + PredictInput, + PredictOutput, + RunRemoteInput, + RunRemoteOutput, + Status, + WebhookStatus, +) + +__all__ = [ + "ApiClient", + "AsyncApiClient", + "ResponseError", + "ResponseErrorResponse", + "AsyncPredictOutput", + "AsyncPredictRequest", + "AsyncRequestError", + "AsyncRequestStatusResponse", + "AsyncRunRemoteInput", + "AsyncRunRemoteOutput", + "CancelAsyncRequestOutput", + "Code", + "ErrorCode", + "ErrorResponse", + "GetAsyncQueueStatusOutput", + "InferenceRetryConfig", + "PredictInput", + "PredictOutput", + "RunRemoteInput", + "RunRemoteOutput", + "Status", + "WebhookStatus", +] diff --git a/baseten/client/inferenceapi/_client.py b/baseten/client/inferenceapi/_client.py new file mode 100644 index 0000000..1974bcc --- /dev/null +++ b/baseten/client/inferenceapi/_client.py @@ -0,0 +1,1366 @@ +# Code generated by apigen/clientgen. DO NOT EDIT. + +from __future__ import annotations + +import urllib.parse +from dataclasses import dataclass +from typing import Any, TypeVar + +import httpx +from pydantic import BaseModel + +from ._models import ( + AsyncPredictOutput, + AsyncPredictRequest, + AsyncRequestStatusResponse, + AsyncRunRemoteInput, + AsyncRunRemoteOutput, + CancelAsyncRequestOutput, + ErrorResponse, + GetAsyncQueueStatusOutput, + PredictInput, + PredictOutput, + RunRemoteInput, + RunRemoteOutput, +) + +_T = TypeVar("_T", bound=BaseModel) + + +@dataclass +class ResponseError(Exception): + status_code: int + body: str + + def __str__(self) -> str: + return f"baseten API error (HTTP {self.status_code}): {self.body}" + + +@dataclass +class ResponseErrorResponse(Exception): + status_code: int + error_response: ErrorResponse + + def __str__(self) -> str: + return f"baseten API error (HTTP {self.status_code}): {self.error_response.model_dump_json()}" + + +_ERROR_TYPES: dict[str, tuple[type[BaseModel], type[Exception], str]] = { + "ErrorResponse": (ErrorResponse, ResponseErrorResponse, "error_response"), +} + + +@dataclass +class _ApiRequest: + method: str + path_fmt: str + path_args: list[str] + body: Any + success_code: int + error_codes: dict[int, str] | None + + +class ApiClient: + """Generated HTTP client for the Baseten API. + + Methods on this client are generated from the OpenAPI specification + and are NOT covered by any stability or compatibility guarantees. + They may change without notice between versions. + """ + + def __init__(self, http_client: httpx.Client) -> None: + """Create a new client. The caller is responsible for closing *http_client*.""" + self._http_client = http_client + + def async_predict( + self, *, env_name: str, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call a named environment of a model.""" + return self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/environments/{}/async_predict", + path_args=[env_name], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_predict_deployment( + self, *, deployment_id: str, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call a specific deployment of a model.""" + return self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/async_predict", + path_args=[deployment_id], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_predict_development( + self, *, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call the development deployment of a model.""" + return self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/development/async_predict", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_predict_production( + self, *, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call the production environment of a model.""" + return self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/production/async_predict", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_predict_regional( + self, *, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call a regional environment of a model.""" + return self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/async_predict", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_run_remote( + self, *, env_name: str, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call a named environment of a chain.""" + return self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/environments/{}/async_run_remote", + path_args=[env_name], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_run_remote_deployment( + self, *, deployment_id: str, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call a specific deployment of a chain.""" + return self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/async_run_remote", + path_args=[deployment_id], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_run_remote_development( + self, *, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call the development deployment of a chain.""" + return self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/development/async_run_remote", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_run_remote_production( + self, *, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call the production environment of a chain.""" + return self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/production/async_run_remote", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def async_run_remote_regional( + self, *, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call a regional environment of a chain.""" + return self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/async_run_remote", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + def cancel_async_request(self, *, request_id: str) -> CancelAsyncRequestOutput: + """Cancel a queued async request.""" + return self._do_json( + CancelAsyncRequestOutput, + _ApiRequest( + method="DELETE", + path_fmt="/async_request/{}", + path_args=[request_id], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + def get_async_queue_status(self, *, env_name: str) -> GetAsyncQueueStatusOutput: + """Get async queue status for a named environment.""" + return self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/environments/{}/async_queue_status", + path_args=[env_name], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + def get_async_queue_status_deployment( + self, *, deployment_id: str + ) -> GetAsyncQueueStatusOutput: + """Get async queue status for a specific deployment.""" + return self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/deployment/{}/async_queue_status", + path_args=[deployment_id], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + def get_async_queue_status_development(self) -> GetAsyncQueueStatusOutput: + """Get async queue status for the development deployment.""" + return self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/development/async_queue_status", + path_args=[], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + def get_async_queue_status_production(self) -> GetAsyncQueueStatusOutput: + """Get async queue status for the production environment.""" + return self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/production/async_queue_status", + path_args=[], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + def get_async_queue_status_regional(self) -> GetAsyncQueueStatusOutput: + """Get async queue status for a regional environment.""" + return self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/async_queue_status", + path_args=[], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + def get_async_request_status( + self, *, request_id: str + ) -> AsyncRequestStatusResponse: + """Get the status of an async request.""" + return self._do_json( + AsyncRequestStatusResponse, + _ApiRequest( + method="GET", + path_fmt="/async_request/{}", + path_args=[request_id], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + def predict(self, *, env_name: str, body: PredictInput) -> PredictOutput: + """Call the model deployment associated with a specified environment.""" + return self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/environments/{}/predict", + path_args=[env_name], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def predict_deployment( + self, *, deployment_id: str, body: PredictInput + ) -> PredictOutput: + """Call a specific deployment of a model by deployment ID.""" + return self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/predict", + path_args=[deployment_id], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def predict_development(self, *, body: PredictInput) -> PredictOutput: + """Call the development deployment of a model.""" + return self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/development/predict", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def predict_production(self, *, body: PredictInput) -> PredictOutput: + """Call the production environment of a model.""" + return self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/production/predict", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def predict_regional(self, *, body: PredictInput) -> PredictOutput: + """Call a regional environment of a model.""" + return self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/predict", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def run_remote(self, *, env_name: str, body: RunRemoteInput) -> RunRemoteOutput: + """Call the chain deployment associated with a specified environment.""" + return self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/environments/{}/run_remote", + path_args=[env_name], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def run_remote_deployment( + self, *, deployment_id: str, body: RunRemoteInput + ) -> RunRemoteOutput: + """Call a specific chain deployment by deployment ID.""" + return self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/run_remote", + path_args=[deployment_id], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def run_remote_development(self, *, body: RunRemoteInput) -> RunRemoteOutput: + """Call the development deployment of a chain.""" + return self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/development/run_remote", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def run_remote_production(self, *, body: RunRemoteInput) -> RunRemoteOutput: + """Call the production environment of a chain.""" + return self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/production/run_remote", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def run_remote_regional(self, *, body: RunRemoteInput) -> RunRemoteOutput: + """Call a regional environment of a chain.""" + return self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/run_remote", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + def wake(self, *, env_name: str) -> None: + """Wake a named environment of a model.""" + self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/environments/{}/wake", + path_args=[env_name], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + def wake_deployment(self, *, deployment_id: str) -> None: + """Wake a specific deployment of a model by deployment ID.""" + self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/wake", + path_args=[deployment_id], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + def wake_development(self) -> None: + """Wake the development deployment of a model.""" + self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/development/wake", + path_args=[], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + def wake_production(self) -> None: + """Wake the production environment of a model.""" + self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/production/wake", + path_args=[], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + def wake_regional(self) -> None: + """Wake a regional environment of a model.""" + self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/wake", + path_args=[], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + def _do(self, request: _ApiRequest) -> httpx.Response: + path = request.path_fmt.format( + *[urllib.parse.quote(a, safe="") for a in request.path_args] + ) + json_body = None + if request.body is not None: + if isinstance(request.body, BaseModel): + json_body = request.body.model_dump(mode="json") + else: + json_body = request.body + response = self._http_client.request(request.method, path, json=json_body) + if response.status_code != request.success_code: + if request.error_codes and response.status_code in request.error_codes: + error_name = request.error_codes[response.status_code] + if error_name in _ERROR_TYPES: + model_cls, exc_cls, field_name = _ERROR_TYPES[error_name] + try: + model = model_cls.model_validate_json(response.content) + raise exc_cls( + status_code=response.status_code, # ty: ignore[unknown-argument] + **{field_name: model}, + ) + except exc_cls: + raise + except Exception: + pass + raise ResponseError(status_code=response.status_code, body=response.text) + return response + + def _do_json(self, response_type: type[_T], request: _ApiRequest) -> _T: + response = self._do(request) + content_type = response.headers.get("content-type", "") + if not content_type.startswith("application/json"): + raise ValueError( + f"unexpected content type {content_type!r}, expected application/json" + ) + return response_type.model_validate_json(response.content) + + def _do_no_response(self, request: _ApiRequest) -> None: + self._do(request) + + +class AsyncApiClient: + """Generated HTTP client for the Baseten API. + + Methods on this client are generated from the OpenAPI specification + and are NOT covered by any stability or compatibility guarantees. + They may change without notice between versions. + """ + + def __init__(self, http_client: httpx.AsyncClient) -> None: + """Create a new client. The caller is responsible for closing *http_client*.""" + self._http_client = http_client + + async def async_predict( + self, *, env_name: str, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call a named environment of a model.""" + return await self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/environments/{}/async_predict", + path_args=[env_name], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_predict_deployment( + self, *, deployment_id: str, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call a specific deployment of a model.""" + return await self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/async_predict", + path_args=[deployment_id], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_predict_development( + self, *, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call the development deployment of a model.""" + return await self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/development/async_predict", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_predict_production( + self, *, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call the production environment of a model.""" + return await self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/production/async_predict", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_predict_regional( + self, *, body: AsyncPredictRequest + ) -> AsyncPredictOutput: + """Asynchronously call a regional environment of a model.""" + return await self._do_json( + AsyncPredictOutput, + _ApiRequest( + method="POST", + path_fmt="/async_predict", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 413: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_run_remote( + self, *, env_name: str, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call a named environment of a chain.""" + return await self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/environments/{}/async_run_remote", + path_args=[env_name], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_run_remote_deployment( + self, *, deployment_id: str, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call a specific deployment of a chain.""" + return await self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/async_run_remote", + path_args=[deployment_id], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_run_remote_development( + self, *, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call the development deployment of a chain.""" + return await self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/development/async_run_remote", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_run_remote_production( + self, *, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call the production environment of a chain.""" + return await self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/production/async_run_remote", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def async_run_remote_regional( + self, *, body: AsyncRunRemoteInput + ) -> AsyncRunRemoteOutput: + """Asynchronously call a regional environment of a chain.""" + return await self._do_json( + AsyncRunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/async_run_remote", + path_args=[], + body=body, + success_code=201, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 503: "ErrorResponse", + }, + ), + ) + + async def cancel_async_request( + self, *, request_id: str + ) -> CancelAsyncRequestOutput: + """Cancel a queued async request.""" + return await self._do_json( + CancelAsyncRequestOutput, + _ApiRequest( + method="DELETE", + path_fmt="/async_request/{}", + path_args=[request_id], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + async def get_async_queue_status( + self, *, env_name: str + ) -> GetAsyncQueueStatusOutput: + """Get async queue status for a named environment.""" + return await self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/environments/{}/async_queue_status", + path_args=[env_name], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + async def get_async_queue_status_deployment( + self, *, deployment_id: str + ) -> GetAsyncQueueStatusOutput: + """Get async queue status for a specific deployment.""" + return await self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/deployment/{}/async_queue_status", + path_args=[deployment_id], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + async def get_async_queue_status_development(self) -> GetAsyncQueueStatusOutput: + """Get async queue status for the development deployment.""" + return await self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/development/async_queue_status", + path_args=[], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + async def get_async_queue_status_production(self) -> GetAsyncQueueStatusOutput: + """Get async queue status for the production environment.""" + return await self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/production/async_queue_status", + path_args=[], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + async def get_async_queue_status_regional(self) -> GetAsyncQueueStatusOutput: + """Get async queue status for a regional environment.""" + return await self._do_json( + GetAsyncQueueStatusOutput, + _ApiRequest( + method="GET", + path_fmt="/async_queue_status", + path_args=[], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + async def get_async_request_status( + self, *, request_id: str + ) -> AsyncRequestStatusResponse: + """Get the status of an async request.""" + return await self._do_json( + AsyncRequestStatusResponse, + _ApiRequest( + method="GET", + path_fmt="/async_request/{}", + path_args=[request_id], + body=None, + success_code=200, + error_codes={401: "ErrorResponse", 429: "ErrorResponse"}, + ), + ) + + async def predict(self, *, env_name: str, body: PredictInput) -> PredictOutput: + """Call the model deployment associated with a specified environment.""" + return await self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/environments/{}/predict", + path_args=[env_name], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def predict_deployment( + self, *, deployment_id: str, body: PredictInput + ) -> PredictOutput: + """Call a specific deployment of a model by deployment ID.""" + return await self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/predict", + path_args=[deployment_id], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def predict_development(self, *, body: PredictInput) -> PredictOutput: + """Call the development deployment of a model.""" + return await self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/development/predict", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def predict_production(self, *, body: PredictInput) -> PredictOutput: + """Call the production environment of a model.""" + return await self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/production/predict", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def predict_regional(self, *, body: PredictInput) -> PredictOutput: + """Call a regional environment of a model.""" + return await self._do_json( + PredictOutput, + _ApiRequest( + method="POST", + path_fmt="/predict", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def run_remote( + self, *, env_name: str, body: RunRemoteInput + ) -> RunRemoteOutput: + """Call the chain deployment associated with a specified environment.""" + return await self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/environments/{}/run_remote", + path_args=[env_name], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def run_remote_deployment( + self, *, deployment_id: str, body: RunRemoteInput + ) -> RunRemoteOutput: + """Call a specific chain deployment by deployment ID.""" + return await self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/run_remote", + path_args=[deployment_id], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def run_remote_development(self, *, body: RunRemoteInput) -> RunRemoteOutput: + """Call the development deployment of a chain.""" + return await self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/development/run_remote", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def run_remote_production(self, *, body: RunRemoteInput) -> RunRemoteOutput: + """Call the production environment of a chain.""" + return await self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/production/run_remote", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def run_remote_regional(self, *, body: RunRemoteInput) -> RunRemoteOutput: + """Call a regional environment of a chain.""" + return await self._do_json( + RunRemoteOutput, + _ApiRequest( + method="POST", + path_fmt="/run_remote", + path_args=[], + body=body, + success_code=200, + error_codes={ + 400: "ErrorResponse", + 401: "ErrorResponse", + 429: "ErrorResponse", + 502: "ErrorResponse", + 503: "ErrorResponse", + 504: "ErrorResponse", + }, + ), + ) + + async def wake(self, *, env_name: str) -> None: + """Wake a named environment of a model.""" + await self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/environments/{}/wake", + path_args=[env_name], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + async def wake_deployment(self, *, deployment_id: str) -> None: + """Wake a specific deployment of a model by deployment ID.""" + await self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/deployment/{}/wake", + path_args=[deployment_id], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + async def wake_development(self) -> None: + """Wake the development deployment of a model.""" + await self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/development/wake", + path_args=[], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + async def wake_production(self) -> None: + """Wake the production environment of a model.""" + await self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/production/wake", + path_args=[], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + async def wake_regional(self) -> None: + """Wake a regional environment of a model.""" + await self._do_no_response( + _ApiRequest( + method="POST", + path_fmt="/wake", + path_args=[], + body=None, + success_code=202, + error_codes={401: "ErrorResponse"}, + ) + ) + + async def _do(self, request: _ApiRequest) -> httpx.Response: + path = request.path_fmt.format( + *[urllib.parse.quote(a, safe="") for a in request.path_args] + ) + json_body = None + if request.body is not None: + if isinstance(request.body, BaseModel): + json_body = request.body.model_dump(mode="json") + else: + json_body = request.body + response = await self._http_client.request(request.method, path, json=json_body) + if response.status_code != request.success_code: + if request.error_codes and response.status_code in request.error_codes: + error_name = request.error_codes[response.status_code] + if error_name in _ERROR_TYPES: + model_cls, exc_cls, field_name = _ERROR_TYPES[error_name] + try: + model = model_cls.model_validate_json(response.content) + raise exc_cls( + status_code=response.status_code, # ty: ignore[unknown-argument] + **{field_name: model}, + ) + except exc_cls: + raise + except Exception: + pass + raise ResponseError(status_code=response.status_code, body=response.text) + return response + + async def _do_json(self, response_type: type[_T], request: _ApiRequest) -> _T: + response = await self._do(request) + content_type = response.headers.get("content-type", "") + if not content_type.startswith("application/json"): + raise ValueError( + f"unexpected content type {content_type!r}, expected application/json" + ) + return response_type.model_validate_json(response.content) + + async def _do_no_response(self, request: _ApiRequest) -> None: + await self._do(request) diff --git a/baseten/client/inferenceapi/_models.py b/baseten/client/inferenceapi/_models.py new file mode 100644 index 0000000..f6970e4 --- /dev/null +++ b/baseten/client/inferenceapi/_models.py @@ -0,0 +1,207 @@ +# generated by datamodel-codegen: +# filename: + +from __future__ import annotations +from typing import Annotated, Any +from pydantic import AnyUrl, BaseModel, Field, RootModel +from enum import Enum + + +class InferenceRetryConfig(BaseModel): + max_attempts: Annotated[ + int | None, + Field(description="Number of predict request attempts.", ge=1, le=10), + ] = 3 + initial_delay_ms: Annotated[ + int | None, + Field( + description="Minimum time between retries in milliseconds.", ge=0, le=10000 + ), + ] = 1000 + max_delay_ms: Annotated[ + int | None, + Field( + description="Maximum time between retries in milliseconds.", ge=0, le=60000 + ), + ] = 5000 + + +class Status(Enum): + QUEUED = "QUEUED" + IN_PROGRESS = "IN_PROGRESS" + SUCCEEDED = "SUCCEEDED" + FAILED = "FAILED" + EXPIRED = "EXPIRED" + CANCELED = "CANCELED" + WEBHOOK_FAILED = "WEBHOOK_FAILED" + + +class WebhookStatus(Enum): + PENDING = "PENDING" + SUCCEEDED = "SUCCEEDED" + FAILED = "FAILED" + CANCELED = "CANCELED" + NO_WEBHOOK_PROVIDED = "NO_WEBHOOK_PROVIDED" + + +class Code(Enum): + MODEL_PREDICT_ERROR = "MODEL_PREDICT_ERROR" + MODEL_PREDICT_TIMEOUT = "MODEL_PREDICT_TIMEOUT" + MODEL_NOT_READY = "MODEL_NOT_READY" + MODEL_DOES_NOT_EXIST = "MODEL_DOES_NOT_EXIST" + MODEL_UNAVAILABLE = "MODEL_UNAVAILABLE" + MODEL_INVALID_INPUT = "MODEL_INVALID_INPUT" + ASYNC_REQUEST_NOT_SUPPORTED = "ASYNC_REQUEST_NOT_SUPPORTED" + INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR" + + +class AsyncRequestError(BaseModel): + code: Annotated[Code, Field(description="The type of error that occurred.")] + message: Annotated[str, Field(description="Details of the error.")] + + +class ErrorCode(Enum): + timeout = "timeout" + client_error = "client_error" + model_unavailable = "model_unavailable" + model_not_ready = "model_not_ready" + model_does_not_exist = "model_does_not_exist" + rate_limited = "rate_limited" + payload_too_large = "payload_too_large" + unauthorized = "unauthorized" + application_error = "application_error" + internal_baseten_error = "internal_baseten_error" + + +class ErrorResponse(BaseModel): + error: Annotated[str | None, Field(description="Human-readable error message.")] = ( + None + ) + error_code: Annotated[ + ErrorCode | None, Field(description="Machine-readable error code.") + ] = None + detail: Annotated[ + str | None, Field(description="Additional error details, if available.") + ] = None + + +class PredictOutput(RootModel[dict[str, Any]]): + root: dict[str, Any] + + +class RunRemoteOutput(RootModel[dict[str, Any]]): + root: dict[str, Any] + + +class AsyncPredictOutput(BaseModel): + request_id: Annotated[str, Field(description="The ID of the async request.")] + + +class AsyncRunRemoteOutput(BaseModel): + request_id: Annotated[str, Field(description="The ID of the async request.")] + + +class CancelAsyncRequestOutput(BaseModel): + request_id: Annotated[str, Field(description="The ID of the async request.")] + canceled: Annotated[bool, Field(description="Whether the request was canceled.")] + message: Annotated[ + str, + Field(description="Additional details about whether the request was canceled."), + ] + + +class GetAsyncQueueStatusOutput(BaseModel): + model_id: Annotated[str, Field(description="The ID of the model.")] + deployment_id: Annotated[str, Field(description="The ID of the deployment.")] + num_queued_requests: Annotated[ + int, + Field(description="Number of requests with QUEUED status awaiting processing."), + ] + num_in_progress_requests: Annotated[ + int, + Field(description="Number of requests currently being processed by the model."), + ] + + +class PredictInput(RootModel[dict[str, Any]]): + root: dict[str, Any] + + +class RunRemoteInput(RootModel[dict[str, Any]]): + root: dict[str, Any] + + +class AsyncRunRemoteInput(RootModel[dict[str, Any]]): + root: dict[str, Any] + + +class AsyncPredictRequest(BaseModel): + model_input: Annotated[ + dict[str, Any], Field(description="JSON-serializable model input.") + ] + webhook_endpoint: Annotated[ + AnyUrl | None, + Field( + description="HTTPS URL to receive the prediction result via webhook. Both HTTP/2 and HTTP/1.1 are supported. If omitted, the model must save outputs so they can be accessed later." + ), + ] = None + priority: Annotated[ + int | None, + Field( + description="Priority of the request. Lower values are higher priority.", + ge=0, + le=2, + ), + ] = 0 + max_time_in_queue_seconds: Annotated[ + int | None, + Field( + description="Maximum time in seconds a request will spend in the queue before expiring. Must be between 10 seconds and 72 hours.", + ge=10, + le=259200, + ), + ] = 600 + inference_retry_config: InferenceRetryConfig | None = None + + +class AsyncRequestStatusResponse(BaseModel): + request_id: Annotated[str, Field(description="The ID of the async request.")] + model_id: Annotated[ + str | None, + Field( + description="The ID of the model that executed the request. Present for model requests." + ), + ] = None + chain_id: Annotated[ + str | None, + Field( + description="The ID of the chain that executed the request. Present for chain requests." + ), + ] = None + deployment_id: Annotated[ + str, Field(description="The ID of the deployment that executed the request.") + ] + status: Annotated[Status, Field(description="The status of the async request.")] + webhook_status: Annotated[ + WebhookStatus, + Field( + description="The status of sending the prediction result to the provided webhook." + ), + ] + created_at: Annotated[ + str, + Field(description="The time in UTC at which the async request was created."), + ] + status_at: Annotated[ + str, + Field( + description="The time in UTC at which the async request's status was last updated." + ), + ] + errors: Annotated[ + list[AsyncRequestError], + Field( + default_factory=list, + description="Errors that occurred while processing the async request. Empty if no errors occurred.", + ), + ] diff --git a/baseten/client/managementapi/__init__.py b/baseten/client/managementapi/__init__.py new file mode 100644 index 0000000..9af74bd --- /dev/null +++ b/baseten/client/managementapi/__init__.py @@ -0,0 +1,379 @@ +# Code generated by apigen. DO NOT EDIT. + +"""Generated client and models for the Baseten Management API. + +Use :class:`ApiClient` for synchronous access or :class:`AsyncApiClient` for +asynchronous access. + +Types in this module are generated from the OpenAPI specification and are NOT +covered by any stability or compatibility guarantees. They may change without +notice between versions. +""" + +from ._client import ApiClient, AsyncApiClient, ResponseError +from ._models import ( + APIKey, + APIKeyCategory, + APIKeyInfo, + APIKeyTombstone, + APIKeys, + AWSCredentials, + ActivateResponse, + AuthCode, + AutoscalingSettings, + AwsIamDockerAuth, + AwsOidcDockerAuth, + BasetenLatestCheckpointConfig, + BasetenNamedCheckpointConfig, + BillableResource, + CancelPromotionResponse, + CancelPromotionStatus, + Chain, + ChainDeployment, + ChainDeploymentTombstone, + ChainDeployments, + ChainEnvironment, + ChainMetadata, + ChainTombstone, + Chainlet, + ChainletEnvironmentAutoscalingSettingsUpdate, + ChainletEnvironmentInstanceTypeUpdate, + ChainletEnvironmentSettings, + ChainletEnvironmentSettingsRequest, + Chains, + CheckpointFile, + CheckpointSyncStatus, + Checkpoints, + CreateAPIKeyRequest, + CreateChainEnvironmentRequest, + CreateEnvironmentRequest, + CreateJobWeightConfig, + CreateLLMModelRequest, + CreateLLMModelVersionRequest, + CreateLibraryListingRequest, + CreateLibraryListingVersionRequest, + CreateModelWeightSnapshotRequest, + CreateTrainingJob, + CreateTrainingJobAccelerator, + CreateTrainingJobCacheConfig, + CreateTrainingJobCheckpointingConfig, + CreateTrainingJobCompute, + CreateTrainingJobImage, + CreateTrainingJobRequest, + CreateTrainingJobResponse, + CreateTrainingJobRuntime, + CreateTrainingJobS3Artifact, + CreditsUsed, + DailyDedicatedUsage, + DailyModelApiUsage, + DailyTrainingUsage, + DeactivateResponse, + DedicatedItem, + DedicatedUsage, + Deployment, + DeploymentStatus, + DeploymentTombstone, + Deployments, + DockerAuth, + DockerAuthType, + DownloadTrainingJobResponse, + Environment, + Environments, + FileSummary, + GcpOidcDockerAuth, + GcpServiceAccountJsonDockerAuth, + GetAuthCodesResponse, + GetBlobCredentialsResponse, + GetCacheSummaryResponse, + GetDeploymentLogsRequest, + GetLogsResponse, + GetTrainingJobCheckpointFilesResponse, + GetTrainingJobCheckpointsResponse, + GetTrainingJobLogsRequest, + GetTrainingJobMetricsRequest, + GetTrainingJobMetricsResponse, + GetTrainingJobResponse, + GetTrainingProjectResponse, + GitInfo, + InProgressPromotion, + InProgressPromotionStatus, + InstanceType, + InstanceTypePrices, + InstanceTypeWithPrice, + InstanceTypes, + InteractiveSession, + InteractiveSessionConfig, + LLMModel, + LLMModelVersion, + LibraryListing, + LibraryListingTombstone, + LibraryListingVersion, + LibraryListingVersionTombstone, + LibraryListingVersions, + LibraryListings, + Limit, + ListTrainingJobsResponse, + ListTrainingProjectsResponse, + LoadCheckpointConfig, + Log, + Model, + ModelApiItem, + ModelApisUsage, + ModelTombstone, + ModelWeightSnapshot, + Models, + OrderBy, + PatchInteractiveSessionRequest, + PatchInteractiveSessionResponse, + PromoteRequest, + PromoteToChainEnvironmentRequest, + PromoteToEnvironmentRequest, + PromotionCleanupStrategy, + PromotionSettings, + RecreateTrainingJobResponse, + RegistrySecretDockerAuth, + ResourceKind, + RetryDeploymentResponse, + RollingDeployConfig, + RollingDeployStrategy, + SearchTrainingJobsRequest, + SearchTrainingJobsResponse, + Secret, + SecretReference, + Secrets, + SignalPromotionResponse, + SortOrder, + StopTrainingJobRequest, + StopTrainingJobResponse, + StorageMetrics, + Subtotal, + Subtotal3, + Subtotal4, + Subtotal5, + Subtotal6, + Subtotal7, + Subtotal8, + Team, + Teams, + TerminateReplicaResponse, + Total, + TrainingItem, + TrainingJob, + TrainingJobCheckpoint, + TrainingJobMetric, + TrainingJobMetrics, + TrainingJobNodeMetrics, + TrainingJobTombstone, + TrainingProject, + TrainingProjectSummary, + TrainingProjectTombstone, + TrainingUsage, + TrussUserEnv, + UpdateAutoscalingSettings, + UpdateAutoscalingSettingsResponse, + UpdateAutoscalingSettingsStatus, + UpdateChainEnvironmentRequest, + UpdateChainEnvironmentResponse, + UpdateChainletEnvironmentAutoscalingSettingsRequest, + UpdateChainletEnvironmentInstanceTypeRequest, + UpdateChainletEnvironmentInstanceTypeResponse, + UpdateEnvironmentRequest, + UpdateLibraryListingRequest, + UpdateLibraryListingVersionRequest, + UpdatePromotionSettings, + UpdateRollingDeployConfig, + UpsertSecretRequest, + UpsertTrainingProject, + UpsertTrainingProjectRequest, + UpsertTrainingProjectResponse, + UsageSummary, + User, + V1InteractiveSessionAuthProvider, + V1InteractiveSessionProvider, + V1InteractiveSessionTrigger, +) + +__all__ = [ + "ApiClient", + "AsyncApiClient", + "ResponseError", + "APIKey", + "APIKeyCategory", + "APIKeyInfo", + "APIKeyTombstone", + "APIKeys", + "AWSCredentials", + "ActivateResponse", + "AuthCode", + "AutoscalingSettings", + "AwsIamDockerAuth", + "AwsOidcDockerAuth", + "BasetenLatestCheckpointConfig", + "BasetenNamedCheckpointConfig", + "BillableResource", + "CancelPromotionResponse", + "CancelPromotionStatus", + "Chain", + "ChainDeployment", + "ChainDeploymentTombstone", + "ChainDeployments", + "ChainEnvironment", + "ChainMetadata", + "ChainTombstone", + "Chainlet", + "ChainletEnvironmentAutoscalingSettingsUpdate", + "ChainletEnvironmentInstanceTypeUpdate", + "ChainletEnvironmentSettings", + "ChainletEnvironmentSettingsRequest", + "Chains", + "CheckpointFile", + "CheckpointSyncStatus", + "Checkpoints", + "CreateAPIKeyRequest", + "CreateChainEnvironmentRequest", + "CreateEnvironmentRequest", + "CreateJobWeightConfig", + "CreateLLMModelRequest", + "CreateLLMModelVersionRequest", + "CreateLibraryListingRequest", + "CreateLibraryListingVersionRequest", + "CreateModelWeightSnapshotRequest", + "CreateTrainingJob", + "CreateTrainingJobAccelerator", + "CreateTrainingJobCacheConfig", + "CreateTrainingJobCheckpointingConfig", + "CreateTrainingJobCompute", + "CreateTrainingJobImage", + "CreateTrainingJobRequest", + "CreateTrainingJobResponse", + "CreateTrainingJobRuntime", + "CreateTrainingJobS3Artifact", + "CreditsUsed", + "DailyDedicatedUsage", + "DailyModelApiUsage", + "DailyTrainingUsage", + "DeactivateResponse", + "DedicatedItem", + "DedicatedUsage", + "Deployment", + "DeploymentStatus", + "DeploymentTombstone", + "Deployments", + "DockerAuth", + "DockerAuthType", + "DownloadTrainingJobResponse", + "Environment", + "Environments", + "FileSummary", + "GcpOidcDockerAuth", + "GcpServiceAccountJsonDockerAuth", + "GetAuthCodesResponse", + "GetBlobCredentialsResponse", + "GetCacheSummaryResponse", + "GetDeploymentLogsRequest", + "GetLogsResponse", + "GetTrainingJobCheckpointFilesResponse", + "GetTrainingJobCheckpointsResponse", + "GetTrainingJobLogsRequest", + "GetTrainingJobMetricsRequest", + "GetTrainingJobMetricsResponse", + "GetTrainingJobResponse", + "GetTrainingProjectResponse", + "GitInfo", + "InProgressPromotion", + "InProgressPromotionStatus", + "InstanceType", + "InstanceTypePrices", + "InstanceTypeWithPrice", + "InstanceTypes", + "InteractiveSession", + "InteractiveSessionConfig", + "LLMModel", + "LLMModelVersion", + "LibraryListing", + "LibraryListingTombstone", + "LibraryListingVersion", + "LibraryListingVersionTombstone", + "LibraryListingVersions", + "LibraryListings", + "Limit", + "ListTrainingJobsResponse", + "ListTrainingProjectsResponse", + "LoadCheckpointConfig", + "Log", + "Model", + "ModelApiItem", + "ModelApisUsage", + "ModelTombstone", + "ModelWeightSnapshot", + "Models", + "OrderBy", + "PatchInteractiveSessionRequest", + "PatchInteractiveSessionResponse", + "PromoteRequest", + "PromoteToChainEnvironmentRequest", + "PromoteToEnvironmentRequest", + "PromotionCleanupStrategy", + "PromotionSettings", + "RecreateTrainingJobResponse", + "RegistrySecretDockerAuth", + "ResourceKind", + "RetryDeploymentResponse", + "RollingDeployConfig", + "RollingDeployStrategy", + "SearchTrainingJobsRequest", + "SearchTrainingJobsResponse", + "Secret", + "SecretReference", + "Secrets", + "SignalPromotionResponse", + "SortOrder", + "StopTrainingJobRequest", + "StopTrainingJobResponse", + "StorageMetrics", + "Subtotal", + "Subtotal3", + "Subtotal4", + "Subtotal5", + "Subtotal6", + "Subtotal7", + "Subtotal8", + "Team", + "Teams", + "TerminateReplicaResponse", + "Total", + "TrainingItem", + "TrainingJob", + "TrainingJobCheckpoint", + "TrainingJobMetric", + "TrainingJobMetrics", + "TrainingJobNodeMetrics", + "TrainingJobTombstone", + "TrainingProject", + "TrainingProjectSummary", + "TrainingProjectTombstone", + "TrainingUsage", + "TrussUserEnv", + "UpdateAutoscalingSettings", + "UpdateAutoscalingSettingsResponse", + "UpdateAutoscalingSettingsStatus", + "UpdateChainEnvironmentRequest", + "UpdateChainEnvironmentResponse", + "UpdateChainletEnvironmentAutoscalingSettingsRequest", + "UpdateChainletEnvironmentInstanceTypeRequest", + "UpdateChainletEnvironmentInstanceTypeResponse", + "UpdateEnvironmentRequest", + "UpdateLibraryListingRequest", + "UpdateLibraryListingVersionRequest", + "UpdatePromotionSettings", + "UpdateRollingDeployConfig", + "UpsertSecretRequest", + "UpsertTrainingProject", + "UpsertTrainingProjectRequest", + "UpsertTrainingProjectResponse", + "UsageSummary", + "User", + "V1InteractiveSessionAuthProvider", + "V1InteractiveSessionProvider", + "V1InteractiveSessionTrigger", +] diff --git a/baseten/client/managementapi/_client.py b/baseten/client/managementapi/_client.py new file mode 100644 index 0000000..b21f9b7 --- /dev/null +++ b/baseten/client/managementapi/_client.py @@ -0,0 +1,3339 @@ +# Code generated by apigen/clientgen. DO NOT EDIT. + +from __future__ import annotations + +import urllib.parse +from dataclasses import dataclass +from typing import Any, TypeVar + +import httpx +from pydantic import BaseModel + +from ._models import ( + APIKey, + APIKeyTombstone, + APIKeys, + ActivateResponse, + CancelPromotionResponse, + Chain, + ChainDeployment, + ChainDeploymentTombstone, + ChainDeployments, + ChainEnvironment, + ChainTombstone, + Chains, + CreateAPIKeyRequest, + CreateChainEnvironmentRequest, + CreateEnvironmentRequest, + CreateLLMModelRequest, + CreateLLMModelVersionRequest, + CreateLibraryListingRequest, + CreateLibraryListingVersionRequest, + CreateModelWeightSnapshotRequest, + CreateTrainingJobRequest, + CreateTrainingJobResponse, + DeactivateResponse, + Deployment, + DeploymentTombstone, + Deployments, + DownloadTrainingJobResponse, + Environment, + Environments, + GetAuthCodesResponse, + GetBlobCredentialsResponse, + GetCacheSummaryResponse, + GetDeploymentLogsRequest, + GetLogsResponse, + GetTrainingJobCheckpointFilesResponse, + GetTrainingJobCheckpointsResponse, + GetTrainingJobLogsRequest, + GetTrainingJobMetricsRequest, + GetTrainingJobMetricsResponse, + GetTrainingJobResponse, + GetTrainingProjectResponse, + InstanceTypePrices, + InstanceTypes, + LLMModel, + LLMModelVersion, + LibraryListing, + LibraryListingTombstone, + LibraryListingVersion, + LibraryListingVersionTombstone, + LibraryListingVersions, + LibraryListings, + ListTrainingJobsResponse, + ListTrainingProjectsResponse, + Model, + ModelTombstone, + ModelWeightSnapshot, + Models, + PatchInteractiveSessionRequest, + PatchInteractiveSessionResponse, + PromoteRequest, + PromoteToChainEnvironmentRequest, + PromoteToEnvironmentRequest, + RecreateTrainingJobResponse, + RetryDeploymentResponse, + SearchTrainingJobsRequest, + SearchTrainingJobsResponse, + Secret, + Secrets, + SignalPromotionResponse, + StopTrainingJobRequest, + StopTrainingJobResponse, + Teams, + TerminateReplicaResponse, + TrainingJobTombstone, + TrainingProjectTombstone, + UpdateAutoscalingSettings, + UpdateAutoscalingSettingsResponse, + UpdateChainEnvironmentRequest, + UpdateChainEnvironmentResponse, + UpdateChainletEnvironmentAutoscalingSettingsRequest, + UpdateChainletEnvironmentInstanceTypeRequest, + UpdateChainletEnvironmentInstanceTypeResponse, + UpdateEnvironmentRequest, + UpdateLibraryListingRequest, + UpdateLibraryListingVersionRequest, + UpsertSecretRequest, + UpsertTrainingProjectRequest, + UpsertTrainingProjectResponse, + UsageSummary, +) + +_T = TypeVar("_T", bound=BaseModel) + + +@dataclass +class ResponseError(Exception): + status_code: int + body: str + + def __str__(self) -> str: + return f"baseten API error (HTTP {self.status_code}): {self.body}" + + +@dataclass +class _ApiRequest: + method: str + path_fmt: str + path_args: list[str] + body: Any + success_code: int + error_codes: dict[int, str] | None + + +class ApiClient: + """Generated HTTP client for the Baseten API. + + Methods on this client are generated from the OpenAPI specification + and are NOT covered by any stability or compatibility guarantees. + They may change without notice between versions. + """ + + def __init__(self, http_client: httpx.Client) -> None: + """Create a new client. The caller is responsible for closing *http_client*.""" + self._http_client = http_client + + def delete_api_keys(self, *, api_key_prefix: str) -> APIKeyTombstone: + """Deletes an API key by prefix""" + return self._do_json( + APIKeyTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/api_keys/{}", + path_args=[api_key_prefix], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_chains(self, *, chain_id: str) -> ChainTombstone: + """Deletes a chain by ID""" + return self._do_json( + ChainTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/chains/{}", + path_args=[chain_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_chains_deployments( + self, *, chain_id: str, chain_deployment_id: str + ) -> ChainDeploymentTombstone: + """Deletes a chain deployment by ID""" + return self._do_json( + ChainDeploymentTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/chains/{}/deployments/{}", + path_args=[chain_id, chain_deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_library_listings( + self, *, user_defined_listing_id: str + ) -> LibraryListingTombstone: + """Deletes a library listing""" + return self._do_json( + LibraryListingTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/library_listings/{}", + path_args=[user_defined_listing_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_library_listings_versions( + self, *, user_defined_listing_id: str, version_tag: str + ) -> LibraryListingVersionTombstone: + """Deletes a library listing version""" + return self._do_json( + LibraryListingVersionTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/library_listings/{}/versions/{}", + path_args=[user_defined_listing_id, version_tag], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_models(self, *, model_id: str) -> ModelTombstone: + """Deletes a model by ID""" + return self._do_json( + ModelTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/models/{}", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_models_deployments( + self, *, model_id: str, deployment_id: str + ) -> DeploymentTombstone: + """Deletes a model's deployment by ID""" + return self._do_json( + DeploymentTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/models/{}/deployments/{}", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_models_deployments_replicas( + self, *, model_id: str, deployment_id: str, replica_id: str + ) -> TerminateReplicaResponse: + """Terminates a replica in a deployment""" + return self._do_json( + TerminateReplicaResponse, + _ApiRequest( + method="DELETE", + path_fmt="/v1/models/{}/deployments/{}/replicas/{}", + path_args=[model_id, deployment_id, replica_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_training_projects( + self, *, training_project_id: str + ) -> TrainingProjectTombstone: + """Delete a training project.""" + return self._do_json( + TrainingProjectTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/training_projects/{}", + path_args=[training_project_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def delete_training_projects_jobs( + self, *, training_project_id: str, training_job_id: str + ) -> TrainingJobTombstone: + """Delete a training job.""" + return self._do_json( + TrainingJobTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/training_projects/{}/jobs/{}", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_api_keys(self) -> APIKeys: + """Lists the user's API keys""" + return self._do_json( + APIKeys, + _ApiRequest( + method="GET", + path_fmt="/v1/api_keys", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_billing_usage_summary(self) -> UsageSummary: + """Gets billing usage summary for a date range""" + return self._do_json( + UsageSummary, + _ApiRequest( + method="GET", + path_fmt="/v1/billing/usage_summary", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_blobs_credentials_model(self) -> GetBlobCredentialsResponse: + """Get blob credentials for models.""" + return self._do_json( + GetBlobCredentialsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/blobs/credentials/model", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_blobs_credentials_train(self) -> GetBlobCredentialsResponse: + """Get blob credentials for training.""" + return self._do_json( + GetBlobCredentialsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/blobs/credentials/train", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_chains(self) -> Chains: + """Gets all chains""" + return self._do_json( + Chains, + _ApiRequest( + method="GET", + path_fmt="/v1/chains", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_chains_chain_id(self, *, chain_id: str) -> Chain: + """Gets a chain by ID""" + return self._do_json( + Chain, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}", + path_args=[chain_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_chains_deployments(self, *, chain_id: str) -> ChainDeployments: + """Gets all chain deployments""" + return self._do_json( + ChainDeployments, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}/deployments", + path_args=[chain_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_chains_deployments_chain_deployment_id( + self, *, chain_id: str, chain_deployment_id: str + ) -> ChainDeployment: + """Gets a chain deployment by ID""" + return self._do_json( + ChainDeployment, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}/deployments/{}", + path_args=[chain_id, chain_deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_chains_environments(self, *, chain_id: str) -> Environments: + """Get all chain environments""" + return self._do_json( + Environments, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}/environments", + path_args=[chain_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_chains_environments_env_name( + self, *, chain_id: str, env_name: str + ) -> ChainEnvironment: + """Get a chain environment's details""" + return self._do_json( + ChainEnvironment, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}/environments/{}", + path_args=[chain_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_instance_type_prices(self) -> InstanceTypePrices: + """Gets prices for available instance types.""" + return self._do_json( + InstanceTypePrices, + _ApiRequest( + method="GET", + path_fmt="/v1/instance_type_prices", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_instance_types(self) -> InstanceTypes: + """Gets all available instance types""" + return self._do_json( + InstanceTypes, + _ApiRequest( + method="GET", + path_fmt="/v1/instance_types", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_library_listings(self) -> LibraryListings: + """Gets all library listings""" + return self._do_json( + LibraryListings, + _ApiRequest( + method="GET", + path_fmt="/v1/library_listings", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_library_listings_user_defined_listing_id( + self, *, user_defined_listing_id: str + ) -> LibraryListing: + """Gets a library listing""" + return self._do_json( + LibraryListing, + _ApiRequest( + method="GET", + path_fmt="/v1/library_listings/{}", + path_args=[user_defined_listing_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_library_listings_versions( + self, *, user_defined_listing_id: str + ) -> LibraryListingVersions: + """Gets all versions for a library listing""" + return self._do_json( + LibraryListingVersions, + _ApiRequest( + method="GET", + path_fmt="/v1/library_listings/{}/versions", + path_args=[user_defined_listing_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_library_listings_versions_version_tag( + self, *, user_defined_listing_id: str, version_tag: str + ) -> LibraryListingVersion: + """Gets a library listing version""" + return self._do_json( + LibraryListingVersion, + _ApiRequest( + method="GET", + path_fmt="/v1/library_listings/{}/versions/{}", + path_args=[user_defined_listing_id, version_tag], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_model_apis_snapshots(self) -> ModelWeightSnapshot: + """Get the latest model weight snapshot""" + return self._do_json( + ModelWeightSnapshot, + _ApiRequest( + method="GET", + path_fmt="/v1/model_apis/snapshots", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_model_apis_snapshots_model_id( + self, *, model_id: str + ) -> ModelWeightSnapshot: + """Get the latest model weight snapshot""" + return self._do_json( + ModelWeightSnapshot, + _ApiRequest( + method="GET", + path_fmt="/v1/model_apis/snapshots/{}", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_models(self) -> Models: + """Gets all models""" + return self._do_json( + Models, + _ApiRequest( + method="GET", + path_fmt="/v1/models", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_models_deployments(self, *, model_id: str) -> Deployments: + """Gets all deployments of a model""" + return self._do_json( + Deployments, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/deployments", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_models_deployments_deployment_id( + self, *, model_id: str, deployment_id: str + ) -> Deployment: + """Gets a model's deployment by ID""" + return self._do_json( + Deployment, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/deployments/{}", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_models_deployments_development(self, *, model_id: str) -> Deployment: + """Gets a model's development deployment""" + return self._do_json( + Deployment, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/deployments/development", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_models_deployments_production(self, *, model_id: str) -> Deployment: + """Gets a model's production deployment""" + return self._do_json( + Deployment, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/deployments/production", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_models_environments(self, *, model_id: str) -> Environments: + """Get all environments""" + return self._do_json( + Environments, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/environments", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_models_environments_env_name( + self, *, model_id: str, env_name: str + ) -> Environment: + """Get an environment's details""" + return self._do_json( + Environment, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/environments/{}", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_models_model_id(self, *, model_id: str) -> Model: + """Gets a model by ID""" + return self._do_json( + Model, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_secrets(self) -> Secrets: + """Gets all secrets""" + return self._do_json( + Secrets, + _ApiRequest( + method="GET", + path_fmt="/v1/secrets", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_teams(self) -> Teams: + """Lists all teams""" + return self._do_json( + Teams, + _ApiRequest( + method="GET", + path_fmt="/v1/teams", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_teams_secrets(self, *, team_id: str) -> Secrets: + """Gets all secrets for a team""" + return self._do_json( + Secrets, + _ApiRequest( + method="GET", + path_fmt="/v1/teams/{}/secrets", + path_args=[team_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects(self) -> ListTrainingProjectsResponse: + """List training projects.""" + return self._do_json( + ListTrainingProjectsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects_cache_summary( + self, *, training_project_id: str + ) -> GetCacheSummaryResponse: + """Get training project cache summary.""" + return self._do_json( + GetCacheSummaryResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/cache/summary", + path_args=[training_project_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects_jobs( + self, *, training_project_id: str + ) -> ListTrainingJobsResponse: + """List training jobs.""" + return self._do_json( + ListTrainingJobsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs", + path_args=[training_project_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects_jobs_auth_codes( + self, *, training_project_id: str, training_job_id: str + ) -> GetAuthCodesResponse: + """Get auth codes for a training job.""" + return self._do_json( + GetAuthCodesResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}/auth_codes", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects_jobs_checkpoint_files( + self, *, training_project_id: str, training_job_id: str + ) -> GetTrainingJobCheckpointFilesResponse: + """Get training job checkpoint files.""" + return self._do_json( + GetTrainingJobCheckpointFilesResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}/checkpoint_files", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects_jobs_checkpoints( + self, *, training_project_id: str, training_job_id: str + ) -> GetTrainingJobCheckpointsResponse: + """Get training job checkpoints.""" + return self._do_json( + GetTrainingJobCheckpointsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}/checkpoints", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects_jobs_download( + self, *, training_project_id: str, training_job_id: str + ) -> DownloadTrainingJobResponse: + """Get the uploaded training job as a S3 Artifact""" + return self._do_json( + DownloadTrainingJobResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}/download", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects_jobs_training_job_id( + self, *, training_project_id: str, training_job_id: str + ) -> GetTrainingJobResponse: + """Get a training job.""" + return self._do_json( + GetTrainingJobResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def get_training_projects_training_project_id( + self, *, training_project_id: str + ) -> GetTrainingProjectResponse: + """Get a training project.""" + return self._do_json( + GetTrainingProjectResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}", + path_args=[training_project_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def patch_chains_environments( + self, *, chain_id: str, env_name: str, body: UpdateChainEnvironmentRequest + ) -> UpdateChainEnvironmentResponse: + """Update a chain environment's settings""" + return self._do_json( + UpdateChainEnvironmentResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/chains/{}/environments/{}", + path_args=[chain_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def patch_chains_environments_chainlet_settings_autoscaling_settings( + self, + *, + chain_id: str, + env_name: str, + body: UpdateChainletEnvironmentAutoscalingSettingsRequest, + ) -> UpdateAutoscalingSettingsResponse: + """Update a chainlet environment's autoscaling settings""" + return self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/chains/{}/environments/{}/chainlet_settings/autoscaling_settings", + path_args=[chain_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def patch_library_listings( + self, *, user_defined_listing_id: str, body: UpdateLibraryListingRequest + ) -> LibraryListing: + """Updates a library listing""" + return self._do_json( + LibraryListing, + _ApiRequest( + method="PATCH", + path_fmt="/v1/library_listings/{}", + path_args=[user_defined_listing_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def patch_library_listings_versions( + self, + *, + user_defined_listing_id: str, + version_tag: str, + body: UpdateLibraryListingVersionRequest, + ) -> LibraryListingVersion: + """Updates a library listing version""" + return self._do_json( + LibraryListingVersion, + _ApiRequest( + method="PATCH", + path_fmt="/v1/library_listings/{}/versions/{}", + path_args=[user_defined_listing_id, version_tag], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def patch_models_deployments_autoscaling_settings( + self, *, model_id: str, deployment_id: str, body: UpdateAutoscalingSettings + ) -> UpdateAutoscalingSettingsResponse: + """Updates a deployment's autoscaling settings""" + return self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/models/{}/deployments/{}/autoscaling_settings", + path_args=[model_id, deployment_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def patch_models_deployments_development_autoscaling_settings( + self, *, model_id: str, body: UpdateAutoscalingSettings + ) -> UpdateAutoscalingSettingsResponse: + """Updates a development deployment's autoscaling settings""" + return self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/models/{}/deployments/development/autoscaling_settings", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def patch_models_deployments_production_autoscaling_settings( + self, *, model_id: str, body: UpdateAutoscalingSettings + ) -> UpdateAutoscalingSettingsResponse: + """Updates a production deployment's autoscaling settings""" + return self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/models/{}/deployments/production/autoscaling_settings", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def patch_models_environments( + self, *, model_id: str, env_name: str, body: UpdateEnvironmentRequest + ) -> UpdateAutoscalingSettingsResponse: + """Update an environment's settings""" + return self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/models/{}/environments/{}", + path_args=[model_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def patch_training_projects_jobs_interactive_sessions( + self, + *, + training_project_id: str, + training_job_id: str, + session_id: str, + body: PatchInteractiveSessionRequest, + ) -> PatchInteractiveSessionResponse: + """Patch an interactive session.""" + return self._do_json( + PatchInteractiveSessionResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/training_projects/{}/jobs/{}/interactive_sessions/{}", + path_args=[training_project_id, training_job_id, session_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_api_keys(self, *, body: CreateAPIKeyRequest) -> APIKey: + """Creates an API key""" + return self._do_json( + APIKey, + _ApiRequest( + method="POST", + path_fmt="/v1/api_keys", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_chains_deployments_deactivate( + self, *, chain_id: str, chain_deployment_id: str + ) -> DeactivateResponse: + """Deactivates a chain deployment""" + return self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/chains/{}/deployments/{}/deactivate", + path_args=[chain_id, chain_deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_chains_environments( + self, *, chain_id: str, body: CreateChainEnvironmentRequest + ) -> ChainEnvironment: + """Create a chain environment""" + return self._do_json( + ChainEnvironment, + _ApiRequest( + method="POST", + path_fmt="/v1/chains/{}/environments", + path_args=[chain_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_chains_environments_chainlet_settings_instance_types_update( + self, + *, + chain_id: str, + env_name: str, + body: UpdateChainletEnvironmentInstanceTypeRequest, + ) -> UpdateChainletEnvironmentInstanceTypeResponse: + """Update a chainlet environment's instance type settings.""" + return self._do_json( + UpdateChainletEnvironmentInstanceTypeResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/chains/{}/environments/{}/chainlet_settings/instance_types/update", + path_args=[chain_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_chains_environments_promote( + self, *, chain_id: str, env_name: str, body: PromoteToChainEnvironmentRequest + ) -> ChainDeployment: + """Promotes a chain deployment to an environment""" + return self._do_json( + ChainDeployment, + _ApiRequest( + method="POST", + path_fmt="/v1/chains/{}/environments/{}/promote", + path_args=[chain_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_library_listings( + self, *, body: CreateLibraryListingRequest + ) -> LibraryListing: + """Creates a new library listing""" + return self._do_json( + LibraryListing, + _ApiRequest( + method="POST", + path_fmt="/v1/library_listings", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_library_listings_versions( + self, *, user_defined_listing_id: str, body: CreateLibraryListingVersionRequest + ) -> LibraryListingVersion: + """Creates a new library listing version""" + return self._do_json( + LibraryListingVersion, + _ApiRequest( + method="POST", + path_fmt="/v1/library_listings/{}/versions", + path_args=[user_defined_listing_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_llm_models(self, *, body: CreateLLMModelRequest) -> LLMModel: + """Creates a new BIS LLM deployment""" + return self._do_json( + LLMModel, + _ApiRequest( + method="POST", + path_fmt="/v1/llm_models", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_llm_models_deployments( + self, *, model_id: str, body: CreateLLMModelVersionRequest + ) -> LLMModelVersion: + """Creates a new BIS LLM deployment version""" + return self._do_json( + LLMModelVersion, + _ApiRequest( + method="POST", + path_fmt="/v1/llm_models/{}/deployments", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_model_apis_snapshots( + self, *, body: CreateModelWeightSnapshotRequest + ) -> ModelWeightSnapshot: + """Create a model weight snapshot""" + return self._do_json( + ModelWeightSnapshot, + _ApiRequest( + method="POST", + path_fmt="/v1/model_apis/snapshots", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_model_apis_snapshots_model_id( + self, *, model_id: str, body: CreateModelWeightSnapshotRequest + ) -> ModelWeightSnapshot: + """Create a model weight snapshot""" + return self._do_json( + ModelWeightSnapshot, + _ApiRequest( + method="POST", + path_fmt="/v1/model_apis/snapshots/{}", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_activate( + self, *, model_id: str, deployment_id: str + ) -> ActivateResponse: + """Activates a deployment""" + return self._do_json( + ActivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/activate", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_deactivate( + self, *, model_id: str, deployment_id: str + ) -> DeactivateResponse: + """Deactivates a deployment""" + return self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/deactivate", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_development_activate( + self, *, model_id: str + ) -> ActivateResponse: + """Activates a development deployment""" + return self._do_json( + ActivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/development/activate", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_development_deactivate( + self, *, model_id: str + ) -> DeactivateResponse: + """Deactivates a development deployment""" + return self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/development/deactivate", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_development_promote( + self, *, model_id: str, body: PromoteRequest + ) -> Deployment: + """Promotes a development deployment to production""" + return self._do_json( + Deployment, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/development/promote", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_development_retry( + self, *, model_id: str + ) -> RetryDeploymentResponse: + """Retries a failed development deployment""" + return self._do_json( + RetryDeploymentResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/development/retry", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_logs( + self, *, model_id: str, deployment_id: str, body: GetDeploymentLogsRequest + ) -> GetLogsResponse: + """Gets the logs for a model deployment.""" + return self._do_json( + GetLogsResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/logs", + path_args=[model_id, deployment_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_production_activate( + self, *, model_id: str + ) -> ActivateResponse: + """Activates a production deployment""" + return self._do_json( + ActivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/production/activate", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_production_deactivate( + self, *, model_id: str + ) -> DeactivateResponse: + """Deactivates a production deployment""" + return self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/production/deactivate", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_production_retry( + self, *, model_id: str + ) -> RetryDeploymentResponse: + """Retries a failed production deployment""" + return self._do_json( + RetryDeploymentResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/production/retry", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_promote( + self, *, model_id: str, deployment_id: str, body: PromoteRequest + ) -> Deployment: + """Promotes a deployment to production""" + return self._do_json( + Deployment, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/promote", + path_args=[model_id, deployment_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_models_deployments_retry( + self, *, model_id: str, deployment_id: str + ) -> RetryDeploymentResponse: + """Retries a failed deployment""" + return self._do_json( + RetryDeploymentResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/retry", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments( + self, *, model_id: str, body: CreateEnvironmentRequest + ) -> Environment: + """Create an environment""" + return self._do_json( + Environment, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments_activate( + self, *, model_id: str, env_name: str + ) -> ActivateResponse: + """Activates a deployment associated with an environment""" + return self._do_json( + ActivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/activate", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments_cancel_promotion( + self, *, model_id: str, env_name: str + ) -> CancelPromotionResponse: + """Cancels a promotion to an environment""" + return self._do_json( + CancelPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/cancel_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments_deactivate( + self, *, model_id: str, env_name: str + ) -> DeactivateResponse: + """Deactivates a deployment associated with an environment""" + return self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/deactivate", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments_force_cancel_promotion( + self, *, model_id: str, env_name: str + ) -> SignalPromotionResponse: + """Force cancels a rolling promotion""" + return self._do_json( + SignalPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/force_cancel_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments_force_roll_forward_promotion( + self, *, model_id: str, env_name: str + ) -> SignalPromotionResponse: + """Force rolls forward a rolling promotion""" + return self._do_json( + SignalPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/force_roll_forward_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments_pause_promotion( + self, *, model_id: str, env_name: str + ) -> SignalPromotionResponse: + """Pauses a rolling promotion""" + return self._do_json( + SignalPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/pause_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments_promote( + self, *, model_id: str, env_name: str, body: PromoteToEnvironmentRequest + ) -> Deployment: + """Promotes a deployment to an environment""" + return self._do_json( + Deployment, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/promote", + path_args=[model_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_models_environments_resume_promotion( + self, *, model_id: str, env_name: str + ) -> SignalPromotionResponse: + """Resumes a paused rolling promotion""" + return self._do_json( + SignalPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/resume_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_secrets(self, *, body: UpsertSecretRequest) -> Secret: + """Upserts a secret""" + return self._do_json( + Secret, + _ApiRequest( + method="POST", + path_fmt="/v1/secrets", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_teams_api_keys(self, *, team_id: str, body: CreateAPIKeyRequest) -> APIKey: + """Creates a team API key""" + return self._do_json( + APIKey, + _ApiRequest( + method="POST", + path_fmt="/v1/teams/{}/api_keys", + path_args=[team_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_teams_secrets(self, *, team_id: str, body: UpsertSecretRequest) -> Secret: + """Upserts a secret in a team""" + return self._do_json( + Secret, + _ApiRequest( + method="POST", + path_fmt="/v1/teams/{}/secrets", + path_args=[team_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_teams_training_projects( + self, *, team_id: str, body: UpsertTrainingProjectRequest + ) -> UpsertTrainingProjectResponse: + """Upsert a training project in a specific team.""" + return self._do_json( + UpsertTrainingProjectResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/teams/{}/training_projects", + path_args=[team_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_training_jobs_search( + self, *, body: SearchTrainingJobsRequest + ) -> SearchTrainingJobsResponse: + """Search training jobs.""" + return self._do_json( + SearchTrainingJobsResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_jobs/search", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_training_projects( + self, *, body: UpsertTrainingProjectRequest + ) -> UpsertTrainingProjectResponse: + """Upsert a training project.""" + return self._do_json( + UpsertTrainingProjectResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_training_projects_jobs( + self, *, training_project_id: str, body: CreateTrainingJobRequest + ) -> CreateTrainingJobResponse: + """Create a training job.""" + return self._do_json( + CreateTrainingJobResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs", + path_args=[training_project_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_training_projects_jobs_logs( + self, + *, + training_project_id: str, + training_job_id: str, + body: GetTrainingJobLogsRequest, + ) -> GetLogsResponse: + """Get the logs for a training job.""" + return self._do_json( + GetLogsResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs/{}/logs", + path_args=[training_project_id, training_job_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_training_projects_jobs_metrics( + self, + *, + training_project_id: str, + training_job_id: str, + body: GetTrainingJobMetricsRequest, + ) -> GetTrainingJobMetricsResponse: + """Get the metrics for a training job.""" + return self._do_json( + GetTrainingJobMetricsResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs/{}/metrics", + path_args=[training_project_id, training_job_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def post_training_projects_jobs_recreate( + self, *, training_project_id: str, training_job_id: str + ) -> RecreateTrainingJobResponse: + """Recreate a training job""" + return self._do_json( + RecreateTrainingJobResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs/{}/recreate", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + def post_training_projects_jobs_stop( + self, + *, + training_project_id: str, + training_job_id: str, + body: StopTrainingJobRequest, + ) -> StopTrainingJobResponse: + """Stop a training job.""" + return self._do_json( + StopTrainingJobResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs/{}/stop", + path_args=[training_project_id, training_job_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + def _do(self, request: _ApiRequest) -> httpx.Response: + path = request.path_fmt.format( + *[urllib.parse.quote(a, safe="") for a in request.path_args] + ) + json_body = None + if request.body is not None: + if isinstance(request.body, BaseModel): + json_body = request.body.model_dump(mode="json") + else: + json_body = request.body + response = self._http_client.request(request.method, path, json=json_body) + if response.status_code != request.success_code: + raise ResponseError(status_code=response.status_code, body=response.text) + return response + + def _do_json(self, response_type: type[_T], request: _ApiRequest) -> _T: + response = self._do(request) + content_type = response.headers.get("content-type", "") + if not content_type.startswith("application/json"): + raise ValueError( + f"unexpected content type {content_type!r}, expected application/json" + ) + return response_type.model_validate_json(response.content) + + +class AsyncApiClient: + """Generated HTTP client for the Baseten API. + + Methods on this client are generated from the OpenAPI specification + and are NOT covered by any stability or compatibility guarantees. + They may change without notice between versions. + """ + + def __init__(self, http_client: httpx.AsyncClient) -> None: + """Create a new client. The caller is responsible for closing *http_client*.""" + self._http_client = http_client + + async def delete_api_keys(self, *, api_key_prefix: str) -> APIKeyTombstone: + """Deletes an API key by prefix""" + return await self._do_json( + APIKeyTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/api_keys/{}", + path_args=[api_key_prefix], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_chains(self, *, chain_id: str) -> ChainTombstone: + """Deletes a chain by ID""" + return await self._do_json( + ChainTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/chains/{}", + path_args=[chain_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_chains_deployments( + self, *, chain_id: str, chain_deployment_id: str + ) -> ChainDeploymentTombstone: + """Deletes a chain deployment by ID""" + return await self._do_json( + ChainDeploymentTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/chains/{}/deployments/{}", + path_args=[chain_id, chain_deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_library_listings( + self, *, user_defined_listing_id: str + ) -> LibraryListingTombstone: + """Deletes a library listing""" + return await self._do_json( + LibraryListingTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/library_listings/{}", + path_args=[user_defined_listing_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_library_listings_versions( + self, *, user_defined_listing_id: str, version_tag: str + ) -> LibraryListingVersionTombstone: + """Deletes a library listing version""" + return await self._do_json( + LibraryListingVersionTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/library_listings/{}/versions/{}", + path_args=[user_defined_listing_id, version_tag], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_models(self, *, model_id: str) -> ModelTombstone: + """Deletes a model by ID""" + return await self._do_json( + ModelTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/models/{}", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_models_deployments( + self, *, model_id: str, deployment_id: str + ) -> DeploymentTombstone: + """Deletes a model's deployment by ID""" + return await self._do_json( + DeploymentTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/models/{}/deployments/{}", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_models_deployments_replicas( + self, *, model_id: str, deployment_id: str, replica_id: str + ) -> TerminateReplicaResponse: + """Terminates a replica in a deployment""" + return await self._do_json( + TerminateReplicaResponse, + _ApiRequest( + method="DELETE", + path_fmt="/v1/models/{}/deployments/{}/replicas/{}", + path_args=[model_id, deployment_id, replica_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_training_projects( + self, *, training_project_id: str + ) -> TrainingProjectTombstone: + """Delete a training project.""" + return await self._do_json( + TrainingProjectTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/training_projects/{}", + path_args=[training_project_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def delete_training_projects_jobs( + self, *, training_project_id: str, training_job_id: str + ) -> TrainingJobTombstone: + """Delete a training job.""" + return await self._do_json( + TrainingJobTombstone, + _ApiRequest( + method="DELETE", + path_fmt="/v1/training_projects/{}/jobs/{}", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_api_keys(self) -> APIKeys: + """Lists the user's API keys""" + return await self._do_json( + APIKeys, + _ApiRequest( + method="GET", + path_fmt="/v1/api_keys", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_billing_usage_summary(self) -> UsageSummary: + """Gets billing usage summary for a date range""" + return await self._do_json( + UsageSummary, + _ApiRequest( + method="GET", + path_fmt="/v1/billing/usage_summary", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_blobs_credentials_model(self) -> GetBlobCredentialsResponse: + """Get blob credentials for models.""" + return await self._do_json( + GetBlobCredentialsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/blobs/credentials/model", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_blobs_credentials_train(self) -> GetBlobCredentialsResponse: + """Get blob credentials for training.""" + return await self._do_json( + GetBlobCredentialsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/blobs/credentials/train", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_chains(self) -> Chains: + """Gets all chains""" + return await self._do_json( + Chains, + _ApiRequest( + method="GET", + path_fmt="/v1/chains", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_chains_chain_id(self, *, chain_id: str) -> Chain: + """Gets a chain by ID""" + return await self._do_json( + Chain, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}", + path_args=[chain_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_chains_deployments(self, *, chain_id: str) -> ChainDeployments: + """Gets all chain deployments""" + return await self._do_json( + ChainDeployments, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}/deployments", + path_args=[chain_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_chains_deployments_chain_deployment_id( + self, *, chain_id: str, chain_deployment_id: str + ) -> ChainDeployment: + """Gets a chain deployment by ID""" + return await self._do_json( + ChainDeployment, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}/deployments/{}", + path_args=[chain_id, chain_deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_chains_environments(self, *, chain_id: str) -> Environments: + """Get all chain environments""" + return await self._do_json( + Environments, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}/environments", + path_args=[chain_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_chains_environments_env_name( + self, *, chain_id: str, env_name: str + ) -> ChainEnvironment: + """Get a chain environment's details""" + return await self._do_json( + ChainEnvironment, + _ApiRequest( + method="GET", + path_fmt="/v1/chains/{}/environments/{}", + path_args=[chain_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_instance_type_prices(self) -> InstanceTypePrices: + """Gets prices for available instance types.""" + return await self._do_json( + InstanceTypePrices, + _ApiRequest( + method="GET", + path_fmt="/v1/instance_type_prices", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_instance_types(self) -> InstanceTypes: + """Gets all available instance types""" + return await self._do_json( + InstanceTypes, + _ApiRequest( + method="GET", + path_fmt="/v1/instance_types", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_library_listings(self) -> LibraryListings: + """Gets all library listings""" + return await self._do_json( + LibraryListings, + _ApiRequest( + method="GET", + path_fmt="/v1/library_listings", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_library_listings_user_defined_listing_id( + self, *, user_defined_listing_id: str + ) -> LibraryListing: + """Gets a library listing""" + return await self._do_json( + LibraryListing, + _ApiRequest( + method="GET", + path_fmt="/v1/library_listings/{}", + path_args=[user_defined_listing_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_library_listings_versions( + self, *, user_defined_listing_id: str + ) -> LibraryListingVersions: + """Gets all versions for a library listing""" + return await self._do_json( + LibraryListingVersions, + _ApiRequest( + method="GET", + path_fmt="/v1/library_listings/{}/versions", + path_args=[user_defined_listing_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_library_listings_versions_version_tag( + self, *, user_defined_listing_id: str, version_tag: str + ) -> LibraryListingVersion: + """Gets a library listing version""" + return await self._do_json( + LibraryListingVersion, + _ApiRequest( + method="GET", + path_fmt="/v1/library_listings/{}/versions/{}", + path_args=[user_defined_listing_id, version_tag], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_model_apis_snapshots(self) -> ModelWeightSnapshot: + """Get the latest model weight snapshot""" + return await self._do_json( + ModelWeightSnapshot, + _ApiRequest( + method="GET", + path_fmt="/v1/model_apis/snapshots", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_model_apis_snapshots_model_id( + self, *, model_id: str + ) -> ModelWeightSnapshot: + """Get the latest model weight snapshot""" + return await self._do_json( + ModelWeightSnapshot, + _ApiRequest( + method="GET", + path_fmt="/v1/model_apis/snapshots/{}", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_models(self) -> Models: + """Gets all models""" + return await self._do_json( + Models, + _ApiRequest( + method="GET", + path_fmt="/v1/models", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_models_deployments(self, *, model_id: str) -> Deployments: + """Gets all deployments of a model""" + return await self._do_json( + Deployments, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/deployments", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_models_deployments_deployment_id( + self, *, model_id: str, deployment_id: str + ) -> Deployment: + """Gets a model's deployment by ID""" + return await self._do_json( + Deployment, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/deployments/{}", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_models_deployments_development(self, *, model_id: str) -> Deployment: + """Gets a model's development deployment""" + return await self._do_json( + Deployment, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/deployments/development", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_models_deployments_production(self, *, model_id: str) -> Deployment: + """Gets a model's production deployment""" + return await self._do_json( + Deployment, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/deployments/production", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_models_environments(self, *, model_id: str) -> Environments: + """Get all environments""" + return await self._do_json( + Environments, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/environments", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_models_environments_env_name( + self, *, model_id: str, env_name: str + ) -> Environment: + """Get an environment's details""" + return await self._do_json( + Environment, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}/environments/{}", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_models_model_id(self, *, model_id: str) -> Model: + """Gets a model by ID""" + return await self._do_json( + Model, + _ApiRequest( + method="GET", + path_fmt="/v1/models/{}", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_secrets(self) -> Secrets: + """Gets all secrets""" + return await self._do_json( + Secrets, + _ApiRequest( + method="GET", + path_fmt="/v1/secrets", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_teams(self) -> Teams: + """Lists all teams""" + return await self._do_json( + Teams, + _ApiRequest( + method="GET", + path_fmt="/v1/teams", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_teams_secrets(self, *, team_id: str) -> Secrets: + """Gets all secrets for a team""" + return await self._do_json( + Secrets, + _ApiRequest( + method="GET", + path_fmt="/v1/teams/{}/secrets", + path_args=[team_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects(self) -> ListTrainingProjectsResponse: + """List training projects.""" + return await self._do_json( + ListTrainingProjectsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects", + path_args=[], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects_cache_summary( + self, *, training_project_id: str + ) -> GetCacheSummaryResponse: + """Get training project cache summary.""" + return await self._do_json( + GetCacheSummaryResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/cache/summary", + path_args=[training_project_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects_jobs( + self, *, training_project_id: str + ) -> ListTrainingJobsResponse: + """List training jobs.""" + return await self._do_json( + ListTrainingJobsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs", + path_args=[training_project_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects_jobs_auth_codes( + self, *, training_project_id: str, training_job_id: str + ) -> GetAuthCodesResponse: + """Get auth codes for a training job.""" + return await self._do_json( + GetAuthCodesResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}/auth_codes", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects_jobs_checkpoint_files( + self, *, training_project_id: str, training_job_id: str + ) -> GetTrainingJobCheckpointFilesResponse: + """Get training job checkpoint files.""" + return await self._do_json( + GetTrainingJobCheckpointFilesResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}/checkpoint_files", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects_jobs_checkpoints( + self, *, training_project_id: str, training_job_id: str + ) -> GetTrainingJobCheckpointsResponse: + """Get training job checkpoints.""" + return await self._do_json( + GetTrainingJobCheckpointsResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}/checkpoints", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects_jobs_download( + self, *, training_project_id: str, training_job_id: str + ) -> DownloadTrainingJobResponse: + """Get the uploaded training job as a S3 Artifact""" + return await self._do_json( + DownloadTrainingJobResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}/download", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects_jobs_training_job_id( + self, *, training_project_id: str, training_job_id: str + ) -> GetTrainingJobResponse: + """Get a training job.""" + return await self._do_json( + GetTrainingJobResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}/jobs/{}", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def get_training_projects_training_project_id( + self, *, training_project_id: str + ) -> GetTrainingProjectResponse: + """Get a training project.""" + return await self._do_json( + GetTrainingProjectResponse, + _ApiRequest( + method="GET", + path_fmt="/v1/training_projects/{}", + path_args=[training_project_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def patch_chains_environments( + self, *, chain_id: str, env_name: str, body: UpdateChainEnvironmentRequest + ) -> UpdateChainEnvironmentResponse: + """Update a chain environment's settings""" + return await self._do_json( + UpdateChainEnvironmentResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/chains/{}/environments/{}", + path_args=[chain_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def patch_chains_environments_chainlet_settings_autoscaling_settings( + self, + *, + chain_id: str, + env_name: str, + body: UpdateChainletEnvironmentAutoscalingSettingsRequest, + ) -> UpdateAutoscalingSettingsResponse: + """Update a chainlet environment's autoscaling settings""" + return await self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/chains/{}/environments/{}/chainlet_settings/autoscaling_settings", + path_args=[chain_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def patch_library_listings( + self, *, user_defined_listing_id: str, body: UpdateLibraryListingRequest + ) -> LibraryListing: + """Updates a library listing""" + return await self._do_json( + LibraryListing, + _ApiRequest( + method="PATCH", + path_fmt="/v1/library_listings/{}", + path_args=[user_defined_listing_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def patch_library_listings_versions( + self, + *, + user_defined_listing_id: str, + version_tag: str, + body: UpdateLibraryListingVersionRequest, + ) -> LibraryListingVersion: + """Updates a library listing version""" + return await self._do_json( + LibraryListingVersion, + _ApiRequest( + method="PATCH", + path_fmt="/v1/library_listings/{}/versions/{}", + path_args=[user_defined_listing_id, version_tag], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def patch_models_deployments_autoscaling_settings( + self, *, model_id: str, deployment_id: str, body: UpdateAutoscalingSettings + ) -> UpdateAutoscalingSettingsResponse: + """Updates a deployment's autoscaling settings""" + return await self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/models/{}/deployments/{}/autoscaling_settings", + path_args=[model_id, deployment_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def patch_models_deployments_development_autoscaling_settings( + self, *, model_id: str, body: UpdateAutoscalingSettings + ) -> UpdateAutoscalingSettingsResponse: + """Updates a development deployment's autoscaling settings""" + return await self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/models/{}/deployments/development/autoscaling_settings", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def patch_models_deployments_production_autoscaling_settings( + self, *, model_id: str, body: UpdateAutoscalingSettings + ) -> UpdateAutoscalingSettingsResponse: + """Updates a production deployment's autoscaling settings""" + return await self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/models/{}/deployments/production/autoscaling_settings", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def patch_models_environments( + self, *, model_id: str, env_name: str, body: UpdateEnvironmentRequest + ) -> UpdateAutoscalingSettingsResponse: + """Update an environment's settings""" + return await self._do_json( + UpdateAutoscalingSettingsResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/models/{}/environments/{}", + path_args=[model_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def patch_training_projects_jobs_interactive_sessions( + self, + *, + training_project_id: str, + training_job_id: str, + session_id: str, + body: PatchInteractiveSessionRequest, + ) -> PatchInteractiveSessionResponse: + """Patch an interactive session.""" + return await self._do_json( + PatchInteractiveSessionResponse, + _ApiRequest( + method="PATCH", + path_fmt="/v1/training_projects/{}/jobs/{}/interactive_sessions/{}", + path_args=[training_project_id, training_job_id, session_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_api_keys(self, *, body: CreateAPIKeyRequest) -> APIKey: + """Creates an API key""" + return await self._do_json( + APIKey, + _ApiRequest( + method="POST", + path_fmt="/v1/api_keys", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_chains_deployments_deactivate( + self, *, chain_id: str, chain_deployment_id: str + ) -> DeactivateResponse: + """Deactivates a chain deployment""" + return await self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/chains/{}/deployments/{}/deactivate", + path_args=[chain_id, chain_deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_chains_environments( + self, *, chain_id: str, body: CreateChainEnvironmentRequest + ) -> ChainEnvironment: + """Create a chain environment""" + return await self._do_json( + ChainEnvironment, + _ApiRequest( + method="POST", + path_fmt="/v1/chains/{}/environments", + path_args=[chain_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_chains_environments_chainlet_settings_instance_types_update( + self, + *, + chain_id: str, + env_name: str, + body: UpdateChainletEnvironmentInstanceTypeRequest, + ) -> UpdateChainletEnvironmentInstanceTypeResponse: + """Update a chainlet environment's instance type settings.""" + return await self._do_json( + UpdateChainletEnvironmentInstanceTypeResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/chains/{}/environments/{}/chainlet_settings/instance_types/update", + path_args=[chain_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_chains_environments_promote( + self, *, chain_id: str, env_name: str, body: PromoteToChainEnvironmentRequest + ) -> ChainDeployment: + """Promotes a chain deployment to an environment""" + return await self._do_json( + ChainDeployment, + _ApiRequest( + method="POST", + path_fmt="/v1/chains/{}/environments/{}/promote", + path_args=[chain_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_library_listings( + self, *, body: CreateLibraryListingRequest + ) -> LibraryListing: + """Creates a new library listing""" + return await self._do_json( + LibraryListing, + _ApiRequest( + method="POST", + path_fmt="/v1/library_listings", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_library_listings_versions( + self, *, user_defined_listing_id: str, body: CreateLibraryListingVersionRequest + ) -> LibraryListingVersion: + """Creates a new library listing version""" + return await self._do_json( + LibraryListingVersion, + _ApiRequest( + method="POST", + path_fmt="/v1/library_listings/{}/versions", + path_args=[user_defined_listing_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_llm_models(self, *, body: CreateLLMModelRequest) -> LLMModel: + """Creates a new BIS LLM deployment""" + return await self._do_json( + LLMModel, + _ApiRequest( + method="POST", + path_fmt="/v1/llm_models", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_llm_models_deployments( + self, *, model_id: str, body: CreateLLMModelVersionRequest + ) -> LLMModelVersion: + """Creates a new BIS LLM deployment version""" + return await self._do_json( + LLMModelVersion, + _ApiRequest( + method="POST", + path_fmt="/v1/llm_models/{}/deployments", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_model_apis_snapshots( + self, *, body: CreateModelWeightSnapshotRequest + ) -> ModelWeightSnapshot: + """Create a model weight snapshot""" + return await self._do_json( + ModelWeightSnapshot, + _ApiRequest( + method="POST", + path_fmt="/v1/model_apis/snapshots", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_model_apis_snapshots_model_id( + self, *, model_id: str, body: CreateModelWeightSnapshotRequest + ) -> ModelWeightSnapshot: + """Create a model weight snapshot""" + return await self._do_json( + ModelWeightSnapshot, + _ApiRequest( + method="POST", + path_fmt="/v1/model_apis/snapshots/{}", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_activate( + self, *, model_id: str, deployment_id: str + ) -> ActivateResponse: + """Activates a deployment""" + return await self._do_json( + ActivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/activate", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_deactivate( + self, *, model_id: str, deployment_id: str + ) -> DeactivateResponse: + """Deactivates a deployment""" + return await self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/deactivate", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_development_activate( + self, *, model_id: str + ) -> ActivateResponse: + """Activates a development deployment""" + return await self._do_json( + ActivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/development/activate", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_development_deactivate( + self, *, model_id: str + ) -> DeactivateResponse: + """Deactivates a development deployment""" + return await self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/development/deactivate", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_development_promote( + self, *, model_id: str, body: PromoteRequest + ) -> Deployment: + """Promotes a development deployment to production""" + return await self._do_json( + Deployment, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/development/promote", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_development_retry( + self, *, model_id: str + ) -> RetryDeploymentResponse: + """Retries a failed development deployment""" + return await self._do_json( + RetryDeploymentResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/development/retry", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_logs( + self, *, model_id: str, deployment_id: str, body: GetDeploymentLogsRequest + ) -> GetLogsResponse: + """Gets the logs for a model deployment.""" + return await self._do_json( + GetLogsResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/logs", + path_args=[model_id, deployment_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_production_activate( + self, *, model_id: str + ) -> ActivateResponse: + """Activates a production deployment""" + return await self._do_json( + ActivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/production/activate", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_production_deactivate( + self, *, model_id: str + ) -> DeactivateResponse: + """Deactivates a production deployment""" + return await self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/production/deactivate", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_production_retry( + self, *, model_id: str + ) -> RetryDeploymentResponse: + """Retries a failed production deployment""" + return await self._do_json( + RetryDeploymentResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/production/retry", + path_args=[model_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_promote( + self, *, model_id: str, deployment_id: str, body: PromoteRequest + ) -> Deployment: + """Promotes a deployment to production""" + return await self._do_json( + Deployment, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/promote", + path_args=[model_id, deployment_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_deployments_retry( + self, *, model_id: str, deployment_id: str + ) -> RetryDeploymentResponse: + """Retries a failed deployment""" + return await self._do_json( + RetryDeploymentResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/deployments/{}/retry", + path_args=[model_id, deployment_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments( + self, *, model_id: str, body: CreateEnvironmentRequest + ) -> Environment: + """Create an environment""" + return await self._do_json( + Environment, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments", + path_args=[model_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments_activate( + self, *, model_id: str, env_name: str + ) -> ActivateResponse: + """Activates a deployment associated with an environment""" + return await self._do_json( + ActivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/activate", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments_cancel_promotion( + self, *, model_id: str, env_name: str + ) -> CancelPromotionResponse: + """Cancels a promotion to an environment""" + return await self._do_json( + CancelPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/cancel_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments_deactivate( + self, *, model_id: str, env_name: str + ) -> DeactivateResponse: + """Deactivates a deployment associated with an environment""" + return await self._do_json( + DeactivateResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/deactivate", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments_force_cancel_promotion( + self, *, model_id: str, env_name: str + ) -> SignalPromotionResponse: + """Force cancels a rolling promotion""" + return await self._do_json( + SignalPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/force_cancel_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments_force_roll_forward_promotion( + self, *, model_id: str, env_name: str + ) -> SignalPromotionResponse: + """Force rolls forward a rolling promotion""" + return await self._do_json( + SignalPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/force_roll_forward_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments_pause_promotion( + self, *, model_id: str, env_name: str + ) -> SignalPromotionResponse: + """Pauses a rolling promotion""" + return await self._do_json( + SignalPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/pause_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments_promote( + self, *, model_id: str, env_name: str, body: PromoteToEnvironmentRequest + ) -> Deployment: + """Promotes a deployment to an environment""" + return await self._do_json( + Deployment, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/promote", + path_args=[model_id, env_name], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_models_environments_resume_promotion( + self, *, model_id: str, env_name: str + ) -> SignalPromotionResponse: + """Resumes a paused rolling promotion""" + return await self._do_json( + SignalPromotionResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/models/{}/environments/{}/resume_promotion", + path_args=[model_id, env_name], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_secrets(self, *, body: UpsertSecretRequest) -> Secret: + """Upserts a secret""" + return await self._do_json( + Secret, + _ApiRequest( + method="POST", + path_fmt="/v1/secrets", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_teams_api_keys( + self, *, team_id: str, body: CreateAPIKeyRequest + ) -> APIKey: + """Creates a team API key""" + return await self._do_json( + APIKey, + _ApiRequest( + method="POST", + path_fmt="/v1/teams/{}/api_keys", + path_args=[team_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_teams_secrets( + self, *, team_id: str, body: UpsertSecretRequest + ) -> Secret: + """Upserts a secret in a team""" + return await self._do_json( + Secret, + _ApiRequest( + method="POST", + path_fmt="/v1/teams/{}/secrets", + path_args=[team_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_teams_training_projects( + self, *, team_id: str, body: UpsertTrainingProjectRequest + ) -> UpsertTrainingProjectResponse: + """Upsert a training project in a specific team.""" + return await self._do_json( + UpsertTrainingProjectResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/teams/{}/training_projects", + path_args=[team_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_training_jobs_search( + self, *, body: SearchTrainingJobsRequest + ) -> SearchTrainingJobsResponse: + """Search training jobs.""" + return await self._do_json( + SearchTrainingJobsResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_jobs/search", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_training_projects( + self, *, body: UpsertTrainingProjectRequest + ) -> UpsertTrainingProjectResponse: + """Upsert a training project.""" + return await self._do_json( + UpsertTrainingProjectResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects", + path_args=[], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_training_projects_jobs( + self, *, training_project_id: str, body: CreateTrainingJobRequest + ) -> CreateTrainingJobResponse: + """Create a training job.""" + return await self._do_json( + CreateTrainingJobResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs", + path_args=[training_project_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_training_projects_jobs_logs( + self, + *, + training_project_id: str, + training_job_id: str, + body: GetTrainingJobLogsRequest, + ) -> GetLogsResponse: + """Get the logs for a training job.""" + return await self._do_json( + GetLogsResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs/{}/logs", + path_args=[training_project_id, training_job_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_training_projects_jobs_metrics( + self, + *, + training_project_id: str, + training_job_id: str, + body: GetTrainingJobMetricsRequest, + ) -> GetTrainingJobMetricsResponse: + """Get the metrics for a training job.""" + return await self._do_json( + GetTrainingJobMetricsResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs/{}/metrics", + path_args=[training_project_id, training_job_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def post_training_projects_jobs_recreate( + self, *, training_project_id: str, training_job_id: str + ) -> RecreateTrainingJobResponse: + """Recreate a training job""" + return await self._do_json( + RecreateTrainingJobResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs/{}/recreate", + path_args=[training_project_id, training_job_id], + body=None, + success_code=200, + error_codes=None, + ), + ) + + async def post_training_projects_jobs_stop( + self, + *, + training_project_id: str, + training_job_id: str, + body: StopTrainingJobRequest, + ) -> StopTrainingJobResponse: + """Stop a training job.""" + return await self._do_json( + StopTrainingJobResponse, + _ApiRequest( + method="POST", + path_fmt="/v1/training_projects/{}/jobs/{}/stop", + path_args=[training_project_id, training_job_id], + body=body, + success_code=200, + error_codes=None, + ), + ) + + async def _do(self, request: _ApiRequest) -> httpx.Response: + path = request.path_fmt.format( + *[urllib.parse.quote(a, safe="") for a in request.path_args] + ) + json_body = None + if request.body is not None: + if isinstance(request.body, BaseModel): + json_body = request.body.model_dump(mode="json") + else: + json_body = request.body + response = await self._http_client.request(request.method, path, json=json_body) + if response.status_code != request.success_code: + raise ResponseError(status_code=response.status_code, body=response.text) + return response + + async def _do_json(self, response_type: type[_T], request: _ApiRequest) -> _T: + response = await self._do(request) + content_type = response.headers.get("content-type", "") + if not content_type.startswith("application/json"): + raise ValueError( + f"unexpected content type {content_type!r}, expected application/json" + ) + return response_type.model_validate_json(response.content) diff --git a/baseten/client/managementapi/_models.py b/baseten/client/managementapi/_models.py new file mode 100644 index 0000000..0081edf --- /dev/null +++ b/baseten/client/managementapi/_models.py @@ -0,0 +1,3564 @@ +# generated by datamodel-codegen: +# filename: + +from __future__ import annotations +from enum import Enum +from typing import Annotated, Any, Literal +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel +from datetime import date as date_aliased + + +class CheckpointSyncStatus(Enum): + SYNCING = "SYNCING" + COMPLETED = "COMPLETED" + + +class BasetenLatestCheckpointConfig(BaseModel): + project_name: Annotated[ + str | None, + Field( + description="Name of the project to load the checkpoint from", + title="Project Name", + ), + ] = None + job_id: Annotated[ + str | None, + Field(description="ID of the job to load the checkpoint from", title="Job Id"), + ] = None + typ: Annotated[Literal["baseten_latest_checkpoint"], Field(title="Typ")] = ( + "baseten_latest_checkpoint" + ) + + +class BasetenNamedCheckpointConfig(BaseModel): + checkpoint_name: Annotated[ + str, + Field( + description="Name of the checkpoint to load from", title="Checkpoint Name" + ), + ] + project_name: Annotated[ + str | None, + Field( + description="Name of the project to load the checkpoint from", + title="Project Name", + ), + ] = None + job_id: Annotated[ + str | None, + Field(description="ID of the job to load the checkpoint from", title="Job Id"), + ] = None + typ: Annotated[Literal["baseten_named_checkpoint"], Field(title="Typ")] = ( + "baseten_named_checkpoint" + ) + + +class CreateTrainingJobCacheConfig(BaseModel): + enable_legacy_hf_mount: Annotated[ + bool | None, + Field( + description="Whether to enable the legacy Hugging Face cache.", + examples=[True], + title="Enable Legacy Hf Mount", + ), + ] = False + enabled: Annotated[ + bool | None, + Field( + description="Whether to enable the read-write cache.", + examples=[True], + title="Enabled", + ), + ] = False + require_cache_affinity: Annotated[ + bool | None, + Field( + description="Whether to require region affinity for the read-write cache. If False, the resulting job is not guaranteed to be deployed alongside the previous cache.", + examples=[True, False], + title="Require Cache Affinity", + ), + ] = True + mount_base_path: Annotated[ + str | None, + Field( + description="Mount base path for the cache directory. The project cache and team cache will be mounted under this path.", + examples=["/workspace/.cache", "/root/.cache"], + title="Mount Base Path", + ), + ] = "/root/.cache" + + +class CreateTrainingJobCheckpointingConfig(BaseModel): + enabled: Annotated[ + bool | None, + Field( + description="Whether checkpointing is enabled.", + examples=[True], + title="Enabled", + ), + ] = False + checkpoint_path: Annotated[ + str | None, + Field( + description="path where checkpoints will be saved.", + examples=["/mnt/ckpts"], + title="Checkpoint Path", + ), + ] = None + volume_size_gib: Annotated[ + int | None, + Field( + description="Size of the volume in gibibytes. If not provided, the default size will be used", + examples=[10], + title="Volume Size Gib", + ), + ] = None + + +class CreateTrainingJobS3Artifact(BaseModel): + s3_bucket: Annotated[ + str, + Field( + description="S3 bucket for the uploaded runtime artifact.", + examples=["my-s3-bucket"], + title="S3 Bucket", + ), + ] + s3_key: Annotated[ + str, + Field( + description="S3 key for the uploaded runtime artifact.", + examples=["my-s3-key"], + title="S3 Key", + ), + ] + + +class DockerAuthType(Enum): + GCP_SERVICE_ACCOUNT_JSON = "GCP_SERVICE_ACCOUNT_JSON" + AWS_IAM = "AWS_IAM" + AWS_OIDC = "AWS_OIDC" + GCP_OIDC = "GCP_OIDC" + REGISTRY_SECRET = "REGISTRY_SECRET" + + +class GitInfo(BaseModel): + latest_commit_sha: Annotated[str, Field(title="Latest Commit Sha")] + latest_tag: Annotated[str | None, Field(title="Latest Tag")] + commits_since_tag: Annotated[int | None, Field(title="Commits Since Tag")] + has_uncommitted_changes: Annotated[bool, Field(title="Has Uncommitted Changes")] + + +class Checkpoints( + RootModel[BasetenLatestCheckpointConfig | BasetenNamedCheckpointConfig] +): + root: Annotated[ + BasetenLatestCheckpointConfig | BasetenNamedCheckpointConfig, + Field(discriminator="typ"), + ] + + +class LoadCheckpointConfig(BaseModel): + enabled: Annotated[ + bool | None, + Field(description="Whether checkpoint loading is enabled", title="Enabled"), + ] = False + download_folder: Annotated[ + str | None, + Field( + description="Folder where checkpoints will be downloaded", + title="Download Folder", + ), + ] = "/tmp/loaded_checkpoints" + checkpoints: Annotated[ + list[Checkpoints] | None, + Field(description="List of checkpoint configurations", title="Checkpoints"), + ] = None + + +class TrussUserEnv(BaseModel): + truss_client_version: Annotated[str | None, Field(title="Truss Client Version")] = ( + None + ) + python_version: Annotated[str | None, Field(title="Python Version")] = None + pydantic_version: Annotated[str | None, Field(title="Pydantic Version")] = None + mypy_version: Annotated[str | None, Field(title="Mypy Version")] = None + is_library_deployment: Annotated[ + bool | None, Field(title="Is Library Deployment") + ] = False + is_frontend_deployment: Annotated[ + bool | None, Field(title="Is Frontend Deployment") + ] = False + git_info: GitInfo | None = None + + +class V1InteractiveSessionAuthProvider(Enum): + github = "github" + microsoft = "microsoft" + + +class V1InteractiveSessionProvider(Enum): + vs_code = "vs_code" + cursor = "cursor" + + +class V1InteractiveSessionTrigger(Enum): + on_startup = "on_startup" + on_failure = "on_failure" + on_demand = "on_demand" + + +class CheckpointFile(BaseModel): + url: Annotated[str, Field(title="Url")] + relative_file_name: Annotated[str, Field(title="Relative File Name")] + node_rank: Annotated[int, Field(title="Node Rank")] + size_bytes: Annotated[int, Field(title="Size Bytes")] + last_modified: Annotated[str, Field(title="Last Modified")] + + +class FileSummary(BaseModel): + path: Annotated[ + str, Field(description="Relative path of the file in the cache", title="Path") + ] + size_bytes: Annotated[ + int, Field(description="Size of the file in bytes", title="Size Bytes") + ] + modified: Annotated[ + str, Field(description="Last modification time of the file", title="Modified") + ] + file_type: Annotated[str, Field(description="Type of the file", title="File Type")] + permissions: Annotated[ + str, Field(description="Permissions of the file", title="Permissions") + ] + + +class APIKeyCategory(Enum): + PERSONAL = "PERSONAL" + WORKSPACE_MANAGE_ALL = "WORKSPACE_MANAGE_ALL" + WORKSPACE_EXPORT_METRICS = "WORKSPACE_EXPORT_METRICS" + WORKSPACE_INVOKE = "WORKSPACE_INVOKE" + + +class ResourceKind(Enum): + MODEL_DEPLOYMENT = "MODEL_DEPLOYMENT" + TRAINING_JOB = "TRAINING_JOB" + CHAINLET = "CHAINLET" + + +class Secret(BaseModel): + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the secret was created in ISO 8601 format", + title="Created At", + ), + ] + name: Annotated[str, Field(description="Name of the secret", title="Name")] + team_name: Annotated[ + str, + Field(description="Name of the team the secret belongs to", title="Team Name"), + ] + + +class Secrets(BaseModel): + secrets: Annotated[list[Secret], Field(title="Secrets")] + + +class UpsertSecretRequest(BaseModel): + name: Annotated[ + str, + Field( + description="Name of the new or existing secret", + examples=["my_secret"], + title="Name", + ), + ] + value: Annotated[ + str, + Field( + description="Value of the secret", + examples=["my_secret_value"], + title="Value", + ), + ] + + +class Team(BaseModel): + id: Annotated[str, Field(description="Unique identifier of the team", title="Id")] + name: Annotated[str, Field(description="Name of the team", title="Name")] + default: Annotated[ + bool, + Field( + description="Whether this is the default team for the organization", + title="Default", + ), + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the team was created in ISO 8601 format", + title="Created At", + ), + ] + + +class Teams(BaseModel): + teams: Annotated[list[Team], Field(description="A list of teams", title="Teams")] + + +class InstanceType(BaseModel): + id: Annotated[ + str, Field(description="Identifier string for the instance type", title="Id") + ] + name: Annotated[ + str, Field(description="Display name of the instance type", title="Name") + ] + memory_limit_mib: Annotated[ + int, + Field( + description="Memory limit of the instance type in Mebibytes", + title="Memory Limit Mib", + ), + ] + millicpu_limit: Annotated[ + int, + Field( + description="CPU limit of the instance type in millicpu", + title="Millicpu Limit", + ), + ] + gpu_count: Annotated[ + int, Field(description="Number of GPUs on the instance type", title="Gpu Count") + ] + gpu_type: Annotated[ + str | None, + Field(description="Type of GPU on the instance type", title="Gpu Type"), + ] + gpu_memory_limit_mib: Annotated[ + int | None, + Field( + description="Memory limit of the GPU on the instance type in Mebibytes", + title="Gpu Memory Limit Mib", + ), + ] + + +class InstanceTypes(BaseModel): + instance_types: Annotated[list[InstanceType], Field(title="Instance Types")] + + +class InstanceTypeWithPrice(BaseModel): + instance_type: Annotated[ + InstanceType, Field(description="Instance type properties.") + ] + price: Annotated[ + float, Field(description="Usage price in USD / minute.", title="Price") + ] + + +class InstanceTypePrices(BaseModel): + instance_types: Annotated[ + list[InstanceTypeWithPrice], Field(title="Instance Types") + ] + + +class Model(BaseModel): + id: Annotated[str, Field(description="Unique identifier of the model", title="Id")] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the model was created in ISO 8601 format", + title="Created At", + ), + ] + name: Annotated[str, Field(description="Name of the model", title="Name")] + deployments_count: Annotated[ + int, + Field( + description="Number of deployments of the model", title="Deployments Count" + ), + ] + production_deployment_id: Annotated[ + str | None, + Field( + description="Unique identifier of the production deployment of the model", + title="Production Deployment Id", + ), + ] + development_deployment_id: Annotated[ + str | None, + Field( + description="Unique identifier of the development deployment of the model", + title="Development Deployment Id", + ), + ] + instance_type_name: Annotated[ + str, + Field( + description="Name of the instance type for the production deployment of the model", + title="Instance Type Name", + ), + ] + team_name: Annotated[ + str, + Field( + description="Name of the team associated with the model.", title="Team Name" + ), + ] + + +class Models(BaseModel): + models: Annotated[list[Model], Field(title="Models")] + + +class ModelTombstone(BaseModel): + id: Annotated[str, Field(description="Unique identifier of the model", title="Id")] + deleted: Annotated[ + bool, Field(description="Whether the model was deleted", title="Deleted") + ] + + +class AutoscalingSettings(BaseModel): + min_replica: Annotated[ + int, Field(description="Minimum number of replicas", title="Min Replica") + ] + max_replica: Annotated[ + int, Field(description="Maximum number of replicas", title="Max Replica") + ] + autoscaling_window: Annotated[ + int | None, + Field( + description="Timeframe of traffic considered for autoscaling decisions", + title="Autoscaling Window", + ), + ] + scale_down_delay: Annotated[ + int | None, + Field( + description="Waiting period before scaling down any active replica", + title="Scale Down Delay", + ), + ] + concurrency_target: Annotated[ + int, + Field( + description="Number of requests per replica before scaling up", + title="Concurrency Target", + ), + ] + target_utilization_percentage: Annotated[ + int | None, + Field( + description="Target utilization percentage for scaling up/down.", + title="Target Utilization Percentage", + ), + ] + target_in_flight_tokens: Annotated[ + int | None, + Field( + description="Target number of in-flight tokens for autoscaling decisions. Early access only.", + title="Target In Flight Tokens", + ), + ] = None + + +class DeploymentStatus(Enum): + BUILDING = "BUILDING" + DEPLOYING = "DEPLOYING" + DEPLOY_FAILED = "DEPLOY_FAILED" + LOADING_MODEL = "LOADING_MODEL" + ACTIVE = "ACTIVE" + UNHEALTHY = "UNHEALTHY" + BUILD_FAILED = "BUILD_FAILED" + BUILD_STOPPED = "BUILD_STOPPED" + DEACTIVATING = "DEACTIVATING" + INACTIVE = "INACTIVE" + FAILED = "FAILED" + UPDATING = "UPDATING" + SCALED_TO_ZERO = "SCALED_TO_ZERO" + WAKING_UP = "WAKING_UP" + + +class Deployment(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the deployment", title="Id") + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the deployment was created in ISO 8601 format", + title="Created At", + ), + ] + name: Annotated[str, Field(description="Name of the deployment", title="Name")] + model_id: Annotated[ + str, Field(description="Unique identifier of the model", title="Model Id") + ] + is_production: Annotated[ + bool, + Field( + description="Whether the deployment is the production deployment of the model", + title="Is Production", + ), + ] + is_development: Annotated[ + bool, + Field( + description="Whether the deployment is the development deployment of the model", + title="Is Development", + ), + ] + status: Annotated[DeploymentStatus, Field(description="Status of the deployment")] + active_replica_count: Annotated[ + int, + Field(description="Number of active replicas", title="Active Replica Count"), + ] + autoscaling_settings: Annotated[ + AutoscalingSettings | None, + Field( + description="Autoscaling settings for the deployment. If null, the model has not finished deploying" + ), + ] + instance_type_name: Annotated[ + str | None, + Field( + description="Name of the instance type the model deployment is running on", + title="Instance Type Name", + ), + ] + environment: Annotated[ + str | None, + Field( + description="The environment associated with the deployment", + title="Environment", + ), + ] + labels: Annotated[ + dict[str, Any] | None, + Field( + description="User-provided key-value labels for the deployment", + title="Labels", + ), + ] = None + + +class Deployments(BaseModel): + deployments: Annotated[ + list[Deployment], + Field(description="A list of deployments of a model", title="Deployments"), + ] + + +class DeploymentTombstone(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the deployment", title="Id") + ] + deleted: Annotated[ + bool, Field(description="Whether the deployment was deleted", title="Deleted") + ] + model_id: Annotated[ + str, Field(description="Unique identifier of the model", title="Model Id") + ] + + +class UpdateAutoscalingSettings(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + min_replica: Annotated[ + int | None, + Field( + description="Minimum number of replicas", examples=[0], title="Min Replica" + ), + ] = None + max_replica: Annotated[ + int | None, + Field( + description="Maximum number of replicas", examples=[7], title="Max Replica" + ), + ] = None + autoscaling_window: Annotated[ + int | None, + Field( + description="Timeframe of traffic considered for autoscaling decisions", + examples=[600], + title="Autoscaling Window", + ), + ] = None + scale_down_delay: Annotated[ + int | None, + Field( + description="Waiting period before scaling down any active replica", + examples=[120], + title="Scale Down Delay", + ), + ] = None + concurrency_target: Annotated[ + int | None, + Field( + description="Number of requests per replica before scaling up", + examples=[2], + title="Concurrency Target", + ), + ] = None + target_utilization_percentage: Annotated[ + int | None, + Field( + description="Target utilization percentage for scaling up/down.", + examples=[70], + title="Target Utilization Percentage", + ), + ] = None + target_in_flight_tokens: Annotated[ + int | None, + Field( + description="Target number of in-flight tokens for autoscaling decisions. Early access only.", + examples=[40000], + title="Target In Flight Tokens", + ), + ] = None + + +class UpdateAutoscalingSettingsStatus(Enum): + ACCEPTED = "ACCEPTED" + QUEUED = "QUEUED" + UNCHANGED = "UNCHANGED" + + +class UpdateAutoscalingSettingsResponse(BaseModel): + status: Annotated[ + UpdateAutoscalingSettingsStatus, + Field(description="Status of the request to update autoscaling settings"), + ] + message: Annotated[ + str, + Field( + description="A message describing the status of the request to update autoscaling settings", + title="Message", + ), + ] + + +class PromoteRequest(BaseModel): + scale_down_previous_production: Annotated[ + bool | None, + Field( + description="Whether to scale down the previous production deployment after promoting", + examples=[True], + title="Scale Down Previous Production", + ), + ] = True + preserve_env_instance_type: Annotated[ + bool | None, + Field( + description="Whether to use the promoting deployment's instance type or preserve target environment's instance type", + examples=[True], + title="Preserve Env Instance Type", + ), + ] = True + + +class ActivateResponse(BaseModel): + success: Annotated[ + bool | None, + Field( + description="Whether the deployment was successfully activated", + title="Success", + ), + ] = True + + +class DeactivateResponse(BaseModel): + success: Annotated[ + bool | None, + Field( + description="Whether the deployment was successfully deactivated", + title="Success", + ), + ] = True + + +class RetryDeploymentResponse(BaseModel): + retried: Annotated[ + bool, + Field( + description="Whether the retry was successfully initiated", title="Retried" + ), + ] + reason: Annotated[ + str | None, + Field( + description="Explanation of the result. Provided when retried is false to explain why retry was not possible.", + title="Reason", + ), + ] = None + deployment: Annotated[ + Deployment, Field(description="The deployment that was retried") + ] + + +class SortOrder(Enum): + asc = "asc" + desc = "desc" + + +class Limit(RootModel[int]): + root: Annotated[ + int, + Field( + description="Limit of logs to fetch in a single request", + ge=1, + le=1000, + title="Limit", + ), + ] = 500 + + +class GetDeploymentLogsRequest(BaseModel): + start_epoch_millis: Annotated[ + int | None, + Field( + description="Epoch millis timestamp to start fetching logs", + title="Start Epoch Millis", + ), + ] = None + end_epoch_millis: Annotated[ + int | None, + Field( + description="Epoch millis timestamp to end fetching logs", + title="End Epoch Millis", + ), + ] = None + direction: Annotated[SortOrder | None, Field(description="Sort order for logs")] = ( + None + ) + limit: Annotated[ + Limit | None, + Field( + default_factory=lambda: Limit(500), + description="Limit of logs to fetch in a single request", + title="Limit", + ), + ] + + +class Log(BaseModel): + timestamp: Annotated[ + str, + Field( + description="Epoch nanosecond timestamp of the log message.", + title="Timestamp", + ), + ] + message: Annotated[ + str, Field(description="The contents of the log message.", title="Message") + ] + replica: Annotated[ + str | None, + Field( + description="The replica the log line was emitted from.", title="Replica" + ), + ] + + +class GetLogsResponse(BaseModel): + logs: Annotated[ + list[Log], Field(description="Logs for a specific entity.", title="Logs") + ] + + +class TerminateReplicaResponse(BaseModel): + success: Annotated[ + bool | None, + Field( + description="Whether the replica was successfully terminated", + title="Success", + ), + ] = True + + +class InProgressPromotionStatus(Enum): + RELEASING = "RELEASING" + RAMPING_UP = "RAMPING_UP" + RAMPING_DOWN = "RAMPING_DOWN" + PAUSED = "PAUSED" + SUCCEEDED = "SUCCEEDED" + FAILED = "FAILED" + CANCELED = "CANCELED" + + +class InProgressPromotion(BaseModel): + status: Annotated[ + InProgressPromotionStatus, Field(description="Status of the promotion") + ] + percent_traffic_to_new_version: Annotated[ + int, + Field( + description="Percentage of traffic routed to the candidate deployment", + title="Percent Traffic To New Version", + ), + ] + error_message: Annotated[ + str | None, + Field(description="Error message if promotion failed", title="Error Message"), + ] = None + rolling_deploy: Annotated[ + bool | None, + Field(description="Whether this is a rolling deploy", title="Rolling Deploy"), + ] = None + + +class PromotionCleanupStrategy(Enum): + KEEP = "KEEP" + SCALE_TO_ZERO = "SCALE_TO_ZERO" + DEACTIVATE = "DEACTIVATE" + + +class RollingDeployStrategy(Enum): + REPLICA = "REPLICA" + + +class UpdateRollingDeployConfig(BaseModel): + rolling_deploy_strategy: Annotated[ + RollingDeployStrategy | None, + Field( + description="The rolling deploy strategy to use for promotions.", + examples=["REPLICA"], + ), + ] = None + max_surge_percent: Annotated[ + int | None, + Field( + description="The maximum surge percentage for rolling deploys.", + examples=[10], + title="Max Surge Percent", + ), + ] = 10 + max_unavailable_percent: Annotated[ + int | None, + Field( + description="The maximum unavailable percentage for rolling deploys.", + examples=[10], + title="Max Unavailable Percent", + ), + ] = None + stabilization_time_seconds: Annotated[ + int | None, + Field( + description="The stabilization time in seconds for rolling deploys.", + examples=[300], + title="Stabilization Time Seconds", + ), + ] = None + replica_overhead_percent: Annotated[ + int | None, + Field( + description="The replica overhead percentage for rolling deploys.", + examples=[0], + title="Replica Overhead Percent", + ), + ] = None + + +class PromoteToEnvironmentRequest(BaseModel): + scale_down_previous_deployment: Annotated[ + bool | None, + Field( + description="Whether to scale down the previous deployment after promoting", + examples=[True], + title="Scale Down Previous Deployment", + ), + ] = True + deployment_id: Annotated[ + str, + Field(description="The id of the deployment to promote", title="Deployment Id"), + ] + preserve_env_instance_type: Annotated[ + bool | None, + Field( + description="Whether to use the promoting deployment's instance type or preserve target environment's instance type", + examples=[True], + title="Preserve Env Instance Type", + ), + ] = True + + +class CancelPromotionStatus(Enum): + CANCELED = "CANCELED" + RAMPING_DOWN = "RAMPING_DOWN" + + +class CancelPromotionResponse(BaseModel): + status: Annotated[ + CancelPromotionStatus, + Field( + description="Status of the request to cancel a promotion. Can be CANCELED or RAMPING_DOWN." + ), + ] + message: Annotated[ + str, + Field( + description="A message describing the status of the request to cancel a promotion", + title="Message", + ), + ] + + +class SignalPromotionResponse(BaseModel): + success: Annotated[ + bool, + Field(description="Whether the signal was successfully sent", title="Success"), + ] + + +class Chain(BaseModel): + id: Annotated[str, Field(description="Unique identifier of the chain", title="Id")] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the chain was created in ISO 8601 format", + title="Created At", + ), + ] + name: Annotated[str, Field(description="Name of the chain", title="Name")] + deployments_count: Annotated[ + int, + Field( + description="Number of deployments of the chain", title="Deployments Count" + ), + ] + team_name: Annotated[ + str, + Field( + description="Name of the team associated with the chain", title="Team Name" + ), + ] + + +class Chains(BaseModel): + chains: Annotated[list[Chain], Field(title="Chains")] + + +class ChainTombstone(BaseModel): + id: Annotated[str, Field(description="Unique identifier of the chain", title="Id")] + deleted: Annotated[ + bool, Field(description="Whether the chain was deleted", title="Deleted") + ] + + +class Chainlet(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the chainlet", title="Id") + ] + name: Annotated[str, Field(description="Name of the chainlet", title="Name")] + autoscaling_settings: Annotated[ + AutoscalingSettings | None, + Field( + description="Autoscaling settings for the chainlet. If null, it has not finished deploying" + ), + ] + instance_type_name: Annotated[ + str, + Field( + description="Name of the instance type the chainlet is deployed on", + title="Instance Type Name", + ), + ] + active_replica_count: Annotated[ + int, + Field(description="Number of active replicas", title="Active Replica Count"), + ] + status: Annotated[DeploymentStatus, Field(description="Status of the chainlet")] + + +class ChainDeploymentTombstone(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the chain deployment", title="Id") + ] + deleted: Annotated[ + bool, + Field(description="Whether the chain deployment was deleted", title="Deleted"), + ] + chain_id: Annotated[ + str, Field(description="Unique identifier of the chain", title="Chain Id") + ] + + +class ChainletEnvironmentSettingsRequest(BaseModel): + chainlet_name: Annotated[ + str, + Field( + description="Name of the chainlet", + examples=["HelloWorld"], + title="Chainlet Name", + ), + ] + autoscaling_settings: Annotated[ + UpdateAutoscalingSettings | None, + Field( + description="Autoscaling settings for the chainlet", + examples=[ + { + "autoscaling_window": 60, + "concurrency_target": 1, + "max_replica": 1, + "min_replica": 0, + "scale_down_delay": 900, + "target_in_flight_tokens": None, + "target_utilization_percentage": 70, + } + ], + ), + ] = None + instance_type_id: Annotated[ + str | None, + Field( + description="ID of the instance type to use for the chainlet", + examples=["1x4", "2x8", "A10G:2x24x96", "H100:2x52x468"], + title="Instance Type Id", + ), + ] = "1x2" + + +class ChainletEnvironmentSettings(BaseModel): + chainlet_name: Annotated[ + str, Field(description="Name of the chainlet", title="Chainlet Name") + ] + autoscaling_settings: Annotated[ + AutoscalingSettings | None, + Field( + description="Autoscaling settings for the chainlet. If null, it has not finished deploying" + ), + ] + instance_type: Annotated[ + InstanceType, Field(description="Instance type for the chainlet") + ] + + +class UpdateChainEnvironmentResponse(BaseModel): + ok: Annotated[ + bool, Field(description="Whether the update was successful", title="Ok") + ] + + +class PromoteToChainEnvironmentRequest(BaseModel): + scale_down_previous_deployment: Annotated[ + bool | None, + Field( + description="Whether to scale down the previous deployment after promoting", + examples=[True], + title="Scale Down Previous Deployment", + ), + ] = True + deployment_id: Annotated[ + str, + Field( + description="The id of the chain deployment to promote", + title="Deployment Id", + ), + ] + + +class ChainletEnvironmentAutoscalingSettingsUpdate(BaseModel): + chainlet_name: Annotated[ + str, + Field( + description="Name of the chainlet", + examples=["HelloWorld"], + title="Chainlet Name", + ), + ] + autoscaling_settings: Annotated[ + UpdateAutoscalingSettings, + Field( + description="Autoscaling settings for the chainlet", + examples=[ + { + "autoscaling_window": 800, + "concurrency_target": 3, + "max_replica": 2, + "min_replica": 1, + "scale_down_delay": 60, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + } + ], + ), + ] + + +class UpdateChainletEnvironmentAutoscalingSettingsRequest(BaseModel): + updates: Annotated[ + list[ChainletEnvironmentAutoscalingSettingsUpdate], + Field( + description="Mapping of chainlet name to the desired chainlet autoscaling settings. If the chainlet name doesn't exist, an error is returned.", + examples=[ + [ + { + "autoscaling_settings": { + "autoscaling_window": 800, + "concurrency_target": 4, + "max_replica": 3, + "min_replica": 2, + "scale_down_delay": 63, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + }, + "chainlet_name": "HelloWorld", + } + ], + [ + { + "autoscaling_settings": { + "autoscaling_window": None, + "concurrency_target": None, + "max_replica": None, + "min_replica": 0, + "scale_down_delay": None, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + }, + "chainlet_name": "HelloWorld", + }, + { + "autoscaling_settings": { + "autoscaling_window": None, + "concurrency_target": None, + "max_replica": None, + "min_replica": 0, + "scale_down_delay": None, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + }, + "chainlet_name": "RandInt", + }, + ], + ], + title="Updates", + ), + ] + + +class ChainletEnvironmentInstanceTypeUpdate(BaseModel): + chainlet_name: Annotated[ + str, + Field( + description="Name of the chainlet", + examples=["HelloWorld"], + title="Chainlet Name", + ), + ] + instance_type_id: Annotated[ + str, + Field( + description="Key of the instance type to use for the chainlet", + examples=["1x4", "2x8", "A10G:2x24x96"], + title="Instance Type Id", + ), + ] + + +class UpdateChainletEnvironmentInstanceTypeRequest(BaseModel): + updates: Annotated[ + list[ChainletEnvironmentInstanceTypeUpdate], + Field( + description="Mapping of chainlet name to the desired chainlet instance type. If the chainlet name doesn't exist, an error is returned.", + examples=[ + [ + {"chainlet_name": "HelloWorld", "instance_type_id": "1x4"}, + {"chainlet_name": "RandInt", "instance_type_id": "A10G:2x24x96"}, + ] + ], + title="Updates", + ), + ] + + +class UpsertTrainingProject(BaseModel): + name: Annotated[ + str, + Field( + description="Name of the training project.", + examples=["My Training Project"], + title="Name", + ), + ] + + +class UpsertTrainingProjectRequest(BaseModel): + training_project: Annotated[ + UpsertTrainingProject, Field(description="The training project to upsert.") + ] + + +class TrainingProjectSummary(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the training project.", title="Id") + ] + name: Annotated[ + str, Field(description="Name of the training project.", title="Name") + ] + + +class User(BaseModel): + email: Annotated[ + str | None, Field(description="Email of the user.", title="Email") + ] = None + + +class AwsOidcDockerAuth(BaseModel): + role_arn: Annotated[ + str, + Field(description="AWS IAM role ARN for OIDC authentication", title="Role Arn"), + ] + region: Annotated[ + str, Field(description="AWS region for OIDC authentication", title="Region") + ] + + +class CreateJobWeightConfig(BaseModel): + source: Annotated[ + str, + Field( + description="Weight source URI. Supported formats: hf://, s3://, gs://, r2://", + examples=[ + "hf://meta-llama/Llama-3-8B@main", + "s3://my-bucket/models/llama", + "gs://my-bucket/models/llama", + "r2://account_id.bucket/models/llama", + ], + title="Source", + ), + ] + mount_location: Annotated[ + str, + Field( + description="Path where weights will be mounted in the container", + examples=["/app/models/base", "/models/llama"], + title="Mount Location", + ), + ] + allow_patterns: Annotated[ + list[str] | None, + Field( + description="File patterns to include (Unix-style shell patterns)", + examples=[["*.safetensors", "config.json"]], + title="Allow Patterns", + ), + ] = None + ignore_patterns: Annotated[ + list[str] | None, + Field( + description="File patterns to exclude (Unix-style shell patterns)", + examples=[["*.bin", "*.h5"]], + title="Ignore Patterns", + ), + ] = None + auth_secret_name: Annotated[ + str | None, + Field( + description="Name of the workspace secret for authentication (e.g., HuggingFace token)", + examples=["hf_token", "aws_credentials"], + title="Auth Secret Name", + ), + ] = None + auth: Annotated[ + dict[str, Any] | None, + Field( + description="Authentication configuration for the weight source.", + examples=[{"auth_method": "CUSTOM_SECRET", "auth_secret_name": "hf_token"}], + title="Auth", + ), + ] = None + + +class CreateTrainingJobAccelerator(BaseModel): + accelerator: Annotated[ + str, + Field( + description="GPU type for the training job.", + examples=["H100"], + title="Accelerator", + ), + ] + count: Annotated[ + int, + Field( + description="GPUs needed for the training job.", examples=[2], title="Count" + ), + ] + + +class CreateTrainingJobCompute(BaseModel): + node_count: Annotated[ + int | None, + Field( + description="Number of nodes for the training job.", + examples=[1], + title="Node Count", + ), + ] = 1 + cpu_count: Annotated[ + int | None, + Field( + description="Number of cpus for the training job.", + examples=[1], + title="Cpu Count", + ), + ] = 1 + memory: Annotated[ + str | None, + Field( + description="Memory for the training job.", examples=["2Gi"], title="Memory" + ), + ] = "2Gi" + accelerator: Annotated[ + CreateTrainingJobAccelerator | None, + Field( + description="GPU specification for the training job", + examples=[{"accelerator": "H100", "count": 2}], + ), + ] = None + + +class GcpOidcDockerAuth(BaseModel): + service_account: Annotated[ + str, + Field( + description="GCP service account name for OIDC authentication", + title="Service Account", + ), + ] + workload_identity_provider: Annotated[ + str, + Field( + description="GCP workload identity provider for OIDC authentication", + title="Workload Identity Provider", + ), + ] + + +class InteractiveSessionConfig(BaseModel): + trigger: Annotated[ + V1InteractiveSessionTrigger | None, + Field( + description="When to create the interactive session. 'on_startup' creates on job start, 'on_failure' creates on job failure, 'on_demand' bypasses automatic session creation." + ), + ] = V1InteractiveSessionTrigger.on_demand + timeout_minutes: Annotated[ + int | None, + Field( + description="Number of minutes before the interactive session times out.", + examples=[480, 1440, 10080], + title="Timeout Minutes", + ), + ] = 480 + session_provider: Annotated[ + V1InteractiveSessionProvider | None, + Field(description="The IDE client for the interactive session."), + ] = V1InteractiveSessionProvider.vs_code + auth_provider: Annotated[ + V1InteractiveSessionAuthProvider | None, + Field(description="The authentication provider for the interactive session."), + ] = V1InteractiveSessionAuthProvider.github + + +class SecretReference(BaseModel): + name: Annotated[ + str, + Field( + description="Name of the secret to reference.", + examples=["hf_token"], + title="Name", + ), + ] + + +class TrainingJobTombstone(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the training job", title="Id") + ] + deleted: Annotated[ + bool, Field(description="Whether the training job was deleted", title="Deleted") + ] + training_project_id: Annotated[ + str, + Field( + description="Unique identifier of the training project", + title="Training Project Id", + ), + ] + + +class DownloadTrainingJobResponse(BaseModel): + artifact_presigned_urls: Annotated[ + list[str], + Field( + description="Presigned URL's for the artifacts", + title="Artifact Presigned Urls", + ), + ] + + +class GetTrainingJobLogsRequest(BaseModel): + start_epoch_millis: Annotated[ + int | None, + Field( + description="Epoch millis timestamp to start fetching logs", + title="Start Epoch Millis", + ), + ] = None + end_epoch_millis: Annotated[ + int | None, + Field( + description="Epoch millis timestamp to end fetching logs", + title="End Epoch Millis", + ), + ] = None + direction: Annotated[SortOrder | None, Field(description="Sort order for logs")] = ( + None + ) + limit: Annotated[ + Limit | None, + Field( + default_factory=lambda: Limit(500), + description="Limit of logs to fetch in a single request", + title="Limit", + ), + ] + + +class GetTrainingJobMetricsRequest(BaseModel): + end_epoch_millis: Annotated[ + int | None, + Field( + description="Epoch millis timestamp to end fetching metrics", + title="End Epoch Millis", + ), + ] = None + start_epoch_millis: Annotated[ + int | None, + Field( + description="Epoch millis timestamp to start fetching metrics.", + title="Start Epoch Millis", + ), + ] = None + + +class TrainingJobMetric(BaseModel): + value: Annotated[ + float, Field(description="The value of the metric.", title="Value") + ] + timestamp: Annotated[ + AwareDatetime, + Field( + description="The timestamp of the metric in ISO 8601 format.", + title="Timestamp", + ), + ] + + +class StopTrainingJobRequest(BaseModel): + pass + + +class TrainingJobCheckpoint(BaseModel): + training_job_id: Annotated[ + str, Field(description="The ID of the training job.", title="Training Job Id") + ] + checkpoint_id: Annotated[ + str, Field(description="The ID of the checkpoint.", title="Checkpoint Id") + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="The timestamp of the checkpoint in ISO 8601 format.", + title="Created At", + ), + ] + checkpoint_type: Annotated[ + str, Field(description="The type of checkpoint.", title="Checkpoint Type") + ] + base_model: Annotated[ + str | None, + Field(description="The base model of the checkpoint.", title="Base Model"), + ] + lora_adapter_config: Annotated[ + dict[str, Any] | None, + Field( + description="The adapter config of the checkpoint.", + title="Lora Adapter Config", + ), + ] + size_bytes: Annotated[ + int, + Field(description="The size of the checkpoint in bytes.", title="Size Bytes"), + ] + sync_status: Annotated[ + str | None, + Field( + description="Sync state of the checkpoint: SYNCING or COMPLETE.", + title="Sync Status", + ), + ] = None + + +class GetTrainingJobCheckpointFilesResponse(BaseModel): + presigned_urls: Annotated[ + list[CheckpointFile], + Field( + description="List of presigned URLs for checkpoint files.", + title="Presigned Urls", + ), + ] + next_page_token: Annotated[ + int | None, + Field( + description="Token to use for fetching the next page of results. None when there are no more results.", + title="Next Page Token", + ), + ] = None + total_count: Annotated[ + int, + Field( + description="Total number of checkpoint files available.", + title="Total Count", + ), + ] + + +class AuthCode(BaseModel): + session_id: Annotated[ + str, + Field( + description="Unique identifier of the interactive session.", + title="Session Id", + ), + ] + replica_id: Annotated[ + str, + Field( + description="Replica identifier in gXXrY format (e.g., 'g00r0' for group 0, replica 0).", + title="Replica Id", + ), + ] + auth_code: Annotated[ + str, + Field( + description="The device authentication code (e.g., '4F64-C0D9').", + title="Auth Code", + ), + ] + auth_url: Annotated[ + str, + Field( + description="URL where the user should enter the auth code (e.g., 'https://github.com/login/device').", + title="Auth Url", + ), + ] + generated_at: Annotated[ + AwareDatetime | None, + Field( + description="When the auth code was generated, in ISO 8601 format.", + title="Generated At", + ), + ] = None + tunnel_name: Annotated[ + str | None, + Field(description="The name of the tunnel node.", title="Tunnel Name"), + ] = None + expires_at: Annotated[ + AwareDatetime | None, + Field( + description="When the session expires, in ISO 8601 format.", + title="Expires At", + ), + ] = None + working_directory: Annotated[ + str | None, + Field( + description="The working directory of the session.", + title="Working Directory", + ), + ] = None + + +class GetAuthCodesResponse(BaseModel): + auth_codes: Annotated[ + list[AuthCode], + Field( + description="List of auth codes for each node that has an active interactive session.", + title="Auth Codes", + ), + ] + + +class PatchInteractiveSessionRequest(BaseModel): + timeout_minutes: Annotated[ + int | None, + Field( + description="For on_startup sessions, minutes to add to the expiration. For on_demand/on_failure sessions, minutes to add to the timeout. Use -1 for infinite timeout (bumps by 10 years).", + title="Timeout Minutes", + ), + ] = None + trigger: Annotated[ + V1InteractiveSessionTrigger | None, + Field( + description="Update when the interactive session is created. Cannot be changed if the session trigger is 'on_startup'." + ), + ] = None + + +class InteractiveSession(BaseModel): + id: Annotated[ + str, + Field(description="Unique identifier of the interactive session.", title="Id"), + ] + trigger: Annotated[ + str, + Field(description="When the interactive session is created.", title="Trigger"), + ] + timeout_minutes: Annotated[ + int, + Field( + description="Minutes before the session times out.", title="Timeout Minutes" + ), + ] + session_provider: Annotated[ + str, + Field(description="The IDE client for the session.", title="Session Provider"), + ] + auth_provider: Annotated[ + str, + Field( + description="The authentication provider for the session.", + title="Auth Provider", + ), + ] + pod_name: Annotated[ + str, + Field(description="Pod name / node rank for the session.", title="Pod Name"), + ] + expires_at: Annotated[ + AwareDatetime | None, + Field( + description="When the session expires, in ISO 8601 format.", + title="Expires At", + ), + ] = None + tunnel_name: Annotated[ + str | None, + Field(description="The tunnel name for the session.", title="Tunnel Name"), + ] = None + auth_code: Annotated[ + str | None, + Field(description="The device authentication code.", title="Auth Code"), + ] = None + auth_url: Annotated[ + str | None, + Field( + description="URL where the user should enter the auth code.", + title="Auth Url", + ), + ] = None + auth_code_generated_at: Annotated[ + AwareDatetime | None, + Field( + description="When the auth code was generated.", + title="Auth Code Generated At", + ), + ] = None + authenticated_at: Annotated[ + AwareDatetime | None, + Field( + description="When the session was authenticated.", title="Authenticated At" + ), + ] = None + working_directory: Annotated[ + str | None, + Field( + description="The working directory of the session.", + title="Working Directory", + ), + ] = None + + +class PatchInteractiveSessionResponse(BaseModel): + interactive_session: Annotated[ + InteractiveSession, Field(description="The updated interactive session.") + ] + message: Annotated[ + str, + Field( + description="Human-readable summary of what was updated.", title="Message" + ), + ] + + +class GetCacheSummaryResponse(BaseModel): + timestamp: Annotated[ + str, + Field( + description="Timestamp when the cache summary was captured", + title="Timestamp", + ), + ] + project_id: Annotated[ + str, + Field(description="Project ID associated with the cache", title="Project Id"), + ] + file_summaries: Annotated[ + list[FileSummary], + Field(description="List of files in the cache", title="File Summaries"), + ] + + +class TrainingProjectTombstone(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the training project", title="Id") + ] + deleted: Annotated[ + bool, + Field(description="Whether the training project was deleted", title="Deleted"), + ] + + +class OrderBy(BaseModel): + field: Annotated[ + str, + Field( + description="The field to order by.", examples=["created_at"], title="Field" + ), + ] + order: Annotated[ + str, + Field( + description="The direction to order by.", + examples=["asc", "desc"], + title="Order", + ), + ] + + +class SearchTrainingJobsRequest(BaseModel): + project_id: Annotated[ + str | None, + Field( + description="Filter the training jobs by project ID.", + examples=["n4q95w5"], + title="Project Id", + ), + ] = None + job_id: Annotated[ + str | None, + Field( + description="Filter the training jobs by job ID.", + examples=["p7qr9qv"], + title="Job Id", + ), + ] = None + statuses: Annotated[ + list[str] | None, + Field( + description="Filter the training jobs by status.", + examples=[["TRAINING_JOB_RUNNING", "TRAINING_JOB_COMPLETED"]], + title="Statuses", + ), + ] = None + order_by: Annotated[ + list[OrderBy] | None, + Field( + default_factory=lambda: [ + OrderBy.model_validate(v) + for v in [{"field": "created_at", "order": "desc"}] + ], + description="Order the training jobs by a field. Currently supports created_at", + title="Order By", + ), + ] + + +class AWSCredentials(BaseModel): + aws_access_key_id: Annotated[ + str, Field(description="The AWS access key ID", title="Aws Access Key Id") + ] + aws_secret_access_key: Annotated[ + str, + Field(description="The AWS secret access key", title="Aws Secret Access Key"), + ] + aws_session_token: Annotated[ + str, Field(description="The AWS session token", title="Aws Session Token") + ] + + +class GetBlobCredentialsResponse(BaseModel): + creds: Annotated[ + AWSCredentials, Field(description="The credentials to upload the blob to") + ] + s3_key: Annotated[ + str, Field(description="The S3 key to upload the blob to", title="S3 Key") + ] + s3_bucket: Annotated[ + str, Field(description="The S3 bucket to upload the blob to", title="S3 Bucket") + ] + + +class CreateAPIKeyRequest(BaseModel): + name: Annotated[ + str | None, + Field( + description="Optional name for the API key", + examples=["my-api-key"], + title="Name", + ), + ] = None + type: Annotated[ + APIKeyCategory, + Field( + description="Type of the API key.", + examples=[ + "PERSONAL", + "WORKSPACE_EXPORT_METRICS", + "WORKSPACE_INVOKE", + "WORKSPACE_MANAGE_ALL", + ], + ), + ] + model_ids: Annotated[ + list[str] | None, + Field( + description="List of model IDs to scope the API key to, only present if type is 'WORKSPACE_EXPORT_METRICS' or 'WORKSPACE_INVOKE'", + examples=[["aaaaaaaa"]], + title="Model Ids", + ), + ] = None + + +class APIKey(BaseModel): + api_key: Annotated[str, Field(description="The API key string", title="Api Key")] + + +class APIKeyInfo(BaseModel): + prefix: Annotated[ + str, Field(description="The prefix of the API key", title="Prefix") + ] + name: Annotated[ + str | None, + Field( + description="Optional name for the API key", + examples=["my-api-key"], + title="Name", + ), + ] = None + type: Annotated[ + APIKeyCategory, + Field( + description="Type of the API key.", + examples=[ + "PERSONAL", + "WORKSPACE_EXPORT_METRICS", + "WORKSPACE_INVOKE", + "WORKSPACE_MANAGE_ALL", + ], + ), + ] + model_ids: Annotated[ + list[str] | None, + Field( + description="List of model IDs to scope the API key to, only present if type is 'WORKSPACE_EXPORT_METRICS' or 'WORKSPACE_INVOKE'", + examples=[["aaaaaaaa"]], + title="Model Ids", + ), + ] = None + team_name: Annotated[ + str | None, + Field( + description="The name of the team associated with the API key", + title="Team Name", + ), + ] = None + + +class APIKeys(BaseModel): + keys: Annotated[ + list[APIKeyInfo], + Field(description="A list of API key information", title="Keys"), + ] + + +class APIKeyTombstone(BaseModel): + prefix: Annotated[ + str, Field(description="Unique prefix of the API key", title="Prefix") + ] + + +class ModelWeightSnapshot(BaseModel): + model: Annotated[ + str, Field(description="Unique identifier of the model", title="Model") + ] + snapshot_uri: Annotated[ + str, + Field(description="Path to the model weight snapshot", title="Snapshot Uri"), + ] + received_at: Annotated[ + AwareDatetime, Field(description="Time of the snapshot", title="Received At") + ] + + +class CreateModelWeightSnapshotRequest(BaseModel): + model: Annotated[ + str, Field(description="Unique identifier of the model", title="Model") + ] + snapshot_uri: Annotated[ + str, + Field(description="Path to the model weight snapshot", title="Snapshot Uri"), + ] + + +class CreateLLMModelRequest(BaseModel): + resources: Annotated[ + dict[str, Any], + Field(description="Resources allocated to the model", title="Resources"), + ] + llm_version: Annotated[ + str | None, + Field(description="Version of the helm chart to use", title="Llm Version"), + ] = "1.0" + llm_config: Annotated[ + dict[str, Any] | None, + Field( + description="Configuration specific to the LLM model", title="Llm Config" + ), + ] = None + environment_variables: Annotated[ + dict[str, Any] | None, + Field( + description="Environment variables for the model", + title="Environment Variables", + ), + ] = None + autoscaling_settings: Annotated[ + UpdateAutoscalingSettings | None, + Field( + description="Autoscaling settings for the model", + examples=[ + { + "autoscaling_window": 600, + "concurrency_target": None, + "max_replica": 5, + "min_replica": 1, + "scale_down_delay": 300, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + } + ], + ), + ] = None + additional_autoscaling_config: Annotated[ + dict[str, Any] | None, + Field( + description="Additional autoscaling configuration (e.g. target in-flight tokens)", + examples=[{"metrics": [{"name": "in_flight_tokens", "target": 40000}]}], + title="Additional Autoscaling Config", + ), + ] = None + metadata: Annotated[ + dict[str, Any] | None, + Field( + description="User-defined metadata for the deployment", + examples=[{"environment": "production", "git_sha": "abc123"}], + title="Metadata", + ), + ] = None + name: Annotated[str, Field(description="Name of the model", title="Name")] + + +class LLMModel(BaseModel): + id: Annotated[str, Field(description="Unique identifier of the model", title="Id")] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the model was created in ISO 8601 format", + title="Created At", + ), + ] + name: Annotated[str, Field(description="Name of the model", title="Name")] + + +class CreateLLMModelVersionRequest(BaseModel): + resources: Annotated[ + dict[str, Any], + Field(description="Resources allocated to the model", title="Resources"), + ] + llm_version: Annotated[ + str | None, + Field(description="Version of the helm chart to use", title="Llm Version"), + ] = "1.0" + llm_config: Annotated[ + dict[str, Any] | None, + Field( + description="Configuration specific to the LLM model", title="Llm Config" + ), + ] = None + environment_variables: Annotated[ + dict[str, Any] | None, + Field( + description="Environment variables for the model", + title="Environment Variables", + ), + ] = None + autoscaling_settings: Annotated[ + UpdateAutoscalingSettings | None, + Field( + description="Autoscaling settings for the model", + examples=[ + { + "autoscaling_window": 600, + "concurrency_target": None, + "max_replica": 5, + "min_replica": 1, + "scale_down_delay": 300, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + } + ], + ), + ] = None + additional_autoscaling_config: Annotated[ + dict[str, Any] | None, + Field( + description="Additional autoscaling configuration (e.g. target in-flight tokens)", + examples=[{"metrics": [{"name": "in_flight_tokens", "target": 40000}]}], + title="Additional Autoscaling Config", + ), + ] = None + metadata: Annotated[ + dict[str, Any] | None, + Field( + description="User-defined metadata for the deployment", + examples=[{"environment": "production", "git_sha": "abc123"}], + title="Metadata", + ), + ] = None + + +class LLMModelVersion(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the model version", title="Id") + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the model was created in ISO 8601 format", + title="Created At", + ), + ] + name: Annotated[str, Field(description="Name of the model version", title="Name")] + + +class LibraryListing(BaseModel): + display_name: Annotated[ + str, + Field(description="Display name of the library listing", title="Display Name"), + ] + user_defined_id: Annotated[ + str, + Field( + description="User-defined identifier of the library listing", + title="User Defined Id", + ), + ] + is_public: Annotated[ + bool, + Field( + description="Whether the listing is publicly accessible", title="Is Public" + ), + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the listing was created in ISO 8601 format", + title="Created At", + ), + ] + modified_at: Annotated[ + AwareDatetime, + Field(description="Time the listing was last modified", title="Modified At"), + ] + + +class LibraryListings(BaseModel): + listings: Annotated[list[LibraryListing], Field(title="Listings")] + + +class CreateLibraryListingRequest(BaseModel): + display_name: Annotated[ + str, + Field(description="Display name of the library listing", title="Display Name"), + ] + user_defined_id: Annotated[ + str, + Field( + description="User-defined identifier of the library listing", + title="User Defined Id", + ), + ] + is_public: Annotated[ + bool | None, + Field( + description="Whether the listing is publicly accessible", title="Is Public" + ), + ] = False + + +class LibraryListingTombstone(BaseModel): + user_defined_id: Annotated[ + str, + Field( + description="User-defined identifier of the library listing", + title="User Defined Id", + ), + ] + deleted: Annotated[ + bool, + Field(description="Whether the library listing was deleted", title="Deleted"), + ] + + +class UpdateLibraryListingRequest(BaseModel): + display_name: Annotated[ + str | None, + Field( + description="New display name for the library listing", title="Display Name" + ), + ] = None + is_public: Annotated[ + bool | None, + Field( + description="Whether the listing is publicly accessible", title="Is Public" + ), + ] = None + + +class LibraryListingVersion(BaseModel): + version_tag: Annotated[ + str, + Field(description="Human-readable tag for this version", title="Version Tag"), + ] + is_live: Annotated[ + bool, + Field(description="Whether this version is the live version", title="Is Live"), + ] + allow_truss_download: Annotated[ + bool, + Field( + description="Whether users deploying this model can download the Truss", + title="Allow Truss Download", + ), + ] + oracle_version_id: Annotated[ + str, + Field(description="Id of the source model version", title="Oracle Version Id"), + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the version was created in ISO 8601 format", + title="Created At", + ), + ] + modified_at: Annotated[ + AwareDatetime, + Field(description="Time the version was last modified", title="Modified At"), + ] + + +class LibraryListingVersions(BaseModel): + versions: Annotated[list[LibraryListingVersion], Field(title="Versions")] + + +class CreateLibraryListingVersionRequest(BaseModel): + display_name: Annotated[ + str | None, + Field( + description="Display name of the library listing. Required when creating a new listing.", + title="Display Name", + ), + ] = None + is_public: Annotated[ + bool | None, + Field( + description="Whether the listing is publicly accessible. Only used when creating a new listing.", + title="Is Public", + ), + ] = False + oracle_version_id: Annotated[ + str, + Field( + description="Id of the source model version to publish", + title="Oracle Version Id", + ), + ] + allow_truss_download: Annotated[ + bool | None, + Field( + description="Whether users deploying this model can download the Truss", + title="Allow Truss Download", + ), + ] = False + version_tag: Annotated[ + str, + Field(description="Human-readable tag for this version", title="Version Tag"), + ] + + +class LibraryListingVersionTombstone(BaseModel): + version_tag: Annotated[ + str, + Field(description="Human-readable tag for this version", title="Version Tag"), + ] + deleted: Annotated[ + bool, + Field( + description="Whether the library listing version was deleted", + title="Deleted", + ), + ] + + +class UpdateLibraryListingVersionRequest(BaseModel): + is_live: Annotated[ + bool | None, + Field( + description="Whether this version should be the live version. Setting to true demotes the current live version.", + title="Is Live", + ), + ] = None + allow_truss_download: Annotated[ + bool | None, + Field( + description="Whether users deploying this model can download the Truss", + title="Allow Truss Download", + ), + ] = None + + +class ChainMetadata(BaseModel): + chain_id: Annotated[ + str, Field(description="Unique identifier of the chain", title="Chain Id") + ] + chain_name: Annotated[ + str | None, Field(description="Name of the chain", title="Chain Name") + ] = None + chain_deployment_id: Annotated[ + str, + Field( + description="Unique identifier of the chain deployment", + title="Chain Deployment Id", + ), + ] + + +class Subtotal(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Subtotal cost incurred on this date in dollars", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Subtotal", + ), + ] + + +class DailyDedicatedUsage(BaseModel): + date: Annotated[date_aliased, Field(description="Date of the usage", title="Date")] + subtotal: Annotated[ + float | Subtotal, + Field( + description="Subtotal cost incurred on this date in dollars", + title="Subtotal", + ), + ] + minutes: Annotated[ + int, Field(description="Minutes used on this date", title="Minutes") + ] + inference_requests: Annotated[ + int, + Field( + description="Number of inference requests on this date", + title="Inference Requests", + ), + ] + + +class DailyModelApiUsage(BaseModel): + date: Annotated[date_aliased, Field(description="Date of the usage", title="Date")] + subtotal: Annotated[ + float | Subtotal, + Field( + description="Subtotal cost incurred on this date in dollars", + title="Subtotal", + ), + ] + input_tokens: Annotated[ + int, + Field(description="Number of input tokens on this date", title="Input Tokens"), + ] + output_tokens: Annotated[ + int, + Field( + description="Number of output tokens on this date", title="Output Tokens" + ), + ] + cached_input_tokens: Annotated[ + int, + Field( + description="Number of cached input tokens on this date", + title="Cached Input Tokens", + ), + ] + + +class DailyTrainingUsage(BaseModel): + date: Annotated[date_aliased, Field(description="Date of the usage", title="Date")] + subtotal: Annotated[ + float | Subtotal, + Field( + description="Subtotal cost incurred on this date in dollars", + title="Subtotal", + ), + ] + minutes: Annotated[ + int, Field(description="Minutes used on this date", title="Minutes") + ] + + +class Subtotal3(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Subtotal cost in dollars for this billable resource", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Subtotal", + ), + ] + + +class Subtotal4(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Subtotal cost in dollars after applying credits used", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Subtotal", + ), + ] + + +class CreditsUsed(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Credits applied in dollars", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Credits Used", + ), + ] + + +class Total(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Total cost in dollars", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Total", + ), + ] + + +class Subtotal5(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Subtotal cost in dollars for this model", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Subtotal", + ), + ] + + +class ModelApiItem(BaseModel): + model_name: Annotated[str, Field(description="Model name", title="Model Name")] + model_family: Annotated[ + str | None, + Field(description="Model family (e.g., llama, mistral)", title="Model Family"), + ] = None + subtotal: Annotated[ + float | Subtotal5, + Field(description="Subtotal cost in dollars for this model", title="Subtotal"), + ] + input_tokens: Annotated[ + int, + Field(description="Total input tokens for this model", title="Input Tokens"), + ] + output_tokens: Annotated[ + int, + Field(description="Total output tokens for this model", title="Output Tokens"), + ] + cached_input_tokens: Annotated[ + int, + Field( + description="Total cached input tokens for this model", + title="Cached Input Tokens", + ), + ] + daily: Annotated[ + list[DailyModelApiUsage] | None, + Field(description="Daily usage breakdown", title="Daily"), + ] = None + + +class Subtotal6(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Subtotal cost in dollars after applying credits used", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Subtotal", + ), + ] + + +class ModelApisUsage(BaseModel): + subtotal: Annotated[ + float | Subtotal6, + Field( + description="Subtotal cost in dollars after applying credits used", + title="Subtotal", + ), + ] + credits_used: Annotated[ + float | CreditsUsed, + Field(description="Credits applied in dollars", title="Credits Used"), + ] + total: Annotated[ + float | Total, Field(description="Total cost in dollars", title="Total") + ] + breakdown: Annotated[ + list[ModelApiItem] | None, + Field(description="Per-model usage breakdown", title="Breakdown"), + ] = None + + +class Subtotal7(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Subtotal cost in dollars for this billable resource", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Subtotal", + ), + ] + + +class Subtotal8(RootModel[str]): + model_config = ConfigDict( + regex_engine="python-re", + ) + root: Annotated[ + str, + Field( + description="Subtotal cost in dollars after applying credits used", + pattern="^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + title="Subtotal", + ), + ] + + +class RollingDeployConfig(BaseModel): + rolling_deploy_strategy: Annotated[ + RollingDeployStrategy | None, + Field( + description="The rolling deploy strategy to use for promotions.", + examples=["REPLICA"], + ), + ] = RollingDeployStrategy.REPLICA + max_surge_percent: Annotated[ + int | None, + Field( + description="The maximum surge percentage for rolling deploys.", + examples=[10], + title="Max Surge Percent", + ), + ] = 10 + max_unavailable_percent: Annotated[ + int | None, + Field( + description="The maximum unavailable percentage for rolling deploys.", + examples=[10], + title="Max Unavailable Percent", + ), + ] = 0 + stabilization_time_seconds: Annotated[ + int | None, + Field( + description="The stabilization time in seconds for rolling deploys.", + examples=[300], + title="Stabilization Time Seconds", + ), + ] = 0 + replica_overhead_percent: Annotated[ + int | None, + Field( + description="The replica overhead percentage for rolling deploys.", + examples=[0], + title="Replica Overhead Percent", + ), + ] = 0 + + +class UpdatePromotionSettings(BaseModel): + redeploy_on_promotion: Annotated[ + bool | None, + Field( + description="Whether to deploy on all promotions. Enabling this flag allows model code to safely handle environment-specific logic. When a deployment is promoted, a new deployment will be created with a copy of the image.", + examples=[True], + title="Redeploy On Promotion", + ), + ] = None + rolling_deploy: Annotated[ + bool | None, + Field( + description="Whether the environment should rely on rolling deploy orchestration.", + examples=[True], + title="Rolling Deploy", + ), + ] = None + promotion_cleanup_strategy: Annotated[ + PromotionCleanupStrategy | None, + Field( + description="The cleanup strategy to use after a promotion completes.", + examples=["SCALE_TO_ZERO"], + ), + ] = None + rolling_deploy_config: Annotated[ + UpdateRollingDeployConfig | None, + Field(description="Rolling deploy configuration for promotions"), + ] = None + ramp_up_while_promoting: Annotated[ + bool | None, + Field( + description="Whether to ramp up traffic while promoting", + examples=[True], + title="Ramp Up While Promoting", + ), + ] = None + ramp_up_duration_seconds: Annotated[ + int | None, + Field( + description="Duration of the ramp up in seconds", + examples=[600], + title="Ramp Up Duration Seconds", + ), + ] = None + + +class CreateEnvironmentRequest(BaseModel): + name: Annotated[ + str, + Field( + description="Name of the environment", examples=["staging"], title="Name" + ), + ] + autoscaling_settings: Annotated[ + UpdateAutoscalingSettings | None, + Field( + description="Autoscaling settings for the environment", + examples=[ + { + "autoscaling_window": 800, + "concurrency_target": 3, + "max_replica": 2, + "min_replica": 1, + "scale_down_delay": 60, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + } + ], + ), + ] = None + promotion_settings: Annotated[ + UpdatePromotionSettings | None, + Field( + description="Promotion settings for the environment", + examples=[ + { + "promotion_cleanup_strategy": None, + "ramp_up_duration_seconds": 600, + "ramp_up_while_promoting": True, + "redeploy_on_promotion": True, + "rolling_deploy": True, + "rolling_deploy_config": None, + } + ], + ), + ] = None + + +class UpdateEnvironmentRequest(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + autoscaling_settings: Annotated[ + UpdateAutoscalingSettings | None, + Field( + description="Autoscaling settings for the environment", + examples=[ + { + "autoscaling_window": 800, + "concurrency_target": 3, + "max_replica": 2, + "min_replica": 1, + "scale_down_delay": 60, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + } + ], + ), + ] = None + promotion_settings: Annotated[ + UpdatePromotionSettings | None, + Field( + description="Promotion settings for the environment", + examples=[ + { + "promotion_cleanup_strategy": None, + "ramp_up_duration_seconds": 600, + "ramp_up_while_promoting": True, + "redeploy_on_promotion": True, + "rolling_deploy": None, + "rolling_deploy_config": None, + } + ], + ), + ] = None + + +class ChainDeployment(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the chain deployment", title="Id") + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the chain deployment was created in ISO 8601 format", + title="Created At", + ), + ] + chain_id: Annotated[ + str, Field(description="Unique identifier of the chain", title="Chain Id") + ] + environment: Annotated[ + str | None, + Field( + description="Environment the chain deployment is deployed in", + title="Environment", + ), + ] + chainlets: Annotated[ + list[Chainlet], + Field(description="Chainlets in the chain deployment", title="Chainlets"), + ] + status: Annotated[ + DeploymentStatus, Field(description="Status of the chain deployment") + ] + + +class ChainDeployments(BaseModel): + deployments: Annotated[ + list[ChainDeployment], + Field(description="A list of chain deployments", title="Deployments"), + ] + + +class CreateChainEnvironmentRequest(BaseModel): + name: Annotated[ + str, + Field( + description="Name of the environment", examples=["staging"], title="Name" + ), + ] + promotion_settings: Annotated[ + UpdatePromotionSettings | None, + Field( + description="Promotion settings for the environment", + examples=[ + { + "promotion_cleanup_strategy": None, + "ramp_up_duration_seconds": 600, + "ramp_up_while_promoting": True, + "redeploy_on_promotion": True, + "rolling_deploy": None, + "rolling_deploy_config": None, + } + ], + ), + ] = None + chainlet_settings: Annotated[ + list[ChainletEnvironmentSettingsRequest] | None, + Field( + description="Mapping of chainlet name to the desired chainlet environment settings", + examples=[ + [ + { + "autoscaling_settings": { + "autoscaling_window": 800, + "concurrency_target": 4, + "max_replica": 3, + "min_replica": 2, + "scale_down_delay": 63, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + }, + "chainlet_name": "HelloWorld", + "instance_type_id": "2x8", + }, + { + "autoscaling_settings": { + "autoscaling_window": None, + "concurrency_target": None, + "max_replica": 3, + "min_replica": 3, + "scale_down_delay": None, + "target_in_flight_tokens": None, + "target_utilization_percentage": None, + }, + "chainlet_name": "RandInt", + "instance_type_id": "A10Gx8x32", + }, + ] + ], + title="Chainlet Settings", + ), + ] = None + + +class UpdateChainEnvironmentRequest(BaseModel): + promotion_settings: Annotated[ + UpdatePromotionSettings | None, + Field( + description="Promotion settings for the environment", + examples=[ + { + "promotion_cleanup_strategy": None, + "ramp_up_duration_seconds": 600, + "ramp_up_while_promoting": True, + "redeploy_on_promotion": None, + "rolling_deploy": None, + "rolling_deploy_config": None, + } + ], + ), + ] = None + + +class UpdateChainletEnvironmentInstanceTypeResponse(BaseModel): + requires_redeployment: Annotated[ + bool, + Field( + description="Whether the resource update requires a re-deployment to update the instance type.", + title="Requires Redeployment", + ), + ] + chain_deployment: Annotated[ + ChainDeployment | None, + Field( + description="The chain deployment resulting from the resource update, if any." + ), + ] + chainlet_environment_settings: Annotated[ + list[ChainletEnvironmentSettings], + Field( + description="The updated chainlet environment settings", + title="Chainlet Environment Settings", + ), + ] + + +class TrainingJob(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the training job.", title="Id") + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the job was created in ISO 8601 format.", + title="Created At", + ), + ] + current_status: Annotated[ + str, + Field( + description="Current status of the training job.", title="Current Status" + ), + ] + error_message: Annotated[ + str | None, + Field( + description="Error message if the training job failed.", + title="Error Message", + ), + ] = None + instance_type: Annotated[ + InstanceType, Field(description="Instance type of the training job.") + ] + updated_at: Annotated[ + AwareDatetime, + Field( + description="Time the job was updated in ISO 8601 format.", + title="Updated At", + ), + ] + training_project_id: Annotated[ + str, + Field(description="ID of the training project.", title="Training Project Id"), + ] + training_project: Annotated[ + TrainingProjectSummary, Field(description="Summary of the training project.") + ] + name: Annotated[ + str | None, + Field( + description="Name of the training job.", + examples=["gpt-oss-job"], + title="Name", + ), + ] = None + checkpoint_sync_status: Annotated[ + CheckpointSyncStatus | None, + Field(description="Checkpoint sync status of the training job."), + ] = None + user: Annotated[ + User | None, Field(description="The user who created the training job.") + ] = None + + +class TrainingProject(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the training project", title="Id") + ] + name: Annotated[ + str, Field(description="Name of the training project.", title="Name") + ] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the training project was created in ISO 8601 format.", + title="Created At", + ), + ] + updated_at: Annotated[ + AwareDatetime, + Field( + description="Time the training project was updated in ISO 8601 format.", + title="Updated At", + ), + ] + team_name: Annotated[ + str | None, + Field( + description="Name of the team associated with the training project.", + title="Team Name", + ), + ] = None + latest_job: Annotated[ + TrainingJob | None, + Field( + description="Most recently created training job for the training project." + ), + ] + + +class UpsertTrainingProjectResponse(BaseModel): + training_project: Annotated[ + TrainingProject, Field(description="The upserted training project.") + ] + + +class ListTrainingProjectsResponse(BaseModel): + training_projects: Annotated[ + list[TrainingProject], + Field(description="List of training projects.", title="Training Projects"), + ] + + +class ListTrainingJobsResponse(BaseModel): + training_project: Annotated[ + TrainingProject, Field(description="The training project.") + ] + training_jobs: Annotated[ + list[TrainingJob], + Field(description="List of training jobs.", title="Training Jobs"), + ] + + +class AwsIamDockerAuth(BaseModel): + access_key_secret_ref: Annotated[ + SecretReference, Field(description="Name of the access key secret") + ] + secret_access_key_secret_ref: Annotated[ + SecretReference, Field(description="Name of the secret key secret") + ] + + +class CreateTrainingJobRuntime(BaseModel): + start_commands: Annotated[ + list[str] | None, + Field( + description="Commands to execute when starting the runtime.", + examples=[["python main.py"]], + title="Start Commands", + ), + ] = None + environment_variables: Annotated[ + dict[str, str | SecretReference] | None, + Field( + description="Environment variables to set in the runtime.", + examples=[{"API_KEY": "your_api_key_here", "PATH": "/usr/bin"}], + title="Environment Variables", + ), + ] = None + artifacts: Annotated[ + list[CreateTrainingJobS3Artifact] | None, + Field(description="Runtime artifacts for the training job.", title="Artifacts"), + ] = None + enable_cache: Annotated[ + bool | None, + Field( + description="Deprecated. Use cache_config instead.", + examples=[True], + title="Enable Cache", + ), + ] = None + cache_config: Annotated[ + CreateTrainingJobCacheConfig | None, + Field( + description="Configuration for the read-write cache.", + examples=[ + { + "enable_legacy_hf_mount": True, + "enabled": True, + "mount_base_path": "/root/.cache", + "require_cache_affinity": True, + } + ], + ), + ] = None + checkpointing_config: Annotated[ + CreateTrainingJobCheckpointingConfig | None, + Field( + description="Configuration for checkpointing.", + examples=[ + { + "checkpoint_path": "/mnt/ckpts", + "enabled": True, + "volume_size_gib": None, + } + ], + ), + ] = None + load_checkpoint_config: Annotated[ + LoadCheckpointConfig | None, + Field(description="Configuration for loading checkpoints"), + ] = None + + +class GcpServiceAccountJsonDockerAuth(BaseModel): + service_account_json_secret_ref: Annotated[ + SecretReference, Field(description="Name of the service account secret") + ] + + +class RegistrySecretDockerAuth(BaseModel): + secret_ref: Annotated[ + SecretReference, + Field( + description="Reference to a Baseten secret containing credentials in the format 'username:password'" + ), + ] + + +class CreateTrainingJobResponse(BaseModel): + training_job: Annotated[TrainingJob, Field(description="The created training job.")] + + +class GetTrainingJobResponse(BaseModel): + training_project: Annotated[ + TrainingProject, Field(description="The training project.") + ] + training_job: Annotated[TrainingJob, Field(description="The fetched training job.")] + + +class RecreateTrainingJobResponse(BaseModel): + training_job: Annotated[TrainingJob, Field(description="The created training job.")] + + +class StorageMetrics(BaseModel): + usage_bytes: Annotated[ + list[TrainingJobMetric], + Field( + description="The number of bytes used on the storage entity.", + title="Usage Bytes", + ), + ] + utilization: Annotated[ + list[TrainingJobMetric], + Field( + description="The utilization of the storage entity as a decimal percentage.", + title="Utilization", + ), + ] + + +class TrainingJobMetrics(BaseModel): + gpu_memory_usage_bytes: Annotated[ + dict[str, list[TrainingJobMetric]], + Field( + description="A map of GPU rank to memory usage for the training job. For multinode jobs, this is the memory usage of the leader unless specified otherwise.", + title="Gpu Memory Usage Bytes", + ), + ] + gpu_utilization: Annotated[ + dict[str, list[TrainingJobMetric]], + Field( + description="A map of GPU rank to fractional GPU utilization. For multinode jobs, this is the GPU utilization of the leader unless specified otherwise.", + title="Gpu Utilization", + ), + ] + cpu_usage: Annotated[ + list[TrainingJobMetric], + Field( + description="The CPU usage measured in cores. For multinode jobs, this is the CPU usage of the leader unless specified otherwise.", + title="Cpu Usage", + ), + ] + cpu_memory_usage_bytes: Annotated[ + list[TrainingJobMetric], + Field( + description="The CPU memory usage for the training job. For multinode jobs, this is the CPU memory usage of the leader unless specified otherwise.", + title="Cpu Memory Usage Bytes", + ), + ] + ephemeral_storage: Annotated[ + StorageMetrics, + Field( + description="The storage usage for the ephemeral storage. For multinode jobs, this is the ephemeral storage usage of the leader unless specified otherwise." + ), + ] + + +class TrainingJobNodeMetrics(BaseModel): + node_id: Annotated[str, Field(description="The name of the node.", title="Node Id")] + metrics: Annotated[ + TrainingJobMetrics, Field(description="The metrics for the node.") + ] + + +class GetTrainingJobMetricsResponse(BaseModel): + gpu_memory_usage_bytes: Annotated[ + dict[str, list[TrainingJobMetric]], + Field( + description="A map of GPU rank to memory usage for the training job. For multinode jobs, this is the memory usage of the leader unless specified otherwise.", + title="Gpu Memory Usage Bytes", + ), + ] + gpu_utilization: Annotated[ + dict[str, list[TrainingJobMetric]], + Field( + description="A map of GPU rank to fractional GPU utilization. For multinode jobs, this is the GPU utilization of the leader unless specified otherwise.", + title="Gpu Utilization", + ), + ] + cpu_usage: Annotated[ + list[TrainingJobMetric], + Field( + description="The CPU usage measured in cores. For multinode jobs, this is the CPU usage of the leader unless specified otherwise.", + title="Cpu Usage", + ), + ] + cpu_memory_usage_bytes: Annotated[ + list[TrainingJobMetric], + Field( + description="The CPU memory usage for the training job. For multinode jobs, this is the CPU memory usage of the leader unless specified otherwise.", + title="Cpu Memory Usage Bytes", + ), + ] + ephemeral_storage: Annotated[ + StorageMetrics, + Field( + description="The storage usage for the ephemeral storage. For multinode jobs, this is the ephemeral storage usage of the leader unless specified otherwise." + ), + ] + training_job: Annotated[TrainingJob, Field(description="The training job.")] + cache: Annotated[ + StorageMetrics | None, + Field(description="The storage usage for the read-write cache."), + ] + per_node_metrics: Annotated[ + list[TrainingJobNodeMetrics], + Field( + description="The metrics for each node in the training job.", + title="Per Node Metrics", + ), + ] + + +class StopTrainingJobResponse(BaseModel): + training_job: Annotated[TrainingJob, Field(description="The stopped training job.")] + + +class GetTrainingJobCheckpointsResponse(BaseModel): + training_job: Annotated[TrainingJob, Field(description="The training job.")] + checkpoints: Annotated[ + list[TrainingJobCheckpoint], + Field(description="The checkpoints for the training job.", title="Checkpoints"), + ] + + +class GetTrainingProjectResponse(BaseModel): + training_project: Annotated[ + TrainingProject, Field(description="The training project.") + ] + + +class SearchTrainingJobsResponse(BaseModel): + training_jobs: Annotated[ + list[TrainingJob], + Field(description="List of training jobs.", title="Training Jobs"), + ] + + +class BillableResource(BaseModel): + id: Annotated[ + str, Field(description="Unique identifier of the resource", title="Id") + ] + kind: Annotated[ + ResourceKind, + Field( + description="Resource kind (MODEL_DEPLOYMENT, TRAINING_JOB, or CHAINLET)" + ), + ] + name: Annotated[ + str | None, Field(description="Name of the resource", title="Name") + ] = None + is_deleted: Annotated[ + bool, + Field( + description="Indicates if the resource has been deleted", title="Is Deleted" + ), + ] + instance_type: Annotated[ + str | None, Field(description="Instance type used", title="Instance Type") + ] = None + environment_name: Annotated[ + str | None, + Field( + description="Environment name (e.g., 'production', 'staging')", + title="Environment Name", + ), + ] = None + chain_metadata: Annotated[ + ChainMetadata | None, + Field(description="Chain metadata if this is a chainlet deployment"), + ] = None + + +class DedicatedItem(BaseModel): + billable_resource: Annotated[ + BillableResource, Field(description="The model deployment resource") + ] + subtotal: Annotated[ + float | Subtotal3, + Field( + description="Subtotal cost in dollars for this billable resource", + title="Subtotal", + ), + ] + minutes: Annotated[ + int, + Field( + description="Total minutes used for this billable resource", title="Minutes" + ), + ] + inference_requests: Annotated[ + int, + Field( + description="Total inference requests for this billable resource", + title="Inference Requests", + ), + ] + daily: Annotated[ + list[DailyDedicatedUsage] | None, + Field(description="Daily usage breakdown", title="Daily"), + ] = None + + +class DedicatedUsage(BaseModel): + subtotal: Annotated[ + float | Subtotal4, + Field( + description="Subtotal cost in dollars after applying credits used", + title="Subtotal", + ), + ] + credits_used: Annotated[ + float | CreditsUsed, + Field(description="Credits applied in dollars", title="Credits Used"), + ] + total: Annotated[ + float | Total, Field(description="Total cost in dollars", title="Total") + ] + minutes: Annotated[int, Field(description="Total minutes used", title="Minutes")] + breakdown: Annotated[ + list[DedicatedItem] | None, + Field(description="Per-deployment usage breakdown", title="Breakdown"), + ] = None + + +class TrainingItem(BaseModel): + billable_resource: Annotated[ + BillableResource, Field(description="The training job resource") + ] + subtotal: Annotated[ + float | Subtotal7, + Field( + description="Subtotal cost in dollars for this billable resource", + title="Subtotal", + ), + ] + minutes: Annotated[ + int, + Field( + description="Total minutes used for this billable resource", title="Minutes" + ), + ] + daily: Annotated[ + list[DailyTrainingUsage] | None, + Field(description="Daily usage breakdown", title="Daily"), + ] = None + + +class TrainingUsage(BaseModel): + subtotal: Annotated[ + float | Subtotal8, + Field( + description="Subtotal cost in dollars after applying credits used", + title="Subtotal", + ), + ] + credits_used: Annotated[ + float | CreditsUsed, + Field(description="Credits applied in dollars", title="Credits Used"), + ] + total: Annotated[ + float | Total, Field(description="Total cost in dollars", title="Total") + ] + minutes: Annotated[int, Field(description="Total minutes used", title="Minutes")] + breakdown: Annotated[ + list[TrainingItem] | None, + Field(description="Per-job usage breakdown", title="Breakdown"), + ] = None + + +class UsageSummary(BaseModel): + dedicated_usage: Annotated[ + DedicatedUsage | None, Field(description="Dedicated model serving usage") + ] = None + training_usage: Annotated[ + TrainingUsage | None, Field(description="Training usage") + ] = None + model_apis_usage: Annotated[ + ModelApisUsage | None, Field(description="Model APIs usage") + ] = None + + +class PromotionSettings(BaseModel): + redeploy_on_promotion: Annotated[ + bool | None, + Field( + description="Whether to deploy on all promotions. Enabling this flag allows model code to safely handle environment-specific logic. When a deployment is promoted, a new deployment will be created with a copy of the image.", + examples=[True], + title="Redeploy On Promotion", + ), + ] = False + rolling_deploy: Annotated[ + bool | None, + Field( + description="Whether the environment should rely on rolling deploy orchestration.", + examples=[True], + title="Rolling Deploy", + ), + ] = False + promotion_cleanup_strategy: Annotated[ + PromotionCleanupStrategy | None, + Field( + description="The cleanup strategy to use after a promotion completes.", + examples=["SCALE_TO_ZERO"], + ), + ] = PromotionCleanupStrategy.SCALE_TO_ZERO + rolling_deploy_config: Annotated[ + RollingDeployConfig | None, + Field(description="Rolling deploy configuration for promotions"), + ] = None + ramp_up_while_promoting: Annotated[ + bool | None, + Field( + description="Whether to ramp up traffic while promoting", + examples=[True], + title="Ramp Up While Promoting", + ), + ] = False + ramp_up_duration_seconds: Annotated[ + int | None, + Field( + description="Duration of the ramp up in seconds", + examples=[600], + title="Ramp Up Duration Seconds", + ), + ] = 600 + + +class ChainEnvironment(BaseModel): + name: Annotated[str, Field(description="Name of the environment", title="Name")] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the environment was created in ISO 8601 format", + title="Created At", + ), + ] + chain_id: Annotated[ + str, Field(description="Unique identifier of the chain", title="Chain Id") + ] + promotion_settings: Annotated[ + PromotionSettings, Field(description="Promotion settings for the environment") + ] + chainlet_settings: Annotated[ + list[ChainletEnvironmentSettings], + Field( + description="Environment settings for the chainlets", + title="Chainlet Settings", + ), + ] + current_deployment: Annotated[ + ChainDeployment | None, + Field(description="Current chain deployment of the environment"), + ] + candidate_deployment: Annotated[ + ChainDeployment | None, + Field( + description="Candidate chain deployment being promoted to the environment, if a promotion is in progress" + ), + ] = None + + +class DockerAuth(BaseModel): + registry: Annotated[ + str, Field(description="Registry to authenticate with", title="Registry") + ] + auth_method: Annotated[ + DockerAuthType, + Field( + description="Method to authenticate with the registry", + examples=[ + "GCP_SERVICE_ACCOUNT_JSON", + "AWS_IAM", + "AWS_OIDC", + "GCP_OIDC", + "REGISTRY_SECRET", + ], + ), + ] + gcp_service_account_json_docker_auth: Annotated[ + GcpServiceAccountJsonDockerAuth | None, + Field(description="GCP service account details for the registry"), + ] = None + aws_iam_docker_auth: Annotated[ + AwsIamDockerAuth | None, Field(description="AWS details for the registry") + ] = None + aws_oidc_docker_auth: Annotated[ + AwsOidcDockerAuth | None, Field(description="AWS OIDC details for the registry") + ] = None + gcp_oidc_docker_auth: Annotated[ + GcpOidcDockerAuth | None, Field(description="GCP OIDC details for the registry") + ] = None + registry_secret_docker_auth: Annotated[ + RegistrySecretDockerAuth | None, + Field( + description="Required when auth_method is REGISTRY_SECRET. Supports any Docker registry (Docker Hub, GHCR, NGC, etc.) via username:password credentials stored as a Baseten secret." + ), + ] = None + + +class Environment(BaseModel): + name: Annotated[str, Field(description="Name of the environment", title="Name")] + created_at: Annotated[ + AwareDatetime, + Field( + description="Time the environment was created in ISO 8601 format", + title="Created At", + ), + ] + model_id: Annotated[ + str, Field(description="Unique identifier of the model", title="Model Id") + ] + current_deployment: Annotated[ + Deployment | None, Field(description="Current deployment of the environment") + ] + candidate_deployment: Annotated[ + Deployment | None, + Field( + description="Candidate deployment being promoted to the environment, if a promotion is in progress" + ), + ] = None + in_progress_promotion: Annotated[ + InProgressPromotion | None, + Field(description="Details of the in-progress promotion, if any"), + ] = None + autoscaling_settings: Annotated[ + AutoscalingSettings, + Field(description="Autoscaling settings for the environment"), + ] + promotion_settings: Annotated[ + PromotionSettings, Field(description="Promotion settings for the environment") + ] + instance_type: Annotated[ + InstanceType, Field(description="Instance type for the environment") + ] + + +class Environments(BaseModel): + environments: Annotated[list[Environment], Field(title="Environments")] + + +class CreateTrainingJobImage(BaseModel): + base_image: Annotated[ + str, + Field( + description="Base image for the training job.", + examples=["hello-world"], + title="Base Image", + ), + ] + docker_auth: Annotated[ + DockerAuth | None, Field(description="Docker authentication credentials") + ] = None + + +class CreateTrainingJob(BaseModel): + image: Annotated[ + CreateTrainingJobImage, + Field(examples=[{"base_image": "hello-world", "docker_auth": None}]), + ] + compute: Annotated[ + CreateTrainingJobCompute | None, + Field( + examples=[ + { + "accelerator": {"accelerator": "H100", "count": 2}, + "cpu_count": 1, + "memory": "2Gi", + "node_count": 1, + } + ] + ), + ] = None + runtime: Annotated[ + CreateTrainingJobRuntime | None, + Field( + description="Configuration for the runtime environment of the training job.", + examples=[ + { + "artifacts": [], + "cache_config": None, + "checkpointing_config": { + "checkpoint_path": None, + "enabled": False, + "volume_size_gib": None, + }, + "enable_cache": None, + "environment_variables": { + "API_KEY": "your_api_key_here", + "PATH": "/usr/bin", + }, + "load_checkpoint_config": None, + "start_commands": ["python main.py"], + } + ], + ), + ] = None + name: Annotated[ + str | None, + Field( + description="Name of the training job.", + examples=["gpt-oss-job"], + title="Name", + ), + ] = None + truss_user_env: Annotated[ + TrussUserEnv | None, Field(description="Truss user environment information") + ] = None + interactive_session: Annotated[ + InteractiveSessionConfig | None, + Field(description="Configuration for interactive debugging sessions."), + ] = None + weights: Annotated[ + list[CreateJobWeightConfig] | None, + Field( + description="MDN weight sources to mount in the training container. Weights are mirrored and cached for fast startup.", + examples=[ + [ + { + "allow_patterns": None, + "auth": None, + "auth_secret_name": None, + "ignore_patterns": None, + "mount_location": "/app/models/base", + "source": "hf://meta-llama/Llama-3-8B@main", + } + ] + ], + title="Weights", + ), + ] = None + + +class CreateTrainingJobRequest(BaseModel): + training_job: Annotated[ + CreateTrainingJob, Field(description="The training job to create.") + ] diff --git a/baseten/py.typed b/baseten/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e40db2f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,53 @@ +[project] +name = "baseten" +version = "0.9.0" +description = "Baseten Python SDK" +readme = "README.md" +license = "MIT" +requires-python = ">=3.10" +dependencies = [ + "httpx>=0.27", + "pydantic>=2.0", +] + +[dependency-groups] +dev = [ + "datamodel-code-generator>=0.55.0", + "poethepoet>=0.35", + "pytest>=9.0.2", + "pytest-asyncio>=1.3.0", + "ruff>=0.15.8", + "ty>=0.0.26", +] + +[project.urls] +Homepage = "https://github.com/basetenlabs/baseten-python" +Issues = "https://github.com/basetenlabs/baseten-python/issues" + +[tool.poe.tasks] +generate-api = "python -m scripts.apigen" +typecheck = "ty check" +test = "pytest" + +[tool.poe.tasks.format] +sequence = [ + { cmd = "ruff format ." }, + { cmd = "ruff check --fix --exit-zero ." }, +] + +[tool.poe.tasks.lint] +sequence = [ + { cmd = "ruff format --check ." }, + { cmd = "ruff check ." }, +] + +# TODO: Set exclude-newer when closer to stable release +# [tool.uv] +# exclude-newer = "7 days" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/scripts/apigen/__main__.py b/scripts/apigen/__main__.py new file mode 100644 index 0000000..68718a8 --- /dev/null +++ b/scripts/apigen/__main__.py @@ -0,0 +1,158 @@ +"""Code generator for Baseten Python SDK. + +Usage: + python -m scripts.apigen + python -m scripts.apigen --update-specs +""" + +import argparse +import ast +import subprocess +import sys +import urllib.request +from pathlib import Path + +from scripts.apigen.clientgen import generate_client +from scripts.apigen.preprocess import preprocess_spec + +MANAGEMENT_SPEC_URL = "https://api.baseten.co/v1/spec" +INFERENCE_SPEC_URL = "https://api.baseten.co/inference-spec" + +APIGEN_DIR = Path(__file__).parent +SPECS_DIR = APIGEN_DIR / "specs" +REPO_ROOT = APIGEN_DIR.parent.parent +CLIENT_DIR = REPO_ROOT / "baseten" / "client" + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + "--update-specs", + action="store_true", + help="Download latest specs from remote URLs before generating", + ) + args = parser.parse_args() + + if args.update_specs: + print("Updating specs from remote URLs...") + download_spec(MANAGEMENT_SPEC_URL, SPECS_DIR / "management.json") + download_spec(INFERENCE_SPEC_URL, SPECS_DIR / "inference.json") + + generate_api( + SPECS_DIR / "management.json", CLIENT_DIR / "managementapi", "Management" + ) + generate_api(SPECS_DIR / "inference.json", CLIENT_DIR / "inferenceapi", "Inference") + + +def download_spec(url: str, dest: Path) -> None: + print(f" {url} -> {dest}") + with urllib.request.urlopen(url) as resp: + dest.write_bytes(resp.read()) + + +def generate_api(spec_file: Path, out_dir: Path, display_name: str) -> None: + print(f"Generating {out_dir.name} from {spec_file}") + out_dir.mkdir(parents=True, exist_ok=True) + + preprocessed = preprocess_spec(spec_file.read_bytes()) + + models_file = out_dir / "_models.py" + run_datamodel_codegen(preprocessed, models_file) + print(f" -> {models_file}") + + client_file = out_dir / "_client.py" + generate_client(preprocessed, client_file) + print(f" -> {client_file}") + + init_file = out_dir / "__init__.py" + generate_init(display_name, models_file, client_file, init_file) + print(f" -> {init_file}") + + for f in (models_file, client_file, init_file): + run_ruff(f) + + +def generate_init( + display_name: str, models_file: Path, client_file: Path, out_file: Path +) -> None: + model_names = _public_class_names(models_file) + client_names = _public_class_names(client_file) + all_names = sorted(client_names) + sorted(model_names) + + model_imports = ", ".join(sorted(model_names)) + client_imports = ", ".join(sorted(client_names)) + + all_entries = "\n".join(f' "{name}",' for name in all_names) + + out_file.write_text(f'''\ +# Code generated by apigen. DO NOT EDIT. + +"""Generated client and models for the Baseten {display_name} API. + +Use :class:`ApiClient` for synchronous access or :class:`AsyncApiClient` for +asynchronous access. + +Types in this module are generated from the OpenAPI specification and are NOT +covered by any stability or compatibility guarantees. They may change without +notice between versions. +""" + +from ._client import {client_imports} +from ._models import {model_imports} + +__all__ = [ +{all_entries} +] +''') + + +def _public_class_names(path: Path) -> list[str]: + tree = ast.parse(path.read_text()) + return [ + node.name + for node in ast.iter_child_nodes(tree) + if isinstance(node, ast.ClassDef) and not node.name.startswith("_") + ] + + +def run_datamodel_codegen(spec_data: bytes, out_file: Path) -> None: + subprocess.run( + [ + sys.executable, + "-m", + "datamodel_code_generator", + "--input-file-type", + "openapi", + "--output", + str(out_file), + "--output-model-type", + "pydantic_v2.BaseModel", + "--target-python-version", + "3.10", + "--use-annotated", + "--set-default-enum-member", + "--disable-timestamp", + "--openapi-scopes", + "schemas", + "--formatters", + "ruff-format", + "ruff-check", + ], + input=spec_data, + check=True, + ) + + +def run_ruff(file: Path) -> None: + subprocess.run( + [sys.executable, "-m", "ruff", "format", str(file)], + check=True, + ) + subprocess.run( + [sys.executable, "-m", "ruff", "check", "--fix", str(file)], + check=True, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/apigen/clientgen.py b/scripts/apigen/clientgen.py new file mode 100644 index 0000000..d08b99d --- /dev/null +++ b/scripts/apigen/clientgen.py @@ -0,0 +1,397 @@ +import json +import re +from dataclasses import dataclass +from pathlib import Path + + +def generate_client(spec_data: bytes, out_file: Path) -> None: + spec = json.loads(spec_data) + ops = _extract_operations(spec) + src = _render_client(ops) + out_file.write_text(src) + + +_PATH_PARAM_RE = re.compile(r"\{(\w+)\}") + + +@dataclass +class _Operation: + name: str + http_method: str + path: str + path_params: list[str] + has_body: bool + req_body_ref: str + resp_ref: str + success_code: int + error_codes: dict[int, str] | None + summary: str + + +def _extract_operations(spec: dict) -> list[_Operation]: + paths = spec.get("paths", {}) + + # Collect raw operation data with short names (all params stripped). + raw: list[tuple[str, str, dict]] = [] + short_names: dict[str, int] = {} + for path, path_item in paths.items(): + for http_method, op_data in path_item.items(): + if http_method == "parameters" or not isinstance(op_data, dict): + continue + raw.append((path, http_method, op_data)) + name = _derive_method_name( + http_method, path, op_data, keep_trailing_param=False + ) + short_names[name] = short_names.get(name, 0) + 1 + + # Build operations, using trailing param only where needed to disambiguate. + ops: list[_Operation] = [] + for path, http_method, op_data in raw: + short = _derive_method_name( + http_method, path, op_data, keep_trailing_param=False + ) + if short_names[short] > 1: + name = _derive_method_name( + http_method, path, op_data, keep_trailing_param=True + ) + else: + name = short + ops.append( + _Operation( + name=name, + http_method=http_method.upper(), + path=path, + path_params=_PATH_PARAM_RE.findall(path), + has_body="requestBody" in op_data, + req_body_ref=_body_schema_ref(spec, op_data), + resp_ref=_response_schema_ref(spec, op_data), + success_code=_extract_success_code(op_data, http_method, path), + error_codes=_error_code_map(spec, op_data), + summary=op_data.get("summary", ""), + ) + ) + ops.sort(key=lambda o: o.name) + return ops + + +def _extract_success_code(op: dict, http_method: str, path: str) -> int: + responses = op.get("responses", {}) + codes = [int(c) for c in responses if c.isdigit() and 200 <= int(c) < 300] + if len(codes) != 1: + raise ValueError( + f"expected exactly one 2xx response for {http_method.upper()} {path}, got {codes}" + ) + return codes[0] + + +def _derive_method_name( + http_method: str, path: str, op: dict, *, keep_trailing_param: bool +) -> str: + if op_id := op.get("operationId"): + return _camel_to_snake(op_id) + segments = path.removeprefix("/v1/").strip("/").split("/") + result: list[str] = [] + for i, seg in enumerate(segments): + m = _PATH_PARAM_RE.fullmatch(seg) + if m: + if keep_trailing_param and i == len(segments) - 1: + result.append(m.group(1)) + else: + result.append(seg) + return http_method.lower() + "_" + "_".join(result).replace("-", "_") + + +def _camel_to_snake(s: str) -> str: + return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s).lower() + + +def _resolve_ref(spec: dict, node: dict | None) -> dict | None: + if node is None: + return None + ref = node.get("$ref") + if not ref: + return node + cur: object = spec + for p in ref.removeprefix("#/").split("/"): + if not isinstance(cur, dict): + return None + cur = cur.get(p) + return cur if isinstance(cur, dict) else None + + +def _json_content_schema_ref(node: dict | None) -> str: + if node is None: + return "" + ref = ( + node.get("content", {}) + .get("application/json", {}) + .get("schema", {}) + .get("$ref", "") + ) + return ref.rsplit("/", 1)[-1] if ref else "" + + +def _body_schema_ref(spec: dict, op: dict) -> str: + rb = op.get("requestBody") + if not isinstance(rb, dict): + return "" + return _json_content_schema_ref(_resolve_ref(spec, rb)) + + +def _response_schema_ref(spec: dict, op: dict) -> str: + responses = op.get("responses", {}) + for code in ("200", "201", "202"): + resp_node = responses.get(code) + if not isinstance(resp_node, dict): + continue + resolved = _resolve_ref(spec, resp_node) + if ref := _json_content_schema_ref(resolved): + return ref + # If the response was a $ref to components/responses and has JSON + # content, use the response component name as the type. + if resp_node.get("$ref") and _has_json_content(resolved): + return resp_node["$ref"].rsplit("/", 1)[-1] + return "" + + +def _has_json_content(node: dict | None) -> bool: + if node is None: + return False + return "application/json" in node.get("content", {}) + + +def _error_code_map(spec: dict, op: dict) -> dict[int, str] | None: + responses = op.get("responses", {}) + result: dict[int, str] = {} + for code_str, resp_raw in responses.items(): + if not code_str.isdigit(): + continue + code = int(code_str) + if code < 400 or not isinstance(resp_raw, dict): + continue + resolved = _resolve_ref(spec, resp_raw) + if ref := _json_content_schema_ref(resolved): + result[code] = ref + return result or None + + +def _path_fmt(path: str) -> str: + return _PATH_PARAM_RE.sub("{}", path) + + +def _render_client(ops: list[_Operation]) -> str: + has_typed_resp = any(op.resp_ref for op in ops) + has_no_resp = any(not op.resp_ref for op in ops) + + error_refs = sorted({ref for op in ops for ref in (op.error_codes or {}).values()}) + + model_imports: set[str] = set() + for op in ops: + if op.req_body_ref: + model_imports.add(op.req_body_ref) + if op.resp_ref: + model_imports.add(op.resp_ref) + for ref in (op.error_codes or {}).values(): + model_imports.add(ref) + + src = f"""\ +# Code generated by apigen/clientgen. DO NOT EDIT. + +from __future__ import annotations + +import urllib.parse +from dataclasses import dataclass +from typing import Any, TypeVar + +import httpx +from pydantic import BaseModel + +from ._models import ( +{chr(10).join(f" {name}," for name in sorted(model_imports))} +) + +_T = TypeVar("_T", bound=BaseModel) + + +@dataclass +class ResponseError(Exception): + status_code: int + body: str + + def __str__(self) -> str: + return f"baseten API error (HTTP {{self.status_code}}): {{self.body}}" +""" + + for ref in error_refs: + field_name = _camel_to_snake(ref) + src += f""" + +@dataclass +class Response{ref}(Exception): + status_code: int + {field_name}: {ref} + + def __str__(self) -> str: + return f"baseten API error (HTTP {{self.status_code}}): {{self.{field_name}.model_dump_json()}}" +""" + + if error_refs: + entries = "\n".join( + f' "{ref}": ({ref}, Response{ref}, "{_camel_to_snake(ref)}"),' + for ref in error_refs + ) + src += f""" + +_ERROR_TYPES: dict[str, tuple[type[BaseModel], type[Exception], str]] = {{ +{entries} +}} +""" + + src += """ + +@dataclass +class _ApiRequest: + method: str + path_fmt: str + path_args: list[str] + body: Any + success_code: int + error_codes: dict[int, str] | None +""" + + src += "\n\n" + _render_client_class( + ops, has_typed_resp, has_no_resp, is_async=False + ) + src += "\n\n" + _render_client_class( + ops, has_typed_resp, has_no_resp, is_async=True + ) + src += "\n" + return src + + +def _render_client_class( + ops: list[_Operation], + has_typed_resp: bool, + has_no_resp: bool, + *, + is_async: bool, +) -> str: + cls = "AsyncApiClient" if is_async else "ApiClient" + http_cls = "httpx.AsyncClient" if is_async else "httpx.Client" + aw = "await " if is_async else "" + adef = "async def" if is_async else "def" + + src = f"""\ +class {cls}: + \"""Generated HTTP client for the Baseten API. + + Methods on this client are generated from the OpenAPI specification + and are NOT covered by any stability or compatibility guarantees. + They may change without notice between versions. + \""" + + def __init__(self, http_client: {http_cls}) -> None: + \"""Create a new client. The caller is responsible for closing *http_client*.\""" + self._http_client = http_client +""" + + for op in ops: + src += "\n" + _render_method(op, is_async=is_async) + + error_dispatch = "" + if any(op.error_codes for op in ops): + error_dispatch = """\ + if request.error_codes and response.status_code in request.error_codes: + error_name = request.error_codes[response.status_code] + if error_name in _ERROR_TYPES: + model_cls, exc_cls, field_name = _ERROR_TYPES[error_name] + try: + model = model_cls.model_validate_json(response.content) + raise exc_cls( + status_code=response.status_code, # ty: ignore[unknown-argument] + **{field_name: model}, + ) + except exc_cls: + raise + except Exception: + pass +""" + + src += f""" + {adef} _do(self, request: _ApiRequest) -> httpx.Response: + path = request.path_fmt.format( + *[urllib.parse.quote(a, safe="") for a in request.path_args] + ) + json_body = None + if request.body is not None: + if isinstance(request.body, BaseModel): + json_body = request.body.model_dump(mode="json") + else: + json_body = request.body + response = {aw}self._http_client.request(request.method, path, json=json_body) + if response.status_code != request.success_code: +{error_dispatch}\ + raise ResponseError(status_code=response.status_code, body=response.text) + return response +""" + + if has_typed_resp: + src += f""" + {adef} _do_json(self, response_type: type[_T], request: _ApiRequest) -> _T: + response = {aw}self._do(request) + content_type = response.headers.get("content-type", "") + if not content_type.startswith("application/json"): + raise ValueError(f"unexpected content type {{content_type!r}}, expected application/json") + return response_type.model_validate_json(response.content) +""" + + if has_no_resp: + src += f""" + {adef} _do_no_response(self, request: _ApiRequest) -> None: + {aw}self._do(request) +""" + + return src + + +def _render_method(op: _Operation, *, is_async: bool) -> str: + adef = "async def" if is_async else "def" + aw = "await " if is_async else "" + + params = ["self"] + if op.path_params or op.has_body: + params.append("*") + for p in op.path_params: + params.append(f"{p}: str") + if op.has_body: + body_type = op.req_body_ref if op.req_body_ref else "Any" + params.append(f"body: {body_type}") + + ret = f" -> {op.resp_ref}" if op.resp_ref else " -> None" + path_args = f"[{', '.join(op.path_params)}]" if op.path_params else "[]" + body_arg = "body" if op.has_body else "None" + + if op.error_codes: + codes = sorted(op.error_codes.items()) + error_expr = "{" + ", ".join(f"{c}: {ref!r}" for c, ref in codes) + "}" + else: + error_expr = "None" + + req = ( + f"_ApiRequest(" + f"method={op.http_method!r}, " + f"path_fmt={_path_fmt(op.path)!r}, " + f"path_args={path_args}, " + f"body={body_arg}, " + f"success_code={op.success_code}, " + f"error_codes={error_expr})" + ) + + sig = f" {adef} {op.name}({', '.join(params)}){ret}:" + if op.summary: + sig += f'\n """{op.summary}"""' + + if op.resp_ref: + return f"{sig}\n return {aw}self._do_json({op.resp_ref}, {req})\n" + else: + return f"{sig}\n {aw}self._do_no_response({req})\n" diff --git a/scripts/apigen/preprocess.py b/scripts/apigen/preprocess.py new file mode 100644 index 0000000..c0eb6a5 --- /dev/null +++ b/scripts/apigen/preprocess.py @@ -0,0 +1,100 @@ +"""Preprocesses OpenAPI specs for code generation.""" + +import json +import re + + +def preprocess_spec(data: bytes) -> bytes: + doc = json.loads(data) + + # datamodel-code-generator has --openapi-scopes for schemas and + # requestbodies, but not for responses. Hoist inline schemas from + # both responses and requestBodies into components/schemas so they + # get generated as models. We hoist requestBodies ourselves (rather + # than using the requestbodies scope) to only take the JSON content + # type and skip $refs to existing schemas, avoiding duplicate and + # wrapper classes. + _hoist_component_schemas(doc) + + # datamodel-code-generator generates empty BaseModel classes for + # schemas that are bare type: object with no properties (e.g. + # PredictInput). Adding additionalProperties makes it correctly + # generate dict[str, Any] instead. + _fix_bare_object_schemas(doc) + + # Management API schemas are suffixed with V1 (e.g. ModelV1). Strip + # the suffix so generated class names are cleaner (e.g. Model). + schema_renames = _build_v1_renames(doc) + if schema_renames: + _rename_refs(doc, schema_renames) + schemas = doc.get("components", {}).get("schemas", {}) + for old, new in schema_renames.items(): + schemas[new] = schemas.pop(old) + + return json.dumps(doc, indent=2).encode() + + +def _hoist_component_schemas(doc: dict) -> None: + # Hoist inline JSON schemas from components/responses and + # components/requestBodies into components/schemas. Entries that are + # just a $ref to an existing schema (or whose JSON content schema is + # a $ref) are skipped — they'd only produce pointless wrapper classes. + schemas = doc.setdefault("components", {}).setdefault("schemas", {}) + + for section in ("responses", "requestBodies"): + entries = doc.get("components", {}).get(section) + if not entries: + continue + for name, entry in entries.items(): + if "$ref" in entry: + continue + content = entry.get("content", {}).get("application/json", {}) + schema = content.get("schema") + if schema is None: + continue + if "$ref" in schema and schema["$ref"].startswith("#/components/schemas/"): + continue + schemas[name] = schema + content["schema"] = {"$ref": f"#/components/schemas/{name}"} + + +def _fix_bare_object_schemas(doc: dict) -> None: + schemas = doc.get("components", {}).get("schemas", {}) + for schema in schemas.values(): + if not isinstance(schema, dict): + continue + if ( + schema.get("type") == "object" + and "properties" not in schema + and "additionalProperties" not in schema + and "allOf" not in schema + and "oneOf" not in schema + and "anyOf" not in schema + ): + schema["additionalProperties"] = {} + + +def _build_v1_renames(doc: dict) -> dict[str, str]: + schemas = doc.get("components", {}).get("schemas", {}) + renames = {} + for name in schemas: + if name.endswith("V1"): + renames[name] = name[:-2] + return renames + + +_REF_PATTERN = re.compile(r"#/components/schemas/(\w+)") + + +def _rename_refs(node: object, renames: dict[str, str]) -> None: + if isinstance(node, dict): + ref = node.get("$ref") # ty: ignore[invalid-argument-type] + if isinstance(ref, str): + m = _REF_PATTERN.fullmatch(ref) + if m and m.group(1) in renames: + node["$ref"] = f"#/components/schemas/{renames[m.group(1)]}" # ty: ignore[invalid-assignment] + for child in node.values(): + _rename_refs(child, renames) + elif isinstance(node, list): + for child in node: + _rename_refs(child, renames) diff --git a/scripts/apigen/specs/inference.json b/scripts/apigen/specs/inference.json new file mode 100644 index 0000000..a03a023 --- /dev/null +++ b/scripts/apigen/specs/inference.json @@ -0,0 +1,1491 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Baseten inference API", + "version": "1.0.0", + "description": "API for invoking models and chains deployed on Baseten. Model endpoints use the `model-{model_id}` subdomain; chain endpoints use the `chain-{chain_id}` subdomain. All other path and query semantics are identical across both.\n\nFor regional environments, the environment name is embedded in the hostname (e.g. `model-{model_id}-{env_name}.api.baseten.co`) and the path is bare (e.g. `/predict` instead of `/environments/{env_name}/predict`). See the \"Regional\" tagged endpoints. Path-based routes (`/production/*`, `/development/*`, `/environments/*`, `/deployment/*`) are not available on regional hostnames." + }, + "servers": [ + { + "url": "https://model-{model_id}.api.baseten.co", + "description": "Deployed model endpoints", + "variables": { + "model_id": { + "description": "The model's alphanumeric ID, found in your model dashboard.", + "default": "{model_id}" + } + } + }, + { + "url": "https://chain-{chain_id}.api.baseten.co", + "description": "Deployed chain endpoints", + "variables": { + "chain_id": { + "description": "The chain's alphanumeric ID, found in your chain dashboard.", + "default": "{chain_id}" + } + } + }, + { + "url": "https://model-{model_id}-{env_name}.api.baseten.co", + "description": "Regional model endpoints", + "variables": { + "model_id": { + "description": "The model's alphanumeric ID.", + "default": "{model_id}" + }, + "env_name": { + "description": "The regional environment name (e.g. `prod-us`).", + "default": "{env_name}" + } + } + }, + { + "url": "https://chain-{chain_id}-{env_name}.api.baseten.co", + "description": "Regional chain endpoints", + "variables": { + "chain_id": { + "description": "The chain's alphanumeric ID.", + "default": "{chain_id}" + }, + "env_name": { + "description": "The regional environment name (e.g. `prod-us`).", + "default": "{env_name}" + } + } + } + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "paths": { + "/production/predict": { + "post": { + "operationId": "predictProduction", + "summary": "Call the production environment of a model.", + "description": "Sends a synchronous predict request to the deployment promoted to the production environment. The request body is forwarded directly to the model's `predict` function.", + "tags": [ + "Non-Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/PredictInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/development/predict": { + "post": { + "operationId": "predictDevelopment", + "summary": "Call the development deployment of a model.", + "description": "Sends a synchronous predict request to the development deployment.", + "tags": [ + "Non-Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/PredictInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/environments/{env_name}/predict": { + "post": { + "operationId": "predict", + "summary": "Call the model deployment associated with a specified environment.", + "description": "Sends a synchronous predict request to the deployment promoted to the specified environment.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/env_name" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/PredictInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/deployment/{deployment_id}/predict": { + "post": { + "operationId": "predictDeployment", + "summary": "Call a specific deployment of a model by deployment ID.", + "description": "Sends a synchronous predict request to the specified deployment.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/deployment_id" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/PredictInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/production/run_remote": { + "post": { + "operationId": "runRemoteProduction", + "summary": "Call the production environment of a chain.", + "description": "Sends a synchronous request to the chain deployment promoted to the production environment. The request body is forwarded to the chain's `run_remote` entrypoint.", + "tags": [ + "Non-Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/RunRemoteInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/RunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/development/run_remote": { + "post": { + "operationId": "runRemoteDevelopment", + "summary": "Call the development deployment of a chain.", + "tags": [ + "Non-Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/RunRemoteInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/RunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/environments/{env_name}/run_remote": { + "post": { + "operationId": "runRemote", + "summary": "Call the chain deployment associated with a specified environment.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/env_name" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/RunRemoteInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/RunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/deployment/{deployment_id}/run_remote": { + "post": { + "operationId": "runRemoteDeployment", + "summary": "Call a specific chain deployment by deployment ID.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/deployment_id" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/RunRemoteInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/RunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/production/async_predict": { + "post": { + "operationId": "asyncPredictProduction", + "summary": "Asynchronously call the production environment of a model.", + "description": "Enqueues an asynchronous predict request for the deployment promoted to the production environment. Returns a request ID that can be used to poll for status or cancel the request.", + "tags": [ + "Non-Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncPredictInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncPredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "413": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/development/async_predict": { + "post": { + "operationId": "asyncPredictDevelopment", + "summary": "Asynchronously call the development deployment of a model.", + "tags": [ + "Non-Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncPredictInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncPredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "413": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/environments/{env_name}/async_predict": { + "post": { + "operationId": "asyncPredict", + "summary": "Asynchronously call a named environment of a model.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/env_name" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncPredictInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncPredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "413": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/deployment/{deployment_id}/async_predict": { + "post": { + "operationId": "asyncPredictDeployment", + "summary": "Asynchronously call a specific deployment of a model.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/deployment_id" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncPredictInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncPredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "413": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/production/async_run_remote": { + "post": { + "operationId": "asyncRunRemoteProduction", + "summary": "Asynchronously call the production environment of a chain.", + "description": "Enqueues an asynchronous request for the chain deployment promoted to the production environment.", + "tags": [ + "Non-Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncRunRemoteInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncRunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/development/async_run_remote": { + "post": { + "operationId": "asyncRunRemoteDevelopment", + "summary": "Asynchronously call the development deployment of a chain.", + "tags": [ + "Non-Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncRunRemoteInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncRunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/environments/{env_name}/async_run_remote": { + "post": { + "operationId": "asyncRunRemote", + "summary": "Asynchronously call a named environment of a chain.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/env_name" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncRunRemoteInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncRunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/deployment/{deployment_id}/async_run_remote": { + "post": { + "operationId": "asyncRunRemoteDeployment", + "summary": "Asynchronously call a specific deployment of a chain.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/deployment_id" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncRunRemoteInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncRunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/async_request/{request_id}": { + "get": { + "operationId": "getAsyncRequestStatus", + "summary": "Get the status of an async request.", + "description": "Returns the current status of an async model or chain request. Rate limited to 20 requests per second.", + "tags": [], + "parameters": [ + { + "$ref": "#/components/parameters/request_id" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GetAsyncRequestStatusOutput" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "operationId": "cancelAsyncRequest", + "summary": "Cancel a queued async request.", + "description": "Cancels an async request. Only requests with `QUEUED` status may be canceled. Rate limited to 20 requests per second.", + "tags": [], + "parameters": [ + { + "$ref": "#/components/parameters/request_id" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CancelAsyncRequestOutput" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/production/async_queue_status": { + "get": { + "operationId": "getAsyncQueueStatusProduction", + "summary": "Get async queue status for the production environment.", + "description": "Returns the number of queued and in-progress async requests for the deployment promoted to the production environment. Rate limited to 20 requests per second.", + "tags": [ + "Non-Regional" + ], + "responses": { + "200": { + "$ref": "#/components/responses/GetAsyncQueueStatusOutput" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/development/async_queue_status": { + "get": { + "operationId": "getAsyncQueueStatusDevelopment", + "summary": "Get async queue status for the development deployment.", + "tags": [ + "Non-Regional" + ], + "responses": { + "200": { + "$ref": "#/components/responses/GetAsyncQueueStatusOutput" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/environments/{env_name}/async_queue_status": { + "get": { + "operationId": "getAsyncQueueStatus", + "summary": "Get async queue status for a named environment.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/env_name" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GetAsyncQueueStatusOutput" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/deployment/{deployment_id}/async_queue_status": { + "get": { + "operationId": "getAsyncQueueStatusDeployment", + "summary": "Get async queue status for a specific deployment.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/deployment_id" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GetAsyncQueueStatusOutput" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/production/wake": { + "post": { + "operationId": "wakeProduction", + "summary": "Wake the production environment of a model.", + "description": "Triggers a wake for the deployment promoted to the production environment. Returns immediately with 202 Accepted.", + "tags": [ + "Non-Regional" + ], + "responses": { + "202": { + "description": "Wake request accepted." + }, + "401": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/development/wake": { + "post": { + "operationId": "wakeDevelopment", + "summary": "Wake the development deployment of a model.", + "tags": [ + "Non-Regional" + ], + "responses": { + "202": { + "description": "Wake request accepted." + }, + "401": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/environments/{env_name}/wake": { + "post": { + "operationId": "wake", + "summary": "Wake a named environment of a model.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/env_name" + } + ], + "responses": { + "202": { + "description": "Wake request accepted." + }, + "401": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/deployment/{deployment_id}/wake": { + "post": { + "operationId": "wakeDeployment", + "summary": "Wake a specific deployment of a model by deployment ID.", + "tags": [ + "Non-Regional" + ], + "parameters": [ + { + "$ref": "#/components/parameters/deployment_id" + } + ], + "responses": { + "202": { + "description": "Wake request accepted." + }, + "401": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/predict": { + "post": { + "operationId": "predictRegional", + "summary": "Call a regional environment of a model.", + "description": "Sends a synchronous predict request via a regional hostname. The environment is determined by the hostname, not the path.", + "tags": [ + "Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/PredictInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/run_remote": { + "post": { + "operationId": "runRemoteRegional", + "summary": "Call a regional environment of a chain.", + "description": "Sends a synchronous run_remote request via a regional hostname. The environment is determined by the hostname, not the path.", + "tags": [ + "Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/RunRemoteInput" + }, + "responses": { + "200": { + "$ref": "#/components/responses/RunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "502": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + }, + "504": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/async_predict": { + "post": { + "operationId": "asyncPredictRegional", + "summary": "Asynchronously call a regional environment of a model.", + "description": "Enqueues an asynchronous predict request via a regional hostname. The environment is determined by the hostname, not the path.", + "tags": [ + "Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncPredictInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncPredictOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "413": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/async_run_remote": { + "post": { + "operationId": "asyncRunRemoteRegional", + "summary": "Asynchronously call a regional environment of a chain.", + "description": "Enqueues an asynchronous run_remote request via a regional hostname. The environment is determined by the hostname, not the path.", + "tags": [ + "Regional" + ], + "requestBody": { + "$ref": "#/components/requestBodies/AsyncRunRemoteInput" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AsyncRunRemoteOutput" + }, + "400": { + "$ref": "#/components/responses/Error" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + }, + "503": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/async_queue_status": { + "get": { + "operationId": "getAsyncQueueStatusRegional", + "summary": "Get async queue status for a regional environment.", + "tags": [ + "Regional" + ], + "responses": { + "200": { + "$ref": "#/components/responses/GetAsyncQueueStatusOutput" + }, + "401": { + "$ref": "#/components/responses/Error" + }, + "429": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/wake": { + "post": { + "operationId": "wakeRegional", + "summary": "Wake a regional environment of a model.", + "tags": [ + "Regional" + ], + "responses": { + "202": { + "description": "Wake request accepted." + }, + "401": { + "$ref": "#/components/responses/Error" + } + } + } + } + }, + "components": { + "securitySchemes": { + "ApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "Authorization", + "description": "API key with the `Api-Key` prefix, e.g. `Authorization: Api-Key abcd1234.abcd1234`." + } + }, + "parameters": { + "env_name": { + "name": "env_name", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The name of the environment (e.g. `production`, `staging`)." + }, + "deployment_id": { + "name": "deployment_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The alphanumeric ID of the deployment." + }, + "request_id": { + "name": "request_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the async request." + } + }, + "requestBodies": { + "PredictInput": { + "required": true, + "content": { + "application/json": { + "schema": { + "description": "JSON-serializable model input. The shape is defined by the model's `predict` function.", + "type": "object" + } + }, + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary", + "description": "Binary model input." + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "description": "Multipart model input." + } + } + } + }, + "RunRemoteInput": { + "required": true, + "content": { + "application/json": { + "schema": { + "description": "JSON input matching the chain's `run_remote` method signature.", + "type": "object" + } + } + } + }, + "AsyncPredictInput": { + "required": true, + "description": "There is a 256 KiB size limit on async predict request payloads.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncPredictRequest" + } + } + } + }, + "AsyncRunRemoteInput": { + "required": true, + "content": { + "application/json": { + "schema": { + "description": "JSON input matching the chain's `run_remote` method signature.", + "type": "object" + } + } + } + } + }, + "responses": { + "PredictOutput": { + "description": "Successful synchronous prediction.", + "content": { + "application/json": { + "schema": { + "description": "JSON-serializable output. The shape is defined by the model.", + "type": "object" + } + }, + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary", + "description": "Binary or streaming output. Streaming responses use `transfer-encoding: chunked`." + } + } + }, + "headers": { + "X-BASETEN-REQUEST-ID": { + "$ref": "#/components/headers/X-BASETEN-REQUEST-ID" + } + } + }, + "RunRemoteOutput": { + "description": "Successful synchronous chain execution.", + "content": { + "application/json": { + "schema": { + "description": "JSON-serializable output. The shape is defined by the chain.", + "type": "object" + } + } + }, + "headers": { + "X-BASETEN-REQUEST-ID": { + "$ref": "#/components/headers/X-BASETEN-REQUEST-ID" + } + } + }, + "AsyncPredictOutput": { + "description": "Async predict request enqueued.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "request_id" + ], + "properties": { + "request_id": { + "type": "string", + "description": "The ID of the async request." + } + } + } + } + } + }, + "AsyncRunRemoteOutput": { + "description": "Async run remote request enqueued.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "request_id" + ], + "properties": { + "request_id": { + "type": "string", + "description": "The ID of the async request." + } + } + } + } + } + }, + "GetAsyncRequestStatusOutput": { + "description": "Current status of an async request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncRequestStatusResponse" + } + } + } + }, + "CancelAsyncRequestOutput": { + "description": "Result of an async request cancellation.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "request_id", + "canceled", + "message" + ], + "properties": { + "request_id": { + "type": "string", + "description": "The ID of the async request." + }, + "canceled": { + "type": "boolean", + "description": "Whether the request was canceled." + }, + "message": { + "type": "string", + "description": "Additional details about whether the request was canceled." + } + } + } + } + } + }, + "GetAsyncQueueStatusOutput": { + "description": "Async queue status for a deployment.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "model_id", + "deployment_id", + "num_queued_requests", + "num_in_progress_requests" + ], + "properties": { + "model_id": { + "type": "string", + "description": "The ID of the model." + }, + "deployment_id": { + "type": "string", + "description": "The ID of the deployment." + }, + "num_queued_requests": { + "type": "integer", + "description": "Number of requests with QUEUED status awaiting processing." + }, + "num_in_progress_requests": { + "type": "integer", + "description": "Number of requests currently being processed by the model." + } + } + } + } + } + }, + "Error": { + "description": "Error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "schemas": { + "AsyncPredictRequest": { + "type": "object", + "required": [ + "model_input" + ], + "properties": { + "model_input": { + "type": "object", + "description": "JSON-serializable model input." + }, + "webhook_endpoint": { + "type": "string", + "format": "uri", + "description": "HTTPS URL to receive the prediction result via webhook. Both HTTP/2 and HTTP/1.1 are supported. If omitted, the model must save outputs so they can be accessed later." + }, + "priority": { + "type": "integer", + "minimum": 0, + "maximum": 2, + "default": 0, + "description": "Priority of the request. Lower values are higher priority." + }, + "max_time_in_queue_seconds": { + "type": "integer", + "minimum": 10, + "maximum": 259200, + "default": 600, + "description": "Maximum time in seconds a request will spend in the queue before expiring. Must be between 10 seconds and 72 hours." + }, + "inference_retry_config": { + "$ref": "#/components/schemas/InferenceRetryConfig" + } + } + }, + "InferenceRetryConfig": { + "type": "object", + "description": "Exponential backoff parameters for retrying predict requests.", + "properties": { + "max_attempts": { + "type": "integer", + "minimum": 1, + "maximum": 10, + "default": 3, + "description": "Number of predict request attempts." + }, + "initial_delay_ms": { + "type": "integer", + "minimum": 0, + "maximum": 10000, + "default": 1000, + "description": "Minimum time between retries in milliseconds." + }, + "max_delay_ms": { + "type": "integer", + "minimum": 0, + "maximum": 60000, + "default": 5000, + "description": "Maximum time between retries in milliseconds." + } + } + }, + "AsyncRequestStatusResponse": { + "type": "object", + "required": [ + "request_id", + "deployment_id", + "status", + "webhook_status", + "created_at", + "status_at", + "errors" + ], + "properties": { + "request_id": { + "type": "string", + "description": "The ID of the async request." + }, + "model_id": { + "type": "string", + "description": "The ID of the model that executed the request. Present for model requests." + }, + "chain_id": { + "type": "string", + "description": "The ID of the chain that executed the request. Present for chain requests." + }, + "deployment_id": { + "type": "string", + "description": "The ID of the deployment that executed the request." + }, + "status": { + "type": "string", + "enum": [ + "QUEUED", + "IN_PROGRESS", + "SUCCEEDED", + "FAILED", + "EXPIRED", + "CANCELED", + "WEBHOOK_FAILED" + ], + "description": "The status of the async request." + }, + "webhook_status": { + "type": "string", + "enum": [ + "PENDING", + "SUCCEEDED", + "FAILED", + "CANCELED", + "NO_WEBHOOK_PROVIDED" + ], + "description": "The status of sending the prediction result to the provided webhook." + }, + "created_at": { + "type": "string", + "description": "The time in UTC at which the async request was created." + }, + "status_at": { + "type": "string", + "description": "The time in UTC at which the async request's status was last updated." + }, + "errors": { + "type": "array", + "default": [], + "items": { + "$ref": "#/components/schemas/AsyncRequestError" + }, + "description": "Errors that occurred while processing the async request. Empty if no errors occurred." + } + } + }, + "AsyncRequestError": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "string", + "enum": [ + "MODEL_PREDICT_ERROR", + "MODEL_PREDICT_TIMEOUT", + "MODEL_NOT_READY", + "MODEL_DOES_NOT_EXIST", + "MODEL_UNAVAILABLE", + "MODEL_INVALID_INPUT", + "ASYNC_REQUEST_NOT_SUPPORTED", + "INTERNAL_SERVER_ERROR" + ], + "description": "The type of error that occurred." + }, + "message": { + "type": "string", + "description": "Details of the error." + } + } + }, + "ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Human-readable error message." + }, + "error_code": { + "type": "string", + "description": "Machine-readable error code.", + "enum": [ + "timeout", + "client_error", + "model_unavailable", + "model_not_ready", + "model_does_not_exist", + "rate_limited", + "payload_too_large", + "unauthorized", + "application_error", + "internal_baseten_error" + ] + }, + "detail": { + "type": "string", + "description": "Additional error details, if available." + } + } + } + }, + "headers": { + "X-BASETEN-REQUEST-ID": { + "schema": { + "type": "string" + }, + "description": "Unique identifier for the request." + } + } + }, + "tags": [ + { + "name": "Non-Regional", + "description": "Endpoints that use path-based routing (e.g. `/production/predict`, `/environments/{env_name}/predict`). Only available on standard hostnames (`model-{model_id}.api.baseten.co`), not regional hostnames." + }, + { + "name": "Regional", + "description": "Endpoints that use bare paths (e.g. `/predict`, `/wake`). Only available on regional hostnames (`model-{model_id}-{env_name}.api.baseten.co`), where the environment is determined by the hostname." + } + ] +} diff --git a/scripts/apigen/specs/management.json b/scripts/apigen/specs/management.json new file mode 100644 index 0000000..59b8662 --- /dev/null +++ b/scripts/apigen/specs/management.json @@ -0,0 +1,9994 @@ +{ + "info": { + "description": "REST API for management of Baseten resources", + "title": "Baseten management API", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://api.baseten.co" + } + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "paths": { + "/v1/secrets": { + "get": { + "summary": "Gets all secrets", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/secrets \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/secrets\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SecretsV1" + } + } + } + } + } + }, + "post": { + "summary": "Upserts a secret", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/secrets \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"name\": \"my_secret\",\n \"value\": \"my_secret_value\"\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/secrets\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'name': 'my_secret', 'value': 'my_secret_value'}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a new secret or updates an existing secret if one with the provided name already exists. The name and creation date of the created or updated secret is returned.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertSecretRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SecretV1" + } + } + } + } + } + } + }, + "/v1/teams/{team_id}/secrets": { + "get": { + "summary": "Gets all secrets for a team", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/teams/{team_id}/secrets \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/teams/{team_id}/secrets\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SecretsV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/team_id" + } + ], + "post": { + "summary": "Upserts a secret in a team", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/teams/{team_id}/secrets \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"name\": \"my_secret\",\n \"value\": \"my_secret_value\"\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/teams/{team_id}/secrets\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'name': 'my_secret', 'value': 'my_secret_value'}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a new secret or updates an existing secret if one with the provided name already exists. The name and creation date of the created or updated secret is returned. This secret belongs to the specified team", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertSecretRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SecretV1" + } + } + } + } + } + } + }, + "/v1/teams": { + "get": { + "summary": "Lists all teams", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/teams \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/teams\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Returns a list of all teams the authenticated user has access to.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamsV1" + } + } + } + } + } + } + }, + "/v1/instance_types": { + "get": { + "summary": "Gets all available instance types", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/instance_types \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/instance_types\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InstanceTypesV1" + } + } + } + } + } + } + }, + "/v1/instance_type_prices": { + "get": { + "summary": "Gets prices for available instance types.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/instance_type_prices \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/instance_type_prices\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InstanceTypePricesV1" + } + } + } + } + } + } + }, + "/v1/models": { + "get": { + "summary": "Gets all models", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/models \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelsV1" + } + } + } + } + } + } + }, + "/v1/models/{model_id}": { + "delete": { + "summary": "Deletes a model by ID", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/models/{model_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ], + "get": { + "summary": "Gets a model by ID", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/models/{model_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelV1" + } + } + } + } + } + } + }, + "/v1/models/{model_id}/deployments": { + "get": { + "summary": "Gets all deployments of a model", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentsV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/development": { + "get": { + "summary": "Gets a model's development deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/development \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/development\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets a model's development deployment and returns the deployment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/production": { + "get": { + "summary": "Gets a model's production deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/production \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/production\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets a model's production deployment and returns the deployment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/{deployment_id}": { + "delete": { + "summary": "Deletes a model's deployment by ID", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deletes a model's deployment by ID and returns the tombstone of the deployment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/deployment_id" + } + ], + "get": { + "summary": "Gets a model's deployment by ID", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets a model's deployment by ID and returns the deployment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentV1" + } + } + } + } + } + } + }, + "/v1/models/{model_id}/deployments/development/autoscaling_settings": { + "patch": { + "summary": "Updates a development deployment's autoscaling settings", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/development/autoscaling_settings \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"min_replica\": 0,\n \"max_replica\": 7,\n \"autoscaling_window\": 600,\n \"scale_down_delay\": 120,\n \"concurrency_target\": 2,\n \"target_utilization_percentage\": 70,\n \"target_in_flight_tokens\": 40000\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/development/autoscaling_settings\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'min_replica': 0, 'max_replica': 7, 'autoscaling_window': 600, 'scale_down_delay': 120, 'concurrency_target': 2, 'target_utilization_percentage': 70, 'target_in_flight_tokens': 40000}\n)\n\nprint(response.text)" + } + ], + "description": "Updates a development deployment's autoscaling settings and returns the update status.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/production/autoscaling_settings": { + "patch": { + "summary": "Updates a production deployment's autoscaling settings", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/production/autoscaling_settings \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"min_replica\": 0,\n \"max_replica\": 7,\n \"autoscaling_window\": 600,\n \"scale_down_delay\": 120,\n \"concurrency_target\": 2,\n \"target_utilization_percentage\": 70,\n \"target_in_flight_tokens\": 40000\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/production/autoscaling_settings\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'min_replica': 0, 'max_replica': 7, 'autoscaling_window': 600, 'scale_down_delay': 120, 'concurrency_target': 2, 'target_utilization_percentage': 70, 'target_in_flight_tokens': 40000}\n)\n\nprint(response.text)" + } + ], + "description": "Updates a production deployment's autoscaling settings and returns the update status.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/{deployment_id}/autoscaling_settings": { + "patch": { + "summary": "Updates a deployment's autoscaling settings", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/autoscaling_settings \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"min_replica\": 0,\n \"max_replica\": 7,\n \"autoscaling_window\": 600,\n \"scale_down_delay\": 120,\n \"concurrency_target\": 2,\n \"target_utilization_percentage\": 70,\n \"target_in_flight_tokens\": 40000\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/autoscaling_settings\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'min_replica': 0, 'max_replica': 7, 'autoscaling_window': 600, 'scale_down_delay': 120, 'concurrency_target': 2, 'target_utilization_percentage': 70, 'target_in_flight_tokens': 40000}\n)\n\nprint(response.text)" + } + ], + "description": "Updates a deployment's autoscaling settings and returns the update status.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/deployment_id" + } + ] + }, + "/v1/models/{model_id}/deployments/development/promote": { + "post": { + "summary": "Promotes a development deployment to production", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/development/promote \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"scale_down_previous_production\": true,\n \"preserve_env_instance_type\": true\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/development/promote\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'scale_down_previous_production': True, 'preserve_env_instance_type': True}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a new production deployment from the development deployment, the currently building deployment is returned.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PromoteRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/{deployment_id}/promote": { + "post": { + "summary": "Promotes a deployment to production", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/promote \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"scale_down_previous_production\": true,\n \"preserve_env_instance_type\": true\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/promote\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'scale_down_previous_production': True, 'preserve_env_instance_type': True}\n)\n\nprint(response.text)" + } + ], + "description": "Promotes an existing deployment to production and returns the same deployment.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PromoteRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/deployment_id" + } + ] + }, + "/v1/models/{model_id}/deployments/development/activate": { + "post": { + "summary": "Activates a development deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/development/activate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/development/activate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Activates an inactive development deployment and returns the activation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/production/activate": { + "post": { + "summary": "Activates a production deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/production/activate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/production/activate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Activates an inactive production deployment and returns the activation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/{deployment_id}/activate": { + "post": { + "summary": "Activates a deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/activate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/activate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Activates an inactive deployment and returns the activation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/deployment_id" + } + ] + }, + "/v1/models/{model_id}/deployments/development/deactivate": { + "post": { + "summary": "Deactivates a development deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/development/deactivate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/development/deactivate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deactivates a development deployment and returns the deactivation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeactivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/production/deactivate": { + "post": { + "summary": "Deactivates a production deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/production/deactivate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/production/deactivate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deactivates a production deployment and returns the deactivation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeactivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/{deployment_id}/deactivate": { + "post": { + "summary": "Deactivates a deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/deactivate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/deactivate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deactivates a deployment and returns the deactivation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeactivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/deployment_id" + } + ] + }, + "/v1/models/{model_id}/deployments/development/retry": { + "post": { + "summary": "Retries a failed development deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/development/retry \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/development/retry\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Retries a failed development deployment and returns the retry status and updated deployment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RetryDeploymentResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/production/retry": { + "post": { + "summary": "Retries a failed production deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/production/retry \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/production/retry\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Retries a failed production deployment and returns the retry status and updated deployment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RetryDeploymentResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/models/{model_id}/deployments/{deployment_id}/retry": { + "post": { + "summary": "Retries a failed deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/retry \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/retry\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Retries a failed deployment and returns the retry status and updated deployment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RetryDeploymentResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/deployment_id" + } + ] + }, + "/v1/models/{model_id}/deployments/{deployment_id}/logs": { + "post": { + "summary": "Gets the logs for a model deployment.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/logs \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"start_epoch_millis\": null,\n \"end_epoch_millis\": null,\n \"direction\": null,\n \"limit\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/logs\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'start_epoch_millis': None, 'end_epoch_millis': None, 'direction': None, 'limit': None}\n)\n\nprint(response.text)" + } + ], + "description": "Gets all the logs for a model deployment in the given time range.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetDeploymentLogsRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetLogsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/deployment_id" + } + ] + }, + "/v1/models/{model_id}/deployments/{deployment_id}/replicas/{replica_id}": { + "delete": { + "summary": "Terminates a replica in a deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/replicas/{replica_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/deployments/{deployment_id}/replicas/{replica_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Terminates a deployment replica and returns the termination status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TerminateReplicaResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/deployment_id" + }, + { + "$ref": "#/components/parameters/replica_id" + } + ] + }, + "/v1/models/{model_id}/environments": { + "get": { + "summary": "Get all environments", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/models/{model_id}/environments \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets all environments for a given model", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnvironmentsV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ], + "post": { + "summary": "Create an environment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"name\": \"staging\",\n \"autoscaling_settings\": {\n \"autoscaling_window\": 800,\n \"concurrency_target\": 3,\n \"max_replica\": 2,\n \"min_replica\": 1,\n \"scale_down_delay\": 60,\n \"target_in_flight_tokens\": null,\n \"target_utilization_percentage\": null\n },\n \"promotion_settings\": {\n \"promotion_cleanup_strategy\": null,\n \"ramp_up_duration_seconds\": 600,\n \"ramp_up_while_promoting\": true,\n \"redeploy_on_promotion\": true,\n \"rolling_deploy\": true,\n \"rolling_deploy_config\": null\n }\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'name': 'staging', 'autoscaling_settings': {'autoscaling_window': 800, 'concurrency_target': 3, 'max_replica': 2, 'min_replica': 1, 'scale_down_delay': 60, 'target_in_flight_tokens': None, 'target_utilization_percentage': None}, 'promotion_settings': {'promotion_cleanup_strategy': None, 'ramp_up_duration_seconds': 600, 'ramp_up_while_promoting': True, 'redeploy_on_promotion': True, 'rolling_deploy': True, 'rolling_deploy_config': None}}\n)\n\nprint(response.text)" + } + ], + "description": "Creates an environment for the specified model and returns the environment.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEnvironmentRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnvironmentV1" + } + } + } + } + } + } + }, + "/v1/models/{model_id}/environments/{env_name}": { + "get": { + "summary": "Get an environment's details", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets an environment's details and returns the environment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnvironmentV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ], + "patch": { + "summary": "Update an environment's settings", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"autoscaling_settings\": {\n \"autoscaling_window\": 800,\n \"concurrency_target\": 3,\n \"max_replica\": 2,\n \"min_replica\": 1,\n \"scale_down_delay\": 60,\n \"target_in_flight_tokens\": null,\n \"target_utilization_percentage\": null\n },\n \"promotion_settings\": {\n \"promotion_cleanup_strategy\": null,\n \"ramp_up_duration_seconds\": 600,\n \"ramp_up_while_promoting\": true,\n \"redeploy_on_promotion\": true,\n \"rolling_deploy\": null,\n \"rolling_deploy_config\": null\n }\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'autoscaling_settings': {'autoscaling_window': 800, 'concurrency_target': 3, 'max_replica': 2, 'min_replica': 1, 'scale_down_delay': 60, 'target_in_flight_tokens': None, 'target_utilization_percentage': None}, 'promotion_settings': {'promotion_cleanup_strategy': None, 'ramp_up_duration_seconds': 600, 'ramp_up_while_promoting': True, 'redeploy_on_promotion': True, 'rolling_deploy': None, 'rolling_deploy_config': None}}\n)\n\nprint(response.text)" + } + ], + "description": "Updates an environment's settings and returns the updated environment.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateEnvironmentRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsResponseV1" + } + } + } + } + } + } + }, + "/v1/models/{model_id}/environments/{env_name}/activate": { + "post": { + "summary": "Activates a deployment associated with an environment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/activate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/activate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Activates an inactive deployment associated with an environment and returns the activation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/models/{model_id}/environments/{env_name}/deactivate": { + "post": { + "summary": "Deactivates a deployment associated with an environment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/deactivate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/deactivate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deactivates a deployment associated with an environment and returns the deactivation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeactivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/models/{model_id}/environments/{env_name}/promote": { + "post": { + "summary": "Promotes a deployment to an environment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/promote \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"scale_down_previous_deployment\": true,\n \"deployment_id\": null,\n \"preserve_env_instance_type\": true\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/promote\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'scale_down_previous_deployment': True, 'deployment_id': None, 'preserve_env_instance_type': True}\n)\n\nprint(response.text)" + } + ], + "description": "Promotes an existing deployment to an environment and returns the promoted deployment.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PromoteToEnvironmentRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/models/{model_id}/environments/{env_name}/cancel_promotion": { + "post": { + "summary": "Cancels a promotion to an environment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/cancel_promotion \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/cancel_promotion\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Cancels an ongoing promotion to an environment and returns the cancellation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CancelPromotionResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/models/{model_id}/environments/{env_name}/pause_promotion": { + "post": { + "summary": "Pauses a rolling promotion", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/pause_promotion \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/pause_promotion\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Pauses an in-progress rolling promotion after the current step completes. No further scaling changes are made until resumed.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignalPromotionResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/models/{model_id}/environments/{env_name}/resume_promotion": { + "post": { + "summary": "Resumes a paused rolling promotion", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/resume_promotion \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/resume_promotion\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Resumes a paused rolling promotion, continuing from where it was paused.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignalPromotionResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/models/{model_id}/environments/{env_name}/force_cancel_promotion": { + "post": { + "summary": "Force cancels a rolling promotion", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/force_cancel_promotion \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/force_cancel_promotion\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Immediately cancels an in-progress rolling promotion and triggers rollback to the previous version.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignalPromotionResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/models/{model_id}/environments/{env_name}/force_roll_forward_promotion": { + "post": { + "summary": "Force rolls forward a rolling promotion", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/force_roll_forward_promotion \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/models/{model_id}/environments/{env_name}/force_roll_forward_promotion\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Immediately completes the rolling promotion, shifting all traffic to the new version. This works even if the promotion is in the process of rolling back.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignalPromotionResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/chains": { + "get": { + "summary": "Gets all chains", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/chains \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainsV1" + } + } + } + } + } + } + }, + "/v1/chains/{chain_id}": { + "delete": { + "summary": "Deletes a chain by ID", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/chains/{chain_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + } + ], + "get": { + "summary": "Gets a chain by ID", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/chains/{chain_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainV1" + } + } + } + } + } + } + }, + "/v1/chains/{chain_id}/deployments": { + "get": { + "summary": "Gets all chain deployments", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/chains/{chain_id}/deployments \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/deployments\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainDeploymentsV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + } + ] + }, + "/v1/chains/{chain_id}/deployments/{chain_deployment_id}": { + "delete": { + "summary": "Deletes a chain deployment by ID", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/chains/{chain_id}/deployments/{chain_deployment_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/deployments/{chain_deployment_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainDeploymentTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + }, + { + "$ref": "#/components/parameters/chain_deployment_id" + } + ], + "get": { + "summary": "Gets a chain deployment by ID", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/chains/{chain_id}/deployments/{chain_deployment_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/deployments/{chain_deployment_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainDeploymentV1" + } + } + } + } + } + } + }, + "/v1/chains/{chain_id}/deployments/{chain_deployment_id}/deactivate": { + "post": { + "summary": "Deactivates a chain deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/chains/{chain_id}/deployments/{chain_deployment_id}/deactivate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/deployments/{chain_deployment_id}/deactivate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deactivates a chain deployment and returns the deactivation status.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeactivateResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + }, + { + "$ref": "#/components/parameters/chain_deployment_id" + } + ] + }, + "/v1/chains/{chain_id}/environments": { + "get": { + "summary": "Get all chain environments", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/chains/{chain_id}/environments \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/environments\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets all chain environments for a given chain", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnvironmentsV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + } + ], + "post": { + "summary": "Create a chain environment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/chains/{chain_id}/environments \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"name\": \"staging\",\n \"promotion_settings\": {\n \"promotion_cleanup_strategy\": null,\n \"ramp_up_duration_seconds\": 600,\n \"ramp_up_while_promoting\": true,\n \"redeploy_on_promotion\": true,\n \"rolling_deploy\": null,\n \"rolling_deploy_config\": null\n },\n \"chainlet_settings\": [\n {\n \"autoscaling_settings\": {\n \"autoscaling_window\": 800,\n \"concurrency_target\": 4,\n \"max_replica\": 3,\n \"min_replica\": 2,\n \"scale_down_delay\": 63,\n \"target_in_flight_tokens\": null,\n \"target_utilization_percentage\": null\n },\n \"chainlet_name\": \"HelloWorld\",\n \"instance_type_id\": \"2x8\"\n },\n {\n \"autoscaling_settings\": {\n \"autoscaling_window\": null,\n \"concurrency_target\": null,\n \"max_replica\": 3,\n \"min_replica\": 3,\n \"scale_down_delay\": null,\n \"target_in_flight_tokens\": null,\n \"target_utilization_percentage\": null\n },\n \"chainlet_name\": \"RandInt\",\n \"instance_type_id\": \"A10Gx8x32\"\n }\n ]\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/environments\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'name': 'staging', 'promotion_settings': {'promotion_cleanup_strategy': None, 'ramp_up_duration_seconds': 600, 'ramp_up_while_promoting': True, 'redeploy_on_promotion': True, 'rolling_deploy': None, 'rolling_deploy_config': None}, 'chainlet_settings': [{'autoscaling_settings': {'autoscaling_window': 800, 'concurrency_target': 4, 'max_replica': 3, 'min_replica': 2, 'scale_down_delay': 63, 'target_in_flight_tokens': None, 'target_utilization_percentage': None}, 'chainlet_name': 'HelloWorld', 'instance_type_id': '2x8'}, {'autoscaling_settings': {'autoscaling_window': None, 'concurrency_target': None, 'max_replica': 3, 'min_replica': 3, 'scale_down_delay': None, 'target_in_flight_tokens': None, 'target_utilization_percentage': None}, 'chainlet_name': 'RandInt', 'instance_type_id': 'A10Gx8x32'}]}\n)\n\nprint(response.text)" + } + ], + "description": "Create a chain environment. Returns the resulting environment.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChainEnvironmentRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainEnvironmentV1" + } + } + } + } + } + } + }, + "/v1/chains/{chain_id}/environments/{env_name}": { + "get": { + "summary": "Get a chain environment's details", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets a chain environment's details and returns the chain environment.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainEnvironmentV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ], + "patch": { + "summary": "Update a chain environment's settings", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"promotion_settings\": {\n \"promotion_cleanup_strategy\": null,\n \"ramp_up_duration_seconds\": 600,\n \"ramp_up_while_promoting\": true,\n \"redeploy_on_promotion\": null,\n \"rolling_deploy\": null,\n \"rolling_deploy_config\": null\n }\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'promotion_settings': {'promotion_cleanup_strategy': None, 'ramp_up_duration_seconds': 600, 'ramp_up_while_promoting': True, 'redeploy_on_promotion': None, 'rolling_deploy': None, 'rolling_deploy_config': None}}\n)\n\nprint(response.text)" + } + ], + "description": "Update a chain environment's settings and returns the chain environment.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateChainEnvironmentRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateChainEnvironmentResponseV1" + } + } + } + } + } + } + }, + "/v1/chains/{chain_id}/environments/{env_name}/promote": { + "post": { + "summary": "Promotes a chain deployment to an environment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name}/promote \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"scale_down_previous_deployment\": true,\n \"deployment_id\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name}/promote\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'scale_down_previous_deployment': True, 'deployment_id': None}\n)\n\nprint(response.text)" + } + ], + "description": "Promotes an existing chain deployment to an environment and returns the promoted chain deployment.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PromoteToChainEnvironmentRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainDeploymentV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/chains/{chain_id}/environments/{env_name}/chainlet_settings/autoscaling_settings": { + "patch": { + "summary": "Update a chainlet environment's autoscaling settings", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name}/chainlet_settings/autoscaling_settings \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"updates\": [\n {\n \"autoscaling_settings\": {\n \"autoscaling_window\": 800,\n \"concurrency_target\": 4,\n \"max_replica\": 3,\n \"min_replica\": 2,\n \"scale_down_delay\": 63,\n \"target_in_flight_tokens\": null,\n \"target_utilization_percentage\": null\n },\n \"chainlet_name\": \"HelloWorld\"\n }\n ]\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name}/chainlet_settings/autoscaling_settings\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'updates': [{'autoscaling_settings': {'autoscaling_window': 800, 'concurrency_target': 4, 'max_replica': 3, 'min_replica': 2, 'scale_down_delay': 63, 'target_in_flight_tokens': None, 'target_utilization_percentage': None}, 'chainlet_name': 'HelloWorld'}]}\n)\n\nprint(response.text)" + } + ], + "description": "Updates a chainlet environment's autoscaling settings and returns the updated chainlet environment settings.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateChainletEnvironmentAutoscalingSettingsRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/chains/{chain_id}/environments/{env_name}/chainlet_settings/instance_types/update": { + "post": { + "summary": "Update a chainlet environment's instance type settings.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name}/chainlet_settings/instance_types/update \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"updates\": [\n {\n \"chainlet_name\": \"HelloWorld\",\n \"instance_type_id\": \"1x4\"\n },\n {\n \"chainlet_name\": \"RandInt\",\n \"instance_type_id\": \"A10G:2x24x96\"\n }\n ]\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/chains/{chain_id}/environments/{env_name}/chainlet_settings/instance_types/update\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'updates': [{'chainlet_name': 'HelloWorld', 'instance_type_id': '1x4'}, {'chainlet_name': 'RandInt', 'instance_type_id': 'A10G:2x24x96'}]}\n)\n\nprint(response.text)" + } + ], + "description": "Updates a chainlet environment's instance type settings. The chainlet environment setting must exist. When updated, a new chain deployment is created and deployed. It is promoted to the chain environment according to promotion settings on the environment.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateChainletEnvironmentInstanceTypeRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateChainletEnvironmentInstanceTypeResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/chain_id" + }, + { + "$ref": "#/components/parameters/env_name" + } + ] + }, + "/v1/teams/{team_id}/training_projects": { + "post": { + "summary": "Upsert a training project in a specific team.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/teams/{team_id}/training_projects \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"training_project\": {\n \"name\": \"My Training Project\"\n }\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/teams/{team_id}/training_projects\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'training_project': {'name': 'My Training Project'}}\n)\n\nprint(response.text)" + } + ], + "description": "Upserts a training project with the specified metadata for a team.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertTrainingProjectRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertTrainingProjectResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/team_id" + } + ] + }, + "/v1/training_projects": { + "get": { + "summary": "List training projects.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "List all training projects for the organization.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListTrainingProjectsResponseV1" + } + } + } + } + } + }, + "post": { + "summary": "Upsert a training project.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/training_projects \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"training_project\": {\n \"name\": \"My Training Project\"\n }\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'training_project': {'name': 'My Training Project'}}\n)\n\nprint(response.text)" + } + ], + "description": "Upserts a training project with the specified metadata.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertTrainingProjectRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertTrainingProjectResponseV1" + } + } + } + } + } + } + }, + "/v1/training_projects/{training_project_id}/jobs": { + "get": { + "summary": "List training jobs.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "List all training jobs for the training project.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListTrainingJobsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + } + ], + "post": { + "summary": "Create a training job.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"training_job\": {\n \"image\": {\n \"base_image\": \"hello-world\",\n \"docker_auth\": null\n },\n \"compute\": {\n \"node_count\": 1,\n \"cpu_count\": 1,\n \"memory\": \"2Gi\",\n \"accelerator\": {\n \"accelerator\": \"H100\",\n \"count\": 2\n }\n },\n \"runtime\": {\n \"start_commands\": [\n \"python main.py\"\n ],\n \"environment_variables\": {\n \"API_KEY\": \"your_api_key_here\",\n \"PATH\": \"/usr/bin\"\n },\n \"artifacts\": null,\n \"enable_cache\": true,\n \"cache_config\": {\n \"enable_legacy_hf_mount\": true,\n \"enabled\": true,\n \"mount_base_path\": \"/root/.cache\",\n \"require_cache_affinity\": true\n },\n \"checkpointing_config\": {\n \"enabled\": true,\n \"checkpoint_path\": \"/mnt/ckpts\",\n \"volume_size_gib\": 10\n },\n \"load_checkpoint_config\": null\n },\n \"name\": \"gpt-oss-job\",\n \"truss_user_env\": null,\n \"interactive_session\": null,\n \"weights\": [\n {\n \"allow_patterns\": null,\n \"auth\": null,\n \"auth_secret_name\": null,\n \"ignore_patterns\": null,\n \"mount_location\": \"/app/models/base\",\n \"source\": \"hf://meta-llama/Llama-3-8B@main\"\n }\n ]\n }\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'training_job': {'image': {'base_image': 'hello-world', 'docker_auth': None}, 'compute': {'node_count': 1, 'cpu_count': 1, 'memory': '2Gi', 'accelerator': {'accelerator': 'H100', 'count': 2}}, 'runtime': {'start_commands': ['python main.py'], 'environment_variables': {'API_KEY': 'your_api_key_here', 'PATH': '/usr/bin'}, 'artifacts': None, 'enable_cache': True, 'cache_config': {'enable_legacy_hf_mount': True, 'enabled': True, 'mount_base_path': '/root/.cache', 'require_cache_affinity': True}, 'checkpointing_config': {'enabled': True, 'checkpoint_path': '/mnt/ckpts', 'volume_size_gib': 10}, 'load_checkpoint_config': None}, 'name': 'gpt-oss-job', 'truss_user_env': None, 'interactive_session': None, 'weights': [{'allow_patterns': None, 'auth': None, 'auth_secret_name': None, 'ignore_patterns': None, 'mount_location': '/app/models/base', 'source': 'hf://meta-llama/Llama-3-8B@main'}]}}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a training job with the specified configuration.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTrainingJobRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTrainingJobResponseV1" + } + } + } + } + } + } + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}": { + "delete": { + "summary": "Delete a training job.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deletes a training job. Stops it first if still running.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TrainingJobTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ], + "get": { + "summary": "Get a training job.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Get the details of an existing training job.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetTrainingJobResponseV1" + } + } + } + } + } + } + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/download": { + "get": { + "summary": "Get the uploaded training job as a S3 Artifact", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/download \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/download\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Get the uploaded training job as a S3 Artifact", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DownloadTrainingJobResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/recreate": { + "post": { + "summary": "Recreate a training job", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/recreate \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/recreate\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Create a new training job with the same configuration as an existing training job.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RecreateTrainingJobResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/logs": { + "post": { + "summary": "Get the logs for a training job.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/logs \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"start_epoch_millis\": null,\n \"end_epoch_millis\": null,\n \"direction\": null,\n \"limit\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/logs\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'start_epoch_millis': None, 'end_epoch_millis': None, 'direction': None, 'limit': None}\n)\n\nprint(response.text)" + } + ], + "description": "Get the logs for a training job with the provided filters.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetTrainingJobLogsRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetLogsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/metrics": { + "post": { + "summary": "Get the metrics for a training job.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/metrics \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"end_epoch_millis\": null,\n \"start_epoch_millis\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/metrics\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'end_epoch_millis': None, 'start_epoch_millis': None}\n)\n\nprint(response.text)" + } + ], + "description": "Get the metrics for a training job.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetTrainingJobMetricsRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetTrainingJobMetricsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/stop": { + "post": { + "summary": "Stop a training job.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/stop \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/stop\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Stops a training job.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StopTrainingJobRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StopTrainingJobResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/checkpoints": { + "get": { + "summary": "Get training job checkpoints.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/checkpoints \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/checkpoints\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Get the checkpoints for a training job.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetTrainingJobCheckpointsResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/checkpoint_files": { + "get": { + "summary": "Get training job checkpoint files.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/checkpoint_files \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/checkpoint_files\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Get presigned URLs for all checkpoint files for a training job.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetTrainingJobCheckpointFilesResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/auth_codes": { + "get": { + "summary": "Get auth codes for a training job.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/auth_codes \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/auth_codes\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Get authentication codes for all nodes of a training job's interactive sessions.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetAuthCodesResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/jobs/{training_job_id}/interactive_sessions/{session_id}": { + "patch": { + "summary": "Patch an interactive session.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/interactive_sessions/{session_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"timeout_minutes\": null,\n \"trigger\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/jobs/{training_job_id}/interactive_sessions/{session_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'timeout_minutes': None, 'trigger': None}\n)\n\nprint(response.text)" + } + ], + "description": "Update specific fields on a training job's interactive session. Only provided (non-null) fields are updated.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchInteractiveSessionRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchInteractiveSessionResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + }, + { + "$ref": "#/components/parameters/training_job_id" + }, + { + "$ref": "#/components/parameters/session_id" + } + ] + }, + "/v1/training_projects/{training_project_id}/cache/summary": { + "get": { + "summary": "Get training project cache summary.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id}/cache/summary \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}/cache/summary\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Get the cache summary for the most recent training job in the project.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetCacheSummaryResponseV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + } + ] + }, + "/v1/training_projects/{training_project_id}": { + "delete": { + "summary": "Delete a training project.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deletes a training project and all associated training jobs.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TrainingProjectTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/training_project_id" + } + ], + "get": { + "summary": "Get a training project.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/training_projects/{training_project_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_projects/{training_project_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Get the details of an existing training project.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetTrainingProjectResponseV1" + } + } + } + } + } + } + }, + "/v1/training_jobs/search": { + "post": { + "summary": "Search training jobs.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/training_jobs/search \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"project_id\": \"n4q95w5\",\n \"job_id\": \"p7qr9qv\",\n \"statuses\": [\n \"TRAINING_JOB_RUNNING\",\n \"TRAINING_JOB_COMPLETED\"\n ],\n \"order_by\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/training_jobs/search\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'project_id': 'n4q95w5', 'job_id': 'p7qr9qv', 'statuses': ['TRAINING_JOB_RUNNING', 'TRAINING_JOB_COMPLETED'], 'order_by': None}\n)\n\nprint(response.text)" + } + ], + "description": "Search training jobs for the organization.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchTrainingJobsRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchTrainingJobsResponseV1" + } + } + } + } + } + } + }, + "/v1/blobs/credentials/model": { + "get": { + "summary": "Get blob credentials for models.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/blobs/credentials/model \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/blobs/credentials/model\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetBlobCredentialsResponseV1" + } + } + } + } + } + } + }, + "/v1/blobs/credentials/train": { + "get": { + "summary": "Get blob credentials for training.", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/blobs/credentials/train \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/blobs/credentials/train\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetBlobCredentialsResponseV1" + } + } + } + } + } + } + }, + "/v1/teams/{team_id}/api_keys": { + "post": { + "summary": "Creates a team API key", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/teams/{team_id}/api_keys \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"name\": \"my-api-key\",\n \"type\": \"PERSONAL\"\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/teams/{team_id}/api_keys\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'name': 'my-api-key', 'type': 'PERSONAL'}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a team API key with the provided name and type. The API key is returned in the response.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateAPIKeyRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIKeyV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/team_id" + } + ] + }, + "/v1/api_keys": { + "get": { + "summary": "Lists the user's API keys", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/api_keys \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/api_keys\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIKeysV1" + } + } + } + } + } + }, + "post": { + "summary": "Creates an API key", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/api_keys \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"name\": \"my-api-key\",\n \"type\": \"PERSONAL\"\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/api_keys\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'name': 'my-api-key', 'type': 'PERSONAL'}\n)\n\nprint(response.text)" + } + ], + "description": "Creates an API key with the provided name and type. The API key is returned in the response.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateAPIKeyRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIKeyV1" + } + } + } + } + } + } + }, + "/v1/api_keys/{api_key_prefix}": { + "delete": { + "summary": "Deletes an API key by prefix", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/api_keys/{api_key_prefix} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/api_keys/{api_key_prefix}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deletes an API key by prefix and returns info about the API key.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIKeyTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/api_key_prefix" + } + ] + }, + "/v1/model_apis/snapshots": { + "get": { + "summary": "Get the latest model weight snapshot", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/model_apis/snapshots \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/model_apis/snapshots\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets the most recent model weight snapshot for the specified model.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelWeightSnapshotV1" + } + } + } + } + } + }, + "post": { + "summary": "Create a model weight snapshot", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/model_apis/snapshots \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"model\": null,\n \"snapshot_uri\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/model_apis/snapshots\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'model': None, 'snapshot_uri': None}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a model weight snapshot for the specified model and returns the snapshot.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateModelWeightSnapshotRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelWeightSnapshotV1" + } + } + } + } + } + } + }, + "/v1/model_apis/snapshots/{model_id}": { + "get": { + "summary": "Get the latest model weight snapshot", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/model_apis/snapshots/{model_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/model_apis/snapshots/{model_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Gets the most recent model weight snapshot for the specified model.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelWeightSnapshotV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ], + "post": { + "summary": "Create a model weight snapshot", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/model_apis/snapshots/{model_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"model\": null,\n \"snapshot_uri\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/model_apis/snapshots/{model_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'model': None, 'snapshot_uri': None}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a model weight snapshot for the specified model and returns the snapshot.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateModelWeightSnapshotRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelWeightSnapshotV1" + } + } + } + } + } + } + }, + "/v1/llm_models": { + "post": { + "summary": "Creates a new BIS LLM deployment", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/llm_models \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"resources\": null,\n \"llm_version\": null,\n \"llm_config\": null,\n \"environment_variables\": null,\n \"autoscaling_settings\": {\n \"autoscaling_window\": 600,\n \"concurrency_target\": null,\n \"max_replica\": 5,\n \"min_replica\": 1,\n \"scale_down_delay\": 300,\n \"target_in_flight_tokens\": null,\n \"target_utilization_percentage\": null\n },\n \"additional_autoscaling_config\": {\n \"metrics\": [\n {\n \"name\": \"in_flight_tokens\",\n \"target\": 40000\n }\n ]\n },\n \"metadata\": {\n \"environment\": \"production\",\n \"git_sha\": \"abc123\"\n },\n \"name\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/llm_models\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'resources': None, 'llm_version': None, 'llm_config': None, 'environment_variables': None, 'autoscaling_settings': {'autoscaling_window': 600, 'concurrency_target': None, 'max_replica': 5, 'min_replica': 1, 'scale_down_delay': 300, 'target_in_flight_tokens': None, 'target_utilization_percentage': None}, 'additional_autoscaling_config': {'metrics': [{'name': 'in_flight_tokens', 'target': 40000}]}, 'metadata': {'environment': 'production', 'git_sha': 'abc123'}, 'name': None}\n)\n\nprint(response.text)" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLLMModelRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LLMModelV1" + } + } + } + } + } + } + }, + "/v1/llm_models/{model_id}/deployments": { + "post": { + "summary": "Creates a new BIS LLM deployment version", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/llm_models/{model_id}/deployments \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"resources\": null,\n \"llm_version\": null,\n \"llm_config\": null,\n \"environment_variables\": null,\n \"autoscaling_settings\": {\n \"autoscaling_window\": 600,\n \"concurrency_target\": null,\n \"max_replica\": 5,\n \"min_replica\": 1,\n \"scale_down_delay\": 300,\n \"target_in_flight_tokens\": null,\n \"target_utilization_percentage\": null\n },\n \"additional_autoscaling_config\": {\n \"metrics\": [\n {\n \"name\": \"in_flight_tokens\",\n \"target\": 40000\n }\n ]\n },\n \"metadata\": {\n \"environment\": \"production\",\n \"git_sha\": \"abc123\"\n }\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/llm_models/{model_id}/deployments\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'resources': None, 'llm_version': None, 'llm_config': None, 'environment_variables': None, 'autoscaling_settings': {'autoscaling_window': 600, 'concurrency_target': None, 'max_replica': 5, 'min_replica': 1, 'scale_down_delay': 300, 'target_in_flight_tokens': None, 'target_utilization_percentage': None}, 'additional_autoscaling_config': {'metrics': [{'name': 'in_flight_tokens', 'target': 40000}]}, 'metadata': {'environment': 'production', 'git_sha': 'abc123'}}\n)\n\nprint(response.text)" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLLMModelVersionRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LLMModelVersionV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/model_id" + } + ] + }, + "/v1/library_listings": { + "get": { + "summary": "Gets all library listings", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/library_listings \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Returns all library listings for the authenticated user's organization.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingsV1" + } + } + } + } + } + }, + "post": { + "summary": "Creates a new library listing", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/library_listings \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"display_name\": null,\n \"user_defined_id\": null,\n \"is_public\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'display_name': None, 'user_defined_id': None, 'is_public': None}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a new library listing for the authenticated user's organization.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLibraryListingRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingV1" + } + } + } + } + } + } + }, + "/v1/library_listings/{user_defined_listing_id}": { + "delete": { + "summary": "Deletes a library listing", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/library_listings/{user_defined_listing_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings/{user_defined_listing_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deletes a library listing and all of its associated versions. Any versions that are currently live will also be removed.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/user_defined_listing_id" + } + ], + "get": { + "summary": "Gets a library listing", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/library_listings/{user_defined_listing_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings/{user_defined_listing_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Returns a specific library listing by its user-defined identifier.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingV1" + } + } + } + } + } + }, + "patch": { + "summary": "Updates a library listing", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/library_listings/{user_defined_listing_id} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"display_name\": null,\n \"is_public\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings/{user_defined_listing_id}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'display_name': None, 'is_public': None}\n)\n\nprint(response.text)" + } + ], + "description": "Updates the display name of a library listing.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateLibraryListingRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingV1" + } + } + } + } + } + } + }, + "/v1/library_listings/{user_defined_listing_id}/versions": { + "get": { + "summary": "Gets all versions for a library listing", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Returns all versions for a specific library listing.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingVersionsV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/user_defined_listing_id" + } + ], + "post": { + "summary": "Creates a new library listing version", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request POST \\\n--url https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"display_name\": null,\n \"is_public\": null,\n \"oracle_version_id\": null,\n \"allow_truss_download\": null,\n \"version_tag\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"POST\",\n url,\n headers=headers,\n json={'display_name': None, 'is_public': None, 'oracle_version_id': None, 'allow_truss_download': None, 'version_tag': None}\n)\n\nprint(response.text)" + } + ], + "description": "Creates a new library listing version from an existing model version. The model version must be fully built (have an image_uri). If a listing with the given id already exists for the org, a new version is added. Otherwise, a new listing is created.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLibraryListingVersionRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingVersionV1" + } + } + } + } + } + } + }, + "/v1/library_listings/{user_defined_listing_id}/versions/{version_tag}": { + "delete": { + "summary": "Deletes a library listing version", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request DELETE \\\n--url https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions/{version_tag} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions/{version_tag}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"DELETE\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Deletes a specific version of a library listing. Deleting a live version will fail with a 400 error \u2014 demote the version first by setting another version as live.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingVersionTombstoneV1" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/user_defined_listing_id" + }, + { + "$ref": "#/components/parameters/version_tag" + } + ], + "get": { + "summary": "Gets a library listing version", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions/{version_tag} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions/{version_tag}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Returns a specific version of a library listing.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingVersionV1" + } + } + } + } + } + }, + "patch": { + "summary": "Updates a library listing version", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request PATCH \\\n--url https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions/{version_tag} \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\" \\\n--data '{\n \"is_live\": null,\n \"allow_truss_download\": null\n}'" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/library_listings/{user_defined_listing_id}/versions/{version_tag}\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"PATCH\",\n url,\n headers=headers,\n json={'is_live': None, 'allow_truss_download': None}\n)\n\nprint(response.text)" + } + ], + "description": "Updates a library listing version. Setting is_live to true will demote the current live version.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateLibraryListingVersionRequestV1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibraryListingVersionV1" + } + } + } + } + } + } + }, + "/v1/billing/usage_summary": { + "get": { + "summary": "Gets billing usage summary for a date range", + "x-codeSamples": [ + { + "lang": "bash", + "source": "curl --request GET \\\n--url https://api.baseten.co/v1/billing/usage_summary \\\n--header \"Authorization: Api-Key $BASETEN_API_KEY\"\n" + }, + { + "lang": "python", + "source": "import requests\nimport os\nAPI_KEY = os.environ.get(\"BASETEN_API_KEY\", \"\")\nurl = \"https://api.baseten.co/v1/billing/usage_summary\"\n\nheaders = {\"Authorization\": f\"Api-Key {API_KEY}\"}\n\nresponse = requests.request(\n \"GET\",\n url,\n headers=headers,\n json={}\n)\n\nprint(response.text)" + } + ], + "description": "Returns billing usage data within the specified date range. Includes dedicated model serving, training, and model APIs usage. The date range must not exceed 31 days.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UsageSummaryV1" + } + } + } + } + } + } + } + }, + "openapi": "3.1.0", + "components": { + "schemas": { + "SecretV1": { + "description": "A Baseten secret. Note that we do not support retrieving secret values.", + "properties": { + "created_at": { + "description": "Time the secret was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "name": { + "description": "Name of the secret", + "title": "Name", + "type": "string" + }, + "team_name": { + "description": "Name of the team the secret belongs to", + "title": "Team Name", + "type": "string" + } + }, + "required": [ + "created_at", + "name", + "team_name" + ], + "title": "SecretV1", + "type": "object" + }, + "SecretsV1": { + "description": "A list of Baseten secrets.", + "properties": { + "secrets": { + "items": { + "$ref": "#/components/schemas/SecretV1" + }, + "title": "Secrets", + "type": "array" + } + }, + "required": [ + "secrets" + ], + "title": "SecretsV1", + "type": "object" + }, + "UpsertSecretRequestV1": { + "description": "A request to create or update a Baseten secret by name.", + "properties": { + "name": { + "description": "Name of the new or existing secret", + "examples": [ + "my_secret" + ], + "title": "Name", + "type": "string" + }, + "value": { + "description": "Value of the secret", + "examples": [ + "my_secret_value" + ], + "title": "Value", + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "title": "UpsertSecretRequestV1", + "type": "object" + }, + "TeamV1": { + "description": "A team.", + "properties": { + "id": { + "description": "Unique identifier of the team", + "title": "Id", + "type": "string" + }, + "name": { + "description": "Name of the team", + "title": "Name", + "type": "string" + }, + "default": { + "type": "boolean", + "description": "Whether this is the default team for the organization", + "title": "Default" + }, + "created_at": { + "description": "Time the team was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + } + }, + "required": [ + "id", + "name", + "default", + "created_at" + ], + "title": "TeamV1", + "type": "object" + }, + "TeamsV1": { + "description": "A list of teams.", + "properties": { + "teams": { + "description": "A list of teams", + "items": { + "$ref": "#/components/schemas/TeamV1" + }, + "title": "Teams", + "type": "array" + } + }, + "required": [ + "teams" + ], + "title": "TeamsV1", + "type": "object" + }, + "InstanceTypeV1": { + "description": "An instance type.", + "properties": { + "id": { + "description": "Identifier string for the instance type", + "title": "Id", + "type": "string" + }, + "name": { + "description": "Display name of the instance type", + "title": "Name", + "type": "string" + }, + "memory_limit_mib": { + "description": "Memory limit of the instance type in Mebibytes", + "title": "Memory Limit Mib", + "type": "integer" + }, + "millicpu_limit": { + "description": "CPU limit of the instance type in millicpu", + "title": "Millicpu Limit", + "type": "integer" + }, + "gpu_count": { + "description": "Number of GPUs on the instance type", + "title": "Gpu Count", + "type": "integer" + }, + "gpu_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Type of GPU on the instance type", + "title": "Gpu Type" + }, + "gpu_memory_limit_mib": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Memory limit of the GPU on the instance type in Mebibytes", + "title": "Gpu Memory Limit Mib" + } + }, + "required": [ + "id", + "name", + "memory_limit_mib", + "millicpu_limit", + "gpu_count", + "gpu_type", + "gpu_memory_limit_mib" + ], + "title": "InstanceTypeV1", + "type": "object" + }, + "InstanceTypesV1": { + "description": "A list of instance types.", + "properties": { + "instance_types": { + "items": { + "$ref": "#/components/schemas/InstanceTypeV1" + }, + "title": "Instance Types", + "type": "array" + } + }, + "required": [ + "instance_types" + ], + "title": "InstanceTypesV1", + "type": "object" + }, + "InstanceTypeWithPriceV1": { + "properties": { + "instance_type": { + "$ref": "#/components/schemas/InstanceTypeV1", + "description": "Instance type properties." + }, + "price": { + "description": "Usage price in USD / minute.", + "title": "Price", + "type": "number" + } + }, + "required": [ + "instance_type", + "price" + ], + "title": "InstanceTypeWithPriceV1", + "type": "object" + }, + "InstanceTypePricesV1": { + "description": "A list of instance types.", + "properties": { + "instance_types": { + "items": { + "$ref": "#/components/schemas/InstanceTypeWithPriceV1" + }, + "title": "Instance Types", + "type": "array" + } + }, + "required": [ + "instance_types" + ], + "title": "InstanceTypePricesV1", + "type": "object" + }, + "ModelV1": { + "description": "A model.", + "properties": { + "id": { + "description": "Unique identifier of the model", + "title": "Id", + "type": "string" + }, + "created_at": { + "description": "Time the model was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "name": { + "description": "Name of the model", + "title": "Name", + "type": "string" + }, + "deployments_count": { + "description": "Number of deployments of the model", + "title": "Deployments Count", + "type": "integer" + }, + "production_deployment_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Unique identifier of the production deployment of the model", + "title": "Production Deployment Id" + }, + "development_deployment_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Unique identifier of the development deployment of the model", + "title": "Development Deployment Id" + }, + "instance_type_name": { + "description": "Name of the instance type for the production deployment of the model", + "title": "Instance Type Name", + "type": "string" + }, + "team_name": { + "description": "Name of the team associated with the model.", + "title": "Team Name", + "type": "string" + } + }, + "required": [ + "id", + "created_at", + "name", + "deployments_count", + "production_deployment_id", + "development_deployment_id", + "instance_type_name", + "team_name" + ], + "title": "ModelV1", + "type": "object" + }, + "ModelsV1": { + "description": "A list of models.", + "properties": { + "models": { + "items": { + "$ref": "#/components/schemas/ModelV1" + }, + "title": "Models", + "type": "array" + } + }, + "required": [ + "models" + ], + "title": "ModelsV1", + "type": "object" + }, + "ModelTombstoneV1": { + "description": "A model tombstone.", + "properties": { + "id": { + "description": "Unique identifier of the model", + "title": "Id", + "type": "string" + }, + "deleted": { + "description": "Whether the model was deleted", + "title": "Deleted", + "type": "boolean" + } + }, + "required": [ + "id", + "deleted" + ], + "title": "ModelTombstoneV1", + "type": "object" + }, + "AutoscalingSettingsV1": { + "description": "Autoscaling settings for a deployment.", + "properties": { + "min_replica": { + "description": "Minimum number of replicas", + "title": "Min Replica", + "type": "integer" + }, + "max_replica": { + "description": "Maximum number of replicas", + "title": "Max Replica", + "type": "integer" + }, + "autoscaling_window": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Timeframe of traffic considered for autoscaling decisions", + "title": "Autoscaling Window" + }, + "scale_down_delay": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Waiting period before scaling down any active replica", + "title": "Scale Down Delay" + }, + "concurrency_target": { + "description": "Number of requests per replica before scaling up", + "title": "Concurrency Target", + "type": "integer" + }, + "target_utilization_percentage": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Target utilization percentage for scaling up/down.", + "title": "Target Utilization Percentage" + }, + "target_in_flight_tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Target number of in-flight tokens for autoscaling decisions. Early access only.", + "title": "Target In Flight Tokens" + } + }, + "required": [ + "min_replica", + "max_replica", + "autoscaling_window", + "scale_down_delay", + "concurrency_target", + "target_utilization_percentage" + ], + "title": "AutoscalingSettingsV1", + "type": "object" + }, + "DeploymentStatusV1": { + "description": "The status of a deployment.", + "enum": [ + "BUILDING", + "DEPLOYING", + "DEPLOY_FAILED", + "LOADING_MODEL", + "ACTIVE", + "UNHEALTHY", + "BUILD_FAILED", + "BUILD_STOPPED", + "DEACTIVATING", + "INACTIVE", + "FAILED", + "UPDATING", + "SCALED_TO_ZERO", + "WAKING_UP" + ], + "title": "DeploymentStatusV1", + "type": "string" + }, + "DeploymentV1": { + "description": "A deployment of a model.", + "properties": { + "id": { + "description": "Unique identifier of the deployment", + "title": "Id", + "type": "string" + }, + "created_at": { + "description": "Time the deployment was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "name": { + "description": "Name of the deployment", + "title": "Name", + "type": "string" + }, + "model_id": { + "description": "Unique identifier of the model", + "title": "Model Id", + "type": "string" + }, + "is_production": { + "description": "Whether the deployment is the production deployment of the model", + "title": "Is Production", + "type": "boolean" + }, + "is_development": { + "description": "Whether the deployment is the development deployment of the model", + "title": "Is Development", + "type": "boolean" + }, + "status": { + "$ref": "#/components/schemas/DeploymentStatusV1", + "description": "Status of the deployment" + }, + "active_replica_count": { + "description": "Number of active replicas", + "title": "Active Replica Count", + "type": "integer" + }, + "autoscaling_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/AutoscalingSettingsV1" + }, + { + "type": "null" + } + ], + "description": "Autoscaling settings for the deployment. If null, the model has not finished deploying" + }, + "instance_type_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Name of the instance type the model deployment is running on", + "title": "Instance Type Name" + }, + "environment": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The environment associated with the deployment", + "title": "Environment" + }, + "labels": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "User-provided key-value labels for the deployment", + "title": "Labels" + } + }, + "required": [ + "id", + "created_at", + "name", + "model_id", + "is_production", + "is_development", + "status", + "active_replica_count", + "autoscaling_settings", + "instance_type_name", + "environment" + ], + "title": "DeploymentV1", + "type": "object" + }, + "DeploymentsV1": { + "description": "A list of deployments of a model.", + "properties": { + "deployments": { + "description": "A list of deployments of a model", + "items": { + "$ref": "#/components/schemas/DeploymentV1" + }, + "title": "Deployments", + "type": "array" + } + }, + "required": [ + "deployments" + ], + "title": "DeploymentsV1", + "type": "object" + }, + "DeploymentTombstoneV1": { + "description": "A model deployment tombstone.", + "properties": { + "id": { + "description": "Unique identifier of the deployment", + "title": "Id", + "type": "string" + }, + "deleted": { + "description": "Whether the deployment was deleted", + "title": "Deleted", + "type": "boolean" + }, + "model_id": { + "description": "Unique identifier of the model", + "title": "Model Id", + "type": "string" + } + }, + "required": [ + "id", + "deleted", + "model_id" + ], + "title": "DeploymentTombstoneV1", + "type": "object" + }, + "UpdateAutoscalingSettingsV1": { + "additionalProperties": false, + "description": "A request to update autoscaling settings for a deployment. All fields are optional, and we only update ones passed in.", + "properties": { + "min_replica": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum number of replicas", + "examples": [ + 0 + ], + "title": "Min Replica" + }, + "max_replica": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum number of replicas", + "examples": [ + 7 + ], + "title": "Max Replica" + }, + "autoscaling_window": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Timeframe of traffic considered for autoscaling decisions", + "examples": [ + 600 + ], + "title": "Autoscaling Window" + }, + "scale_down_delay": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Waiting period before scaling down any active replica", + "examples": [ + 120 + ], + "title": "Scale Down Delay" + }, + "concurrency_target": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Number of requests per replica before scaling up", + "examples": [ + 2 + ], + "title": "Concurrency Target" + }, + "target_utilization_percentage": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Target utilization percentage for scaling up/down.", + "examples": [ + 70 + ], + "title": "Target Utilization Percentage" + }, + "target_in_flight_tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Target number of in-flight tokens for autoscaling decisions. Early access only.", + "examples": [ + 40000 + ], + "title": "Target In Flight Tokens" + } + }, + "title": "UpdateAutoscalingSettingsV1", + "type": "object" + }, + "UpdateAutoscalingSettingsStatusV1": { + "description": "The status of a request to update autoscaling settings.", + "enum": [ + "ACCEPTED", + "QUEUED", + "UNCHANGED" + ], + "title": "UpdateAutoscalingSettingsStatusV1", + "type": "string" + }, + "UpdateAutoscalingSettingsResponseV1": { + "description": "The response to a request to update autoscaling settings.", + "properties": { + "status": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsStatusV1", + "description": "Status of the request to update autoscaling settings" + }, + "message": { + "description": "A message describing the status of the request to update autoscaling settings", + "title": "Message", + "type": "string" + } + }, + "required": [ + "status", + "message" + ], + "title": "UpdateAutoscalingSettingsResponseV1", + "type": "object" + }, + "PromoteRequestV1": { + "description": "A request to promote a deployment to production.", + "properties": { + "scale_down_previous_production": { + "default": true, + "description": "Whether to scale down the previous production deployment after promoting", + "examples": [ + true + ], + "title": "Scale Down Previous Production", + "type": "boolean" + }, + "preserve_env_instance_type": { + "default": true, + "description": "Whether to use the promoting deployment's instance type or preserve target environment's instance type", + "examples": [ + true + ], + "title": "Preserve Env Instance Type", + "type": "boolean" + } + }, + "title": "PromoteRequestV1", + "type": "object" + }, + "ActivateResponseV1": { + "description": "The response to a request to activate a deployment.", + "properties": { + "success": { + "default": true, + "description": "Whether the deployment was successfully activated", + "title": "Success", + "type": "boolean" + } + }, + "title": "ActivateResponseV1", + "type": "object" + }, + "DeactivateResponseV1": { + "description": "The response to a request to deactivate a deployment.", + "properties": { + "success": { + "default": true, + "description": "Whether the deployment was successfully deactivated", + "title": "Success", + "type": "boolean" + } + }, + "title": "DeactivateResponseV1", + "type": "object" + }, + "RetryDeploymentResponseV1": { + "description": "The response to a request to retry a deployment.", + "properties": { + "retried": { + "description": "Whether the retry was successfully initiated", + "title": "Retried", + "type": "boolean" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Explanation of the result. Provided when retried is false to explain why retry was not possible.", + "title": "Reason" + }, + "deployment": { + "$ref": "#/components/schemas/DeploymentV1", + "description": "The deployment that was retried" + } + }, + "required": [ + "retried", + "deployment" + ], + "title": "RetryDeploymentResponseV1", + "type": "object" + }, + "SortOrderV1": { + "enum": [ + "asc", + "desc" + ], + "title": "SortOrderV1", + "type": "string" + }, + "GetDeploymentLogsRequestV1": { + "description": "A request to fetch deployment logs.", + "properties": { + "start_epoch_millis": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Epoch millis timestamp to start fetching logs", + "title": "Start Epoch Millis" + }, + "end_epoch_millis": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Epoch millis timestamp to end fetching logs", + "title": "End Epoch Millis" + }, + "direction": { + "anyOf": [ + { + "$ref": "#/components/schemas/SortOrderV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Sort order for logs" + }, + "limit": { + "anyOf": [ + { + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 500, + "description": "Limit of logs to fetch in a single request", + "title": "Limit" + } + }, + "title": "GetDeploymentLogsRequestV1", + "type": "object" + }, + "LogV1": { + "properties": { + "timestamp": { + "description": "Epoch nanosecond timestamp of the log message.", + "title": "Timestamp", + "type": "string" + }, + "message": { + "description": "The contents of the log message.", + "title": "Message", + "type": "string" + }, + "replica": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The replica the log line was emitted from.", + "title": "Replica" + } + }, + "required": [ + "timestamp", + "message", + "replica" + ], + "title": "LogV1", + "type": "object" + }, + "GetLogsResponseV1": { + "description": "A response to querying logs.", + "properties": { + "logs": { + "description": "Logs for a specific entity.", + "items": { + "$ref": "#/components/schemas/LogV1" + }, + "title": "Logs", + "type": "array" + } + }, + "required": [ + "logs" + ], + "title": "GetLogsResponseV1", + "type": "object" + }, + "TerminateReplicaResponseV1": { + "description": "The response to a request to terminate a replica in a deployment.", + "properties": { + "success": { + "default": true, + "description": "Whether the replica was successfully terminated", + "title": "Success", + "type": "boolean" + } + }, + "title": "TerminateReplicaResponseV1", + "type": "object" + }, + "EnvironmentV1": { + "description": "Environment for oracles.", + "properties": { + "name": { + "description": "Name of the environment", + "title": "Name", + "type": "string" + }, + "created_at": { + "description": "Time the environment was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "model_id": { + "description": "Unique identifier of the model", + "title": "Model Id", + "type": "string" + }, + "current_deployment": { + "anyOf": [ + { + "$ref": "#/components/schemas/DeploymentV1" + }, + { + "type": "null" + } + ], + "description": "Current deployment of the environment" + }, + "candidate_deployment": { + "anyOf": [ + { + "$ref": "#/components/schemas/DeploymentV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Candidate deployment being promoted to the environment, if a promotion is in progress" + }, + "in_progress_promotion": { + "anyOf": [ + { + "$ref": "#/components/schemas/InProgressPromotionV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Details of the in-progress promotion, if any" + }, + "autoscaling_settings": { + "$ref": "#/components/schemas/AutoscalingSettingsV1", + "description": "Autoscaling settings for the environment" + }, + "promotion_settings": { + "$ref": "#/components/schemas/PromotionSettingsV1", + "description": "Promotion settings for the environment" + }, + "instance_type": { + "$ref": "#/components/schemas/InstanceTypeV1", + "description": "Instance type for the environment" + } + }, + "required": [ + "name", + "created_at", + "model_id", + "current_deployment", + "autoscaling_settings", + "promotion_settings", + "instance_type" + ], + "title": "EnvironmentV1", + "type": "object" + }, + "InProgressPromotionStatusV1": { + "enum": [ + "RELEASING", + "RAMPING_UP", + "RAMPING_DOWN", + "PAUSED", + "SUCCEEDED", + "FAILED", + "CANCELED" + ], + "title": "InProgressPromotionStatusV1", + "type": "string" + }, + "InProgressPromotionV1": { + "description": "Details of an in-progress promotion.", + "properties": { + "status": { + "$ref": "#/components/schemas/InProgressPromotionStatusV1", + "description": "Status of the promotion" + }, + "percent_traffic_to_new_version": { + "description": "Percentage of traffic routed to the candidate deployment", + "title": "Percent Traffic To New Version", + "type": "integer" + }, + "error_message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Error message if promotion failed", + "title": "Error Message" + }, + "rolling_deploy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether this is a rolling deploy", + "title": "Rolling Deploy" + } + }, + "required": [ + "status", + "percent_traffic_to_new_version" + ], + "title": "InProgressPromotionV1", + "type": "object" + }, + "PromotionCleanupStrategyV1": { + "description": "The promotion cleanup strategy.", + "enum": [ + "KEEP", + "SCALE_TO_ZERO", + "DEACTIVATE" + ], + "title": "PromotionCleanupStrategyV1", + "type": "string" + }, + "PromotionSettingsV1": { + "description": "Promotion settings for promoting chains and oracles", + "properties": { + "redeploy_on_promotion": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": false, + "description": "Whether to deploy on all promotions. Enabling this flag allows model code to safely handle environment-specific logic. When a deployment is promoted, a new deployment will be created with a copy of the image.", + "examples": [ + true + ], + "title": "Redeploy On Promotion" + }, + "rolling_deploy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": false, + "description": "Whether the environment should rely on rolling deploy orchestration.", + "examples": [ + true + ], + "title": "Rolling Deploy" + }, + "promotion_cleanup_strategy": { + "anyOf": [ + { + "$ref": "#/components/schemas/PromotionCleanupStrategyV1" + }, + { + "type": "null" + } + ], + "default": "SCALE_TO_ZERO", + "description": "The cleanup strategy to use after a promotion completes.", + "examples": [ + "SCALE_TO_ZERO" + ] + }, + "rolling_deploy_config": { + "anyOf": [ + { + "$ref": "#/components/schemas/RollingDeployConfigV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Rolling deploy configuration for promotions" + }, + "ramp_up_while_promoting": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": false, + "description": "Whether to ramp up traffic while promoting", + "examples": [ + true + ], + "title": "Ramp Up While Promoting" + }, + "ramp_up_duration_seconds": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 600, + "description": "Duration of the ramp up in seconds", + "examples": [ + 600 + ], + "title": "Ramp Up Duration Seconds" + } + }, + "title": "PromotionSettingsV1", + "type": "object" + }, + "RollingDeployConfigV1": { + "description": "Rolling deploy config for promoting chains and oracles", + "properties": { + "rolling_deploy_strategy": { + "$ref": "#/components/schemas/RollingDeployStrategyV1", + "default": "REPLICA", + "description": "The rolling deploy strategy to use for promotions.", + "examples": [ + "REPLICA" + ] + }, + "max_surge_percent": { + "default": 10, + "description": "The maximum surge percentage for rolling deploys.", + "examples": [ + 10 + ], + "title": "Max Surge Percent", + "type": "integer" + }, + "max_unavailable_percent": { + "default": 0, + "description": "The maximum unavailable percentage for rolling deploys.", + "examples": [ + 10 + ], + "title": "Max Unavailable Percent", + "type": "integer" + }, + "stabilization_time_seconds": { + "default": 0, + "description": "The stabilization time in seconds for rolling deploys.", + "examples": [ + 300 + ], + "title": "Stabilization Time Seconds", + "type": "integer" + }, + "replica_overhead_percent": { + "default": 0, + "description": "The replica overhead percentage for rolling deploys.", + "examples": [ + 0 + ], + "title": "Replica Overhead Percent", + "type": "integer" + } + }, + "title": "RollingDeployConfigV1", + "type": "object" + }, + "RollingDeployStrategyV1": { + "description": "The rolling deploy strategy.", + "enum": [ + "REPLICA" + ], + "title": "RollingDeployStrategyV1", + "type": "string" + }, + "EnvironmentsV1": { + "description": "list of environments", + "properties": { + "environments": { + "items": { + "$ref": "#/components/schemas/EnvironmentV1" + }, + "title": "Environments", + "type": "array" + } + }, + "required": [ + "environments" + ], + "title": "EnvironmentsV1", + "type": "object" + }, + "UpdatePromotionSettingsV1": { + "description": "Promotion settings for model promotion", + "properties": { + "redeploy_on_promotion": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether to deploy on all promotions. Enabling this flag allows model code to safely handle environment-specific logic. When a deployment is promoted, a new deployment will be created with a copy of the image.", + "examples": [ + true + ], + "title": "Redeploy On Promotion" + }, + "rolling_deploy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether the environment should rely on rolling deploy orchestration.", + "examples": [ + true + ], + "title": "Rolling Deploy" + }, + "promotion_cleanup_strategy": { + "anyOf": [ + { + "$ref": "#/components/schemas/PromotionCleanupStrategyV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The cleanup strategy to use after a promotion completes.", + "examples": [ + "SCALE_TO_ZERO" + ] + }, + "rolling_deploy_config": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdateRollingDeployConfigV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Rolling deploy configuration for promotions" + }, + "ramp_up_while_promoting": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether to ramp up traffic while promoting", + "examples": [ + true + ], + "title": "Ramp Up While Promoting" + }, + "ramp_up_duration_seconds": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Duration of the ramp up in seconds", + "examples": [ + 600 + ], + "title": "Ramp Up Duration Seconds" + } + }, + "title": "UpdatePromotionSettingsV1", + "type": "object" + }, + "UpdateRollingDeployConfigV1": { + "description": "Rolling deploy config for promoting chains and oracles", + "properties": { + "rolling_deploy_strategy": { + "anyOf": [ + { + "$ref": "#/components/schemas/RollingDeployStrategyV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The rolling deploy strategy to use for promotions.", + "examples": [ + "REPLICA" + ] + }, + "max_surge_percent": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 10, + "description": "The maximum surge percentage for rolling deploys.", + "examples": [ + 10 + ], + "title": "Max Surge Percent" + }, + "max_unavailable_percent": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The maximum unavailable percentage for rolling deploys.", + "examples": [ + 10 + ], + "title": "Max Unavailable Percent" + }, + "stabilization_time_seconds": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The stabilization time in seconds for rolling deploys.", + "examples": [ + 300 + ], + "title": "Stabilization Time Seconds" + }, + "replica_overhead_percent": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The replica overhead percentage for rolling deploys.", + "examples": [ + 0 + ], + "title": "Replica Overhead Percent" + } + }, + "title": "UpdateRollingDeployConfigV1", + "type": "object" + }, + "CreateEnvironmentRequestV1": { + "description": "A request to create an environment.", + "properties": { + "name": { + "description": "Name of the environment", + "examples": [ + "staging" + ], + "title": "Name", + "type": "string" + }, + "autoscaling_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Autoscaling settings for the environment", + "examples": [ + { + "autoscaling_window": 800, + "concurrency_target": 3, + "max_replica": 2, + "min_replica": 1, + "scale_down_delay": 60, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + } + ] + }, + "promotion_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdatePromotionSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Promotion settings for the environment", + "examples": [ + { + "promotion_cleanup_strategy": null, + "ramp_up_duration_seconds": 600, + "ramp_up_while_promoting": true, + "redeploy_on_promotion": true, + "rolling_deploy": true, + "rolling_deploy_config": null + } + ] + } + }, + "required": [ + "name" + ], + "title": "CreateEnvironmentRequestV1", + "type": "object" + }, + "UpdateEnvironmentRequestV1": { + "additionalProperties": false, + "description": "A request to update an environment.", + "properties": { + "autoscaling_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Autoscaling settings for the environment", + "examples": [ + { + "autoscaling_window": 800, + "concurrency_target": 3, + "max_replica": 2, + "min_replica": 1, + "scale_down_delay": 60, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + } + ] + }, + "promotion_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdatePromotionSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Promotion settings for the environment", + "examples": [ + { + "promotion_cleanup_strategy": null, + "ramp_up_duration_seconds": 600, + "ramp_up_while_promoting": true, + "redeploy_on_promotion": true, + "rolling_deploy": null, + "rolling_deploy_config": null + } + ] + } + }, + "title": "UpdateEnvironmentRequestV1", + "type": "object" + }, + "PromoteToEnvironmentRequestV1": { + "description": "A request to promote a deployment to a environment.", + "properties": { + "scale_down_previous_deployment": { + "default": true, + "description": "Whether to scale down the previous deployment after promoting", + "examples": [ + true + ], + "title": "Scale Down Previous Deployment", + "type": "boolean" + }, + "deployment_id": { + "description": "The id of the deployment to promote", + "title": "Deployment Id", + "type": "string" + }, + "preserve_env_instance_type": { + "default": true, + "description": "Whether to use the promoting deployment's instance type or preserve target environment's instance type", + "examples": [ + true + ], + "title": "Preserve Env Instance Type", + "type": "boolean" + } + }, + "required": [ + "deployment_id" + ], + "title": "PromoteToEnvironmentRequestV1", + "type": "object" + }, + "CancelPromotionStatusV1": { + "description": "The status of a request to cancel a promotion.", + "enum": [ + "CANCELED", + "RAMPING_DOWN" + ], + "title": "CancelPromotionStatusV1", + "type": "string" + }, + "CancelPromotionResponseV1": { + "description": "The response to a request to cancel a promotion.", + "properties": { + "status": { + "$ref": "#/components/schemas/CancelPromotionStatusV1", + "description": "Status of the request to cancel a promotion. Can be CANCELED or RAMPING_DOWN." + }, + "message": { + "description": "A message describing the status of the request to cancel a promotion", + "title": "Message", + "type": "string" + } + }, + "required": [ + "status", + "message" + ], + "title": "CancelPromotionResponseV1", + "type": "object" + }, + "SignalPromotionResponseV1": { + "description": "The response to a request to signal a rolling promotion.", + "properties": { + "success": { + "description": "Whether the signal was successfully sent", + "title": "Success", + "type": "boolean" + } + }, + "required": [ + "success" + ], + "title": "SignalPromotionResponseV1", + "type": "object" + }, + "ChainV1": { + "description": "A chain.", + "properties": { + "id": { + "description": "Unique identifier of the chain", + "title": "Id", + "type": "string" + }, + "created_at": { + "description": "Time the chain was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "name": { + "description": "Name of the chain", + "title": "Name", + "type": "string" + }, + "deployments_count": { + "description": "Number of deployments of the chain", + "title": "Deployments Count", + "type": "integer" + }, + "team_name": { + "description": "Name of the team associated with the chain", + "title": "Team Name", + "type": "string" + } + }, + "required": [ + "id", + "created_at", + "name", + "deployments_count", + "team_name" + ], + "title": "ChainV1", + "type": "object" + }, + "ChainsV1": { + "description": "A list of chains.", + "properties": { + "chains": { + "items": { + "$ref": "#/components/schemas/ChainV1" + }, + "title": "Chains", + "type": "array" + } + }, + "required": [ + "chains" + ], + "title": "ChainsV1", + "type": "object" + }, + "ChainTombstoneV1": { + "description": "A chain tombstone.", + "properties": { + "id": { + "description": "Unique identifier of the chain", + "title": "Id", + "type": "string" + }, + "deleted": { + "description": "Whether the chain was deleted", + "title": "Deleted", + "type": "boolean" + } + }, + "required": [ + "id", + "deleted" + ], + "title": "ChainTombstoneV1", + "type": "object" + }, + "ChainDeploymentV1": { + "description": "A deployment of a chain.", + "properties": { + "id": { + "description": "Unique identifier of the chain deployment", + "title": "Id", + "type": "string" + }, + "created_at": { + "description": "Time the chain deployment was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "chain_id": { + "description": "Unique identifier of the chain", + "title": "Chain Id", + "type": "string" + }, + "environment": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Environment the chain deployment is deployed in", + "title": "Environment" + }, + "chainlets": { + "description": "Chainlets in the chain deployment", + "items": { + "$ref": "#/components/schemas/ChainletV1" + }, + "title": "Chainlets", + "type": "array" + }, + "status": { + "$ref": "#/components/schemas/DeploymentStatusV1", + "description": "Status of the chain deployment" + } + }, + "required": [ + "id", + "created_at", + "chain_id", + "environment", + "chainlets", + "status" + ], + "title": "ChainDeploymentV1", + "type": "object" + }, + "ChainletV1": { + "description": "A chainlet in a chain deployment.", + "properties": { + "id": { + "description": "Unique identifier of the chainlet", + "title": "Id", + "type": "string" + }, + "name": { + "description": "Name of the chainlet", + "title": "Name", + "type": "string" + }, + "autoscaling_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/AutoscalingSettingsV1" + }, + { + "type": "null" + } + ], + "description": "Autoscaling settings for the chainlet. If null, it has not finished deploying" + }, + "instance_type_name": { + "description": "Name of the instance type the chainlet is deployed on", + "title": "Instance Type Name", + "type": "string" + }, + "active_replica_count": { + "description": "Number of active replicas", + "title": "Active Replica Count", + "type": "integer" + }, + "status": { + "$ref": "#/components/schemas/DeploymentStatusV1", + "description": "Status of the chainlet" + } + }, + "required": [ + "id", + "name", + "autoscaling_settings", + "instance_type_name", + "active_replica_count", + "status" + ], + "title": "ChainletV1", + "type": "object" + }, + "ChainDeploymentsV1": { + "description": "A list of chain deployments.", + "properties": { + "deployments": { + "description": "A list of chain deployments", + "items": { + "$ref": "#/components/schemas/ChainDeploymentV1" + }, + "title": "Deployments", + "type": "array" + } + }, + "required": [ + "deployments" + ], + "title": "ChainDeploymentsV1", + "type": "object" + }, + "ChainDeploymentTombstoneV1": { + "description": "A chain deployment tombstone.", + "properties": { + "id": { + "description": "Unique identifier of the chain deployment", + "title": "Id", + "type": "string" + }, + "deleted": { + "description": "Whether the chain deployment was deleted", + "title": "Deleted", + "type": "boolean" + }, + "chain_id": { + "description": "Unique identifier of the chain", + "title": "Chain Id", + "type": "string" + } + }, + "required": [ + "id", + "deleted", + "chain_id" + ], + "title": "ChainDeploymentTombstoneV1", + "type": "object" + }, + "ChainletEnvironmentSettingsRequestV1": { + "description": "Request to create environment settings for a chainlet.", + "properties": { + "chainlet_name": { + "description": "Name of the chainlet", + "examples": [ + "HelloWorld" + ], + "title": "Chainlet Name", + "type": "string" + }, + "autoscaling_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Autoscaling settings for the chainlet", + "examples": [ + { + "autoscaling_window": 60, + "concurrency_target": 1, + "max_replica": 1, + "min_replica": 0, + "scale_down_delay": 900, + "target_in_flight_tokens": null, + "target_utilization_percentage": 70 + } + ] + }, + "instance_type_id": { + "default": "1x2", + "description": "ID of the instance type to use for the chainlet", + "examples": [ + "1x4", + "2x8", + "A10G:2x24x96", + "H100:2x52x468" + ], + "title": "Instance Type Id", + "type": "string" + } + }, + "required": [ + "chainlet_name" + ], + "title": "ChainletEnvironmentSettingsRequestV1", + "type": "object" + }, + "CreateChainEnvironmentRequestV1": { + "description": "A request to create a custom environment for a chain.", + "properties": { + "name": { + "description": "Name of the environment", + "examples": [ + "staging" + ], + "title": "Name", + "type": "string" + }, + "promotion_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdatePromotionSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Promotion settings for the environment", + "examples": [ + { + "promotion_cleanup_strategy": null, + "ramp_up_duration_seconds": 600, + "ramp_up_while_promoting": true, + "redeploy_on_promotion": true, + "rolling_deploy": null, + "rolling_deploy_config": null + } + ] + }, + "chainlet_settings": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ChainletEnvironmentSettingsRequestV1" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Mapping of chainlet name to the desired chainlet environment settings", + "examples": [ + [ + { + "autoscaling_settings": { + "autoscaling_window": 800, + "concurrency_target": 4, + "max_replica": 3, + "min_replica": 2, + "scale_down_delay": 63, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + }, + "chainlet_name": "HelloWorld", + "instance_type_id": "2x8" + }, + { + "autoscaling_settings": { + "autoscaling_window": null, + "concurrency_target": null, + "max_replica": 3, + "min_replica": 3, + "scale_down_delay": null, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + }, + "chainlet_name": "RandInt", + "instance_type_id": "A10Gx8x32" + } + ] + ], + "title": "Chainlet Settings" + } + }, + "required": [ + "name" + ], + "title": "CreateChainEnvironmentRequestV1", + "type": "object" + }, + "ChainletEnvironmentSettingsV1": { + "description": "Environment settings for a chainlet.", + "properties": { + "chainlet_name": { + "description": "Name of the chainlet", + "title": "Chainlet Name", + "type": "string" + }, + "autoscaling_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/AutoscalingSettingsV1" + }, + { + "type": "null" + } + ], + "description": "Autoscaling settings for the chainlet. If null, it has not finished deploying" + }, + "instance_type": { + "$ref": "#/components/schemas/InstanceTypeV1", + "description": "Instance type for the chainlet" + } + }, + "required": [ + "chainlet_name", + "autoscaling_settings", + "instance_type" + ], + "title": "ChainletEnvironmentSettingsV1", + "type": "object" + }, + "ChainEnvironmentV1": { + "description": "Environment for oracles.", + "properties": { + "name": { + "description": "Name of the environment", + "title": "Name", + "type": "string" + }, + "created_at": { + "description": "Time the environment was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "chain_id": { + "description": "Unique identifier of the chain", + "title": "Chain Id", + "type": "string" + }, + "promotion_settings": { + "$ref": "#/components/schemas/PromotionSettingsV1", + "description": "Promotion settings for the environment" + }, + "chainlet_settings": { + "description": "Environment settings for the chainlets", + "items": { + "$ref": "#/components/schemas/ChainletEnvironmentSettingsV1" + }, + "title": "Chainlet Settings", + "type": "array" + }, + "current_deployment": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChainDeploymentV1" + }, + { + "type": "null" + } + ], + "description": "Current chain deployment of the environment" + }, + "candidate_deployment": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChainDeploymentV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Candidate chain deployment being promoted to the environment, if a promotion is in progress" + } + }, + "required": [ + "name", + "created_at", + "chain_id", + "promotion_settings", + "chainlet_settings", + "current_deployment" + ], + "title": "ChainEnvironmentV1", + "type": "object" + }, + "UpdateChainEnvironmentRequestV1": { + "description": "A request to update a chain environment.", + "properties": { + "promotion_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdatePromotionSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Promotion settings for the environment", + "examples": [ + { + "promotion_cleanup_strategy": null, + "ramp_up_duration_seconds": 600, + "ramp_up_while_promoting": true, + "redeploy_on_promotion": null, + "rolling_deploy": null, + "rolling_deploy_config": null + } + ] + } + }, + "title": "UpdateChainEnvironmentRequestV1", + "type": "object" + }, + "UpdateChainEnvironmentResponseV1": { + "description": "A response to update a chain environment.", + "properties": { + "ok": { + "description": "Whether the update was successful", + "title": "Ok", + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "title": "UpdateChainEnvironmentResponseV1", + "type": "object" + }, + "PromoteToChainEnvironmentRequestV1": { + "description": "A request to promote a deployment to a environment.", + "properties": { + "scale_down_previous_deployment": { + "default": true, + "description": "Whether to scale down the previous deployment after promoting", + "examples": [ + true + ], + "title": "Scale Down Previous Deployment", + "type": "boolean" + }, + "deployment_id": { + "description": "The id of the chain deployment to promote", + "title": "Deployment Id", + "type": "string" + } + }, + "required": [ + "deployment_id" + ], + "title": "PromoteToChainEnvironmentRequestV1", + "type": "object" + }, + "ChainletEnvironmentAutoscalingSettingsUpdateV1": { + "description": "The request to update the autoscaling settings for a chainlet.", + "properties": { + "chainlet_name": { + "description": "Name of the chainlet", + "examples": [ + "HelloWorld" + ], + "title": "Chainlet Name", + "type": "string" + }, + "autoscaling_settings": { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1", + "description": "Autoscaling settings for the chainlet", + "examples": [ + { + "autoscaling_window": 800, + "concurrency_target": 3, + "max_replica": 2, + "min_replica": 1, + "scale_down_delay": 60, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + } + ] + } + }, + "required": [ + "chainlet_name", + "autoscaling_settings" + ], + "title": "ChainletEnvironmentAutoscalingSettingsUpdateV1", + "type": "object" + }, + "UpdateChainletEnvironmentAutoscalingSettingsRequestV1": { + "description": "A request to update the autoscaling settings for a multiple chainlets in an environment.\nIf a chainlet name doesn't exist, an error is returned.", + "properties": { + "updates": { + "description": "Mapping of chainlet name to the desired chainlet autoscaling settings. If the chainlet name doesn't exist, an error is returned.", + "examples": [ + [ + { + "autoscaling_settings": { + "autoscaling_window": 800, + "concurrency_target": 4, + "max_replica": 3, + "min_replica": 2, + "scale_down_delay": 63, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + }, + "chainlet_name": "HelloWorld" + } + ], + [ + { + "autoscaling_settings": { + "autoscaling_window": null, + "concurrency_target": null, + "max_replica": null, + "min_replica": 0, + "scale_down_delay": null, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + }, + "chainlet_name": "HelloWorld" + }, + { + "autoscaling_settings": { + "autoscaling_window": null, + "concurrency_target": null, + "max_replica": null, + "min_replica": 0, + "scale_down_delay": null, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + }, + "chainlet_name": "RandInt" + } + ] + ], + "items": { + "$ref": "#/components/schemas/ChainletEnvironmentAutoscalingSettingsUpdateV1" + }, + "title": "Updates", + "type": "array" + } + }, + "required": [ + "updates" + ], + "title": "UpdateChainletEnvironmentAutoscalingSettingsRequestV1", + "type": "object" + }, + "ChainletEnvironmentInstanceTypeUpdateV1": { + "description": "A request to update the environment settings for a chainlet.", + "properties": { + "chainlet_name": { + "description": "Name of the chainlet", + "examples": [ + "HelloWorld" + ], + "title": "Chainlet Name", + "type": "string" + }, + "instance_type_id": { + "description": "Key of the instance type to use for the chainlet", + "examples": [ + "1x4", + "2x8", + "A10G:2x24x96" + ], + "title": "Instance Type Id", + "type": "string" + } + }, + "required": [ + "chainlet_name", + "instance_type_id" + ], + "title": "ChainletEnvironmentInstanceTypeUpdateV1", + "type": "object" + }, + "UpdateChainletEnvironmentInstanceTypeRequestV1": { + "description": "A request to update the instance types for chainlets in an environment. Multiples\nupdates can be made in one request. The updates will be processed in batch and a new deployment\nwill be created, deployed and promoted into the environment.", + "properties": { + "updates": { + "description": "Mapping of chainlet name to the desired chainlet instance type. If the chainlet name doesn't exist, an error is returned.", + "examples": [ + [ + { + "chainlet_name": "HelloWorld", + "instance_type_id": "1x4" + }, + { + "chainlet_name": "RandInt", + "instance_type_id": "A10G:2x24x96" + } + ] + ], + "items": { + "$ref": "#/components/schemas/ChainletEnvironmentInstanceTypeUpdateV1" + }, + "title": "Updates", + "type": "array" + } + }, + "required": [ + "updates" + ], + "title": "UpdateChainletEnvironmentInstanceTypeRequestV1", + "type": "object" + }, + "UpdateChainletEnvironmentInstanceTypeResponseV1": { + "description": "A response to update the environment settings for a chainlet. If updating the instance type\nresulted in a re-deployment, `requires_redeployment` will be True and the resulting deployment\nwill be returned in the `chain_deployment` field.", + "properties": { + "requires_redeployment": { + "description": "Whether the resource update requires a re-deployment to update the instance type.", + "title": "Requires Redeployment", + "type": "boolean" + }, + "chain_deployment": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChainDeploymentV1" + }, + { + "type": "null" + } + ], + "description": "The chain deployment resulting from the resource update, if any." + }, + "chainlet_environment_settings": { + "description": "The updated chainlet environment settings", + "items": { + "$ref": "#/components/schemas/ChainletEnvironmentSettingsV1" + }, + "title": "Chainlet Environment Settings", + "type": "array" + } + }, + "required": [ + "requires_redeployment", + "chain_deployment", + "chainlet_environment_settings" + ], + "title": "UpdateChainletEnvironmentInstanceTypeResponseV1", + "type": "object" + }, + "UpsertTrainingProjectV1": { + "description": "Fields that can be upserted on a training project.", + "properties": { + "name": { + "description": "Name of the training project.", + "examples": [ + "My Training Project" + ], + "title": "Name", + "type": "string" + } + }, + "required": [ + "name" + ], + "title": "UpsertTrainingProjectV1", + "type": "object" + }, + "UpsertTrainingProjectRequestV1": { + "description": "A request to upsert a training project.", + "properties": { + "training_project": { + "$ref": "#/components/schemas/UpsertTrainingProjectV1", + "description": "The training project to upsert." + } + }, + "required": [ + "training_project" + ], + "title": "UpsertTrainingProjectRequestV1", + "type": "object" + }, + "CheckpointSyncStatus": { + "description": "Lifecycle state for the checkpoint uploader.", + "enum": [ + "SYNCING", + "COMPLETED" + ], + "title": "CheckpointSyncStatus", + "type": "string" + }, + "TrainingJobV1": { + "properties": { + "id": { + "description": "Unique identifier of the training job.", + "title": "Id", + "type": "string" + }, + "created_at": { + "description": "Time the job was created in ISO 8601 format.", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "current_status": { + "description": "Current status of the training job.", + "title": "Current Status", + "type": "string" + }, + "error_message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Error message if the training job failed.", + "title": "Error Message" + }, + "instance_type": { + "$ref": "#/components/schemas/InstanceTypeV1", + "description": "Instance type of the training job." + }, + "updated_at": { + "description": "Time the job was updated in ISO 8601 format.", + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "training_project_id": { + "description": "ID of the training project.", + "title": "Training Project Id", + "type": "string" + }, + "training_project": { + "$ref": "#/components/schemas/TrainingProjectSummaryV1", + "description": "Summary of the training project." + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the training job.", + "examples": [ + "gpt-oss-job" + ], + "title": "Name" + }, + "checkpoint_sync_status": { + "anyOf": [ + { + "$ref": "#/components/schemas/CheckpointSyncStatus" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Checkpoint sync status of the training job." + }, + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/UserV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The user who created the training job." + } + }, + "required": [ + "id", + "created_at", + "current_status", + "instance_type", + "updated_at", + "training_project_id", + "training_project" + ], + "title": "TrainingJobV1", + "type": "object" + }, + "TrainingProjectSummaryV1": { + "description": "A summary of a training project.", + "properties": { + "id": { + "description": "Unique identifier of the training project.", + "title": "Id", + "type": "string" + }, + "name": { + "description": "Name of the training project.", + "title": "Name", + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "title": "TrainingProjectSummaryV1", + "type": "object" + }, + "TrainingProjectV1": { + "properties": { + "id": { + "description": "Unique identifier of the training project", + "title": "Id", + "type": "string" + }, + "name": { + "description": "Name of the training project.", + "title": "Name", + "type": "string" + }, + "created_at": { + "description": "Time the training project was created in ISO 8601 format.", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "updated_at": { + "description": "Time the training project was updated in ISO 8601 format.", + "format": "date-time", + "title": "Updated At", + "type": "string" + }, + "team_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the team associated with the training project.", + "title": "Team Name" + }, + "latest_job": { + "anyOf": [ + { + "$ref": "#/components/schemas/TrainingJobV1" + }, + { + "type": "null" + } + ], + "description": "Most recently created training job for the training project." + } + }, + "required": [ + "id", + "name", + "created_at", + "updated_at", + "latest_job" + ], + "title": "TrainingProjectV1", + "type": "object" + }, + "UserV1": { + "description": "A user.", + "properties": { + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Email of the user.", + "title": "Email" + } + }, + "title": "UserV1", + "type": "object" + }, + "UpsertTrainingProjectResponseV1": { + "description": "A response to upserting a training project.", + "properties": { + "training_project": { + "$ref": "#/components/schemas/TrainingProjectV1", + "description": "The upserted training project." + } + }, + "required": [ + "training_project" + ], + "title": "UpsertTrainingProjectResponseV1", + "type": "object" + }, + "ListTrainingProjectsResponseV1": { + "description": "A response to list training projects.", + "properties": { + "training_projects": { + "description": "List of training projects.", + "items": { + "$ref": "#/components/schemas/TrainingProjectV1" + }, + "title": "Training Projects", + "type": "array" + } + }, + "required": [ + "training_projects" + ], + "title": "ListTrainingProjectsResponseV1", + "type": "object" + }, + "ListTrainingJobsResponseV1": { + "description": "A response to list training jobs.", + "properties": { + "training_project": { + "$ref": "#/components/schemas/TrainingProjectV1", + "description": "The training project." + }, + "training_jobs": { + "description": "List of training jobs.", + "items": { + "$ref": "#/components/schemas/TrainingJobV1" + }, + "title": "Training Jobs", + "type": "array" + } + }, + "required": [ + "training_project", + "training_jobs" + ], + "title": "ListTrainingJobsResponseV1", + "type": "object" + }, + "AwsIamDockerAuthV1": { + "description": "AWS details for the registry.", + "properties": { + "access_key_secret_ref": { + "$ref": "#/components/schemas/SecretReferenceV1", + "description": "Name of the access key secret" + }, + "secret_access_key_secret_ref": { + "$ref": "#/components/schemas/SecretReferenceV1", + "description": "Name of the secret key secret" + } + }, + "required": [ + "access_key_secret_ref", + "secret_access_key_secret_ref" + ], + "title": "AwsIamDockerAuthV1", + "type": "object" + }, + "AwsOidcDockerAuthV1": { + "description": "AWS OIDC details for the registry.", + "properties": { + "role_arn": { + "description": "AWS IAM role ARN for OIDC authentication", + "title": "Role Arn", + "type": "string" + }, + "region": { + "description": "AWS region for OIDC authentication", + "title": "Region", + "type": "string" + } + }, + "required": [ + "role_arn", + "region" + ], + "title": "AwsOidcDockerAuthV1", + "type": "object" + }, + "BasetenLatestCheckpointConfig": { + "properties": { + "project_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the project to load the checkpoint from", + "title": "Project Name" + }, + "job_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "ID of the job to load the checkpoint from", + "title": "Job Id" + }, + "typ": { + "const": "baseten_latest_checkpoint", + "default": "baseten_latest_checkpoint", + "title": "Typ", + "type": "string" + } + }, + "title": "BasetenLatestCheckpointConfig", + "type": "object" + }, + "BasetenNamedCheckpointConfig": { + "properties": { + "checkpoint_name": { + "description": "Name of the checkpoint to load from", + "title": "Checkpoint Name", + "type": "string" + }, + "project_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the project to load the checkpoint from", + "title": "Project Name" + }, + "job_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "ID of the job to load the checkpoint from", + "title": "Job Id" + }, + "typ": { + "const": "baseten_named_checkpoint", + "default": "baseten_named_checkpoint", + "title": "Typ", + "type": "string" + } + }, + "required": [ + "checkpoint_name" + ], + "title": "BasetenNamedCheckpointConfig", + "type": "object" + }, + "CreateJobWeightConfigV1": { + "description": "Weight source configuration for MDN (Model Distribution Network).\n\nEnables training jobs to mount external model weights from HuggingFace, S3, GCS, or R2\nvia MDN's caching and CSI mounting infrastructure. Weights are mirrored once and\ndeduplicated across training jobs.", + "properties": { + "source": { + "description": "Weight source URI. Supported formats: hf://, s3://, gs://, r2://", + "examples": [ + "hf://meta-llama/Llama-3-8B@main", + "s3://my-bucket/models/llama", + "gs://my-bucket/models/llama", + "r2://account_id.bucket/models/llama" + ], + "title": "Source", + "type": "string" + }, + "mount_location": { + "description": "Path where weights will be mounted in the container", + "examples": [ + "/app/models/base", + "/models/llama" + ], + "title": "Mount Location", + "type": "string" + }, + "allow_patterns": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "File patterns to include (Unix-style shell patterns)", + "examples": [ + [ + "*.safetensors", + "config.json" + ] + ], + "title": "Allow Patterns" + }, + "ignore_patterns": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "File patterns to exclude (Unix-style shell patterns)", + "examples": [ + [ + "*.bin", + "*.h5" + ] + ], + "title": "Ignore Patterns" + }, + "auth_secret_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the workspace secret for authentication (e.g., HuggingFace token)", + "examples": [ + "hf_token", + "aws_credentials" + ], + "title": "Auth Secret Name" + }, + "auth": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Authentication configuration for the weight source.", + "examples": [ + { + "auth_method": "CUSTOM_SECRET", + "auth_secret_name": "hf_token" + } + ], + "title": "Auth" + } + }, + "required": [ + "source", + "mount_location" + ], + "title": "CreateJobWeightConfigV1", + "type": "object" + }, + "CreateTrainingJobAcceleratorV1": { + "properties": { + "accelerator": { + "description": "GPU type for the training job.", + "examples": [ + "H100" + ], + "title": "Accelerator", + "type": "string" + }, + "count": { + "description": "GPUs needed for the training job.", + "examples": [ + 2 + ], + "title": "Count", + "type": "integer" + } + }, + "required": [ + "accelerator", + "count" + ], + "title": "CreateTrainingJobAcceleratorV1", + "type": "object" + }, + "CreateTrainingJobCacheConfig": { + "properties": { + "enable_legacy_hf_mount": { + "default": false, + "description": "Whether to enable the legacy Hugging Face cache.", + "examples": [ + true + ], + "title": "Enable Legacy Hf Mount", + "type": "boolean" + }, + "enabled": { + "default": false, + "description": "Whether to enable the read-write cache.", + "examples": [ + true + ], + "title": "Enabled", + "type": "boolean" + }, + "require_cache_affinity": { + "default": true, + "description": "Whether to require region affinity for the read-write cache. If False, the resulting job is not guaranteed to be deployed alongside the previous cache.", + "examples": [ + true, + false + ], + "title": "Require Cache Affinity", + "type": "boolean" + }, + "mount_base_path": { + "default": "/root/.cache", + "description": "Mount base path for the cache directory. The project cache and team cache will be mounted under this path.", + "examples": [ + "/workspace/.cache", + "/root/.cache" + ], + "title": "Mount Base Path", + "type": "string" + } + }, + "title": "CreateTrainingJobCacheConfig", + "type": "object" + }, + "CreateTrainingJobCheckpointingConfig": { + "properties": { + "enabled": { + "default": false, + "description": "Whether checkpointing is enabled.", + "examples": [ + true + ], + "title": "Enabled", + "type": "boolean" + }, + "checkpoint_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "path where checkpoints will be saved.", + "examples": [ + "/mnt/ckpts" + ], + "title": "Checkpoint Path" + }, + "volume_size_gib": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Size of the volume in gibibytes. If not provided, the default size will be used", + "examples": [ + 10 + ], + "title": "Volume Size Gib" + } + }, + "title": "CreateTrainingJobCheckpointingConfig", + "type": "object" + }, + "CreateTrainingJobComputeV1": { + "description": "Configuration to specify the compute for a training job.", + "properties": { + "node_count": { + "default": 1, + "description": "Number of nodes for the training job.", + "examples": [ + 1 + ], + "title": "Node Count", + "type": "integer" + }, + "cpu_count": { + "default": 1, + "description": "Number of cpus for the training job.", + "examples": [ + 1 + ], + "title": "Cpu Count", + "type": "integer" + }, + "memory": { + "default": "2Gi", + "description": "Memory for the training job.", + "examples": [ + "2Gi" + ], + "title": "Memory", + "type": "string" + }, + "accelerator": { + "anyOf": [ + { + "$ref": "#/components/schemas/CreateTrainingJobAcceleratorV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "GPU specification for the training job", + "examples": [ + { + "accelerator": "H100", + "count": 2 + } + ] + } + }, + "title": "CreateTrainingJobComputeV1", + "type": "object" + }, + "CreateTrainingJobImageV1": { + "description": "Configuration to create a training job image.", + "properties": { + "base_image": { + "description": "Base image for the training job.", + "examples": [ + "hello-world" + ], + "title": "Base Image", + "type": "string" + }, + "docker_auth": { + "anyOf": [ + { + "$ref": "#/components/schemas/DockerAuthV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Docker authentication credentials" + } + }, + "required": [ + "base_image" + ], + "title": "CreateTrainingJobImageV1", + "type": "object" + }, + "CreateTrainingJobRuntimeV1": { + "description": "Configuration to specify the runtime environment for a training job.", + "properties": { + "start_commands": { + "description": "Commands to execute when starting the runtime.", + "examples": [ + [ + "python main.py" + ] + ], + "items": { + "type": "string" + }, + "title": "Start Commands", + "type": "array" + }, + "environment_variables": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/SecretReferenceV1" + } + ] + }, + "description": "Environment variables to set in the runtime.", + "examples": [ + { + "API_KEY": "your_api_key_here", + "PATH": "/usr/bin" + } + ], + "title": "Environment Variables", + "type": "object" + }, + "artifacts": { + "description": "Runtime artifacts for the training job.", + "items": { + "$ref": "#/components/schemas/CreateTrainingJobS3Artifact" + }, + "title": "Artifacts", + "type": "array" + }, + "enable_cache": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Deprecated. Use cache_config instead.", + "examples": [ + true + ], + "title": "Enable Cache" + }, + "cache_config": { + "anyOf": [ + { + "$ref": "#/components/schemas/CreateTrainingJobCacheConfig" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Configuration for the read-write cache.", + "examples": [ + { + "enable_legacy_hf_mount": true, + "enabled": true, + "mount_base_path": "/root/.cache", + "require_cache_affinity": true + } + ] + }, + "checkpointing_config": { + "$ref": "#/components/schemas/CreateTrainingJobCheckpointingConfig", + "description": "Configuration for checkpointing.", + "examples": [ + { + "checkpoint_path": "/mnt/ckpts", + "enabled": true, + "volume_size_gib": null + } + ] + }, + "load_checkpoint_config": { + "anyOf": [ + { + "$ref": "#/components/schemas/LoadCheckpointConfig" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Configuration for loading checkpoints" + } + }, + "title": "CreateTrainingJobRuntimeV1", + "type": "object" + }, + "CreateTrainingJobS3Artifact": { + "properties": { + "s3_bucket": { + "description": "S3 bucket for the uploaded runtime artifact.", + "examples": [ + "my-s3-bucket" + ], + "title": "S3 Bucket", + "type": "string" + }, + "s3_key": { + "description": "S3 key for the uploaded runtime artifact.", + "examples": [ + "my-s3-key" + ], + "title": "S3 Key", + "type": "string" + } + }, + "required": [ + "s3_bucket", + "s3_key" + ], + "title": "CreateTrainingJobS3Artifact", + "type": "object" + }, + "CreateTrainingJobV1": { + "description": "Configuration for a training job.", + "properties": { + "image": { + "$ref": "#/components/schemas/CreateTrainingJobImageV1", + "examples": [ + { + "base_image": "hello-world", + "docker_auth": null + } + ] + }, + "compute": { + "$ref": "#/components/schemas/CreateTrainingJobComputeV1", + "examples": [ + { + "accelerator": { + "accelerator": "H100", + "count": 2 + }, + "cpu_count": 1, + "memory": "2Gi", + "node_count": 1 + } + ] + }, + "runtime": { + "$ref": "#/components/schemas/CreateTrainingJobRuntimeV1", + "description": "Configuration for the runtime environment of the training job.", + "examples": [ + { + "artifacts": [], + "cache_config": null, + "checkpointing_config": { + "checkpoint_path": null, + "enabled": false, + "volume_size_gib": null + }, + "enable_cache": null, + "environment_variables": { + "API_KEY": "your_api_key_here", + "PATH": "/usr/bin" + }, + "load_checkpoint_config": null, + "start_commands": [ + "python main.py" + ] + } + ] + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the training job.", + "examples": [ + "gpt-oss-job" + ], + "title": "Name" + }, + "truss_user_env": { + "anyOf": [ + { + "$ref": "#/components/schemas/TrussUserEnv" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Truss user environment information" + }, + "interactive_session": { + "anyOf": [ + { + "$ref": "#/components/schemas/InteractiveSessionConfigV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Configuration for interactive debugging sessions." + }, + "weights": { + "description": "MDN weight sources to mount in the training container. Weights are mirrored and cached for fast startup.", + "examples": [ + [ + { + "allow_patterns": null, + "auth": null, + "auth_secret_name": null, + "ignore_patterns": null, + "mount_location": "/app/models/base", + "source": "hf://meta-llama/Llama-3-8B@main" + } + ] + ], + "items": { + "$ref": "#/components/schemas/CreateJobWeightConfigV1" + }, + "title": "Weights", + "type": "array" + } + }, + "required": [ + "image" + ], + "title": "CreateTrainingJobV1", + "type": "object" + }, + "DockerAuthType": { + "enum": [ + "GCP_SERVICE_ACCOUNT_JSON", + "AWS_IAM", + "AWS_OIDC", + "GCP_OIDC", + "REGISTRY_SECRET" + ], + "title": "DockerAuthType", + "type": "string" + }, + "DockerAuthV1": { + "description": "Docker authentication credentials.", + "properties": { + "registry": { + "description": "Registry to authenticate with", + "title": "Registry", + "type": "string" + }, + "auth_method": { + "$ref": "#/components/schemas/DockerAuthType", + "description": "Method to authenticate with the registry", + "examples": [ + "GCP_SERVICE_ACCOUNT_JSON", + "AWS_IAM", + "AWS_OIDC", + "GCP_OIDC", + "REGISTRY_SECRET" + ] + }, + "gcp_service_account_json_docker_auth": { + "anyOf": [ + { + "$ref": "#/components/schemas/GcpServiceAccountJsonDockerAuthV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "GCP service account details for the registry" + }, + "aws_iam_docker_auth": { + "anyOf": [ + { + "$ref": "#/components/schemas/AwsIamDockerAuthV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "AWS details for the registry" + }, + "aws_oidc_docker_auth": { + "anyOf": [ + { + "$ref": "#/components/schemas/AwsOidcDockerAuthV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "AWS OIDC details for the registry" + }, + "gcp_oidc_docker_auth": { + "anyOf": [ + { + "$ref": "#/components/schemas/GcpOidcDockerAuthV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "GCP OIDC details for the registry" + }, + "registry_secret_docker_auth": { + "anyOf": [ + { + "$ref": "#/components/schemas/RegistrySecretDockerAuthV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Required when auth_method is REGISTRY_SECRET. Supports any Docker registry (Docker Hub, GHCR, NGC, etc.) via username:password credentials stored as a Baseten secret." + } + }, + "required": [ + "registry", + "auth_method" + ], + "title": "DockerAuthV1", + "type": "object" + }, + "GcpOidcDockerAuthV1": { + "description": "GCP OIDC details for the registry.", + "properties": { + "service_account": { + "description": "GCP service account name for OIDC authentication", + "title": "Service Account", + "type": "string" + }, + "workload_identity_provider": { + "description": "GCP workload identity provider for OIDC authentication", + "title": "Workload Identity Provider", + "type": "string" + } + }, + "required": [ + "service_account", + "workload_identity_provider" + ], + "title": "GcpOidcDockerAuthV1", + "type": "object" + }, + "GcpServiceAccountJsonDockerAuthV1": { + "description": "GCP details for the registry.", + "properties": { + "service_account_json_secret_ref": { + "$ref": "#/components/schemas/SecretReferenceV1", + "description": "Name of the service account secret" + } + }, + "required": [ + "service_account_json_secret_ref" + ], + "title": "GcpServiceAccountJsonDockerAuthV1", + "type": "object" + }, + "GitInfo": { + "properties": { + "latest_commit_sha": { + "title": "Latest Commit Sha", + "type": "string" + }, + "latest_tag": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Latest Tag" + }, + "commits_since_tag": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Commits Since Tag" + }, + "has_uncommitted_changes": { + "title": "Has Uncommitted Changes", + "type": "boolean" + } + }, + "required": [ + "latest_commit_sha", + "latest_tag", + "commits_since_tag", + "has_uncommitted_changes" + ], + "title": "GitInfo", + "type": "object" + }, + "InteractiveSessionConfigV1": { + "description": "Configuration for interactive debugging sessions on training jobs.", + "properties": { + "trigger": { + "$ref": "#/components/schemas/V1InteractiveSessionTrigger", + "default": "on_demand", + "description": "When to create the interactive session. 'on_startup' creates on job start, 'on_failure' creates on job failure, 'on_demand' bypasses automatic session creation." + }, + "timeout_minutes": { + "default": 480, + "description": "Number of minutes before the interactive session times out.", + "examples": [ + 480, + 1440, + 10080 + ], + "title": "Timeout Minutes", + "type": "integer" + }, + "session_provider": { + "$ref": "#/components/schemas/V1InteractiveSessionProvider", + "default": "vs_code", + "description": "The IDE client for the interactive session." + }, + "auth_provider": { + "$ref": "#/components/schemas/V1InteractiveSessionAuthProvider", + "default": "github", + "description": "The authentication provider for the interactive session." + } + }, + "title": "InteractiveSessionConfigV1", + "type": "object" + }, + "LoadCheckpointConfig": { + "properties": { + "enabled": { + "default": false, + "description": "Whether checkpoint loading is enabled", + "title": "Enabled", + "type": "boolean" + }, + "download_folder": { + "default": "/tmp/loaded_checkpoints", + "description": "Folder where checkpoints will be downloaded", + "title": "Download Folder", + "type": "string" + }, + "checkpoints": { + "description": "List of checkpoint configurations", + "items": { + "discriminator": { + "mapping": { + "baseten_latest_checkpoint": "#/components/schemas/BasetenLatestCheckpointConfig", + "baseten_named_checkpoint": "#/components/schemas/BasetenNamedCheckpointConfig" + }, + "propertyName": "typ" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/BasetenLatestCheckpointConfig" + }, + { + "$ref": "#/components/schemas/BasetenNamedCheckpointConfig" + } + ] + }, + "title": "Checkpoints", + "type": "array" + } + }, + "title": "LoadCheckpointConfig", + "type": "object" + }, + "RegistrySecretDockerAuthV1": { + "description": "Authentication via a Baseten secret for any Docker registry (Docker Hub, GHCR, NGC, etc.).\nThe referenced secret must contain credentials in the format 'username:password'.\nFor Docker Hub, set registry to 'https://index.docker.io/v1/'. For GHCR, use 'ghcr.io'.", + "properties": { + "secret_ref": { + "$ref": "#/components/schemas/SecretReferenceV1", + "description": "Reference to a Baseten secret containing credentials in the format 'username:password'" + } + }, + "required": [ + "secret_ref" + ], + "title": "RegistrySecretDockerAuthV1", + "type": "object" + }, + "SecretReferenceV1": { + "properties": { + "name": { + "description": "Name of the secret to reference.", + "examples": [ + "hf_token" + ], + "title": "Name", + "type": "string" + } + }, + "required": [ + "name" + ], + "title": "SecretReferenceV1", + "type": "object" + }, + "TrussUserEnv": { + "description": "This data models is used to flexibly store info alongside oracle versions.\n\nThere is a corresponding data model in the truss client.\nIn contrast, here all fields are optional for backwards compatibility with old\nclients.", + "properties": { + "truss_client_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Truss Client Version" + }, + "python_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Python Version" + }, + "pydantic_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Pydantic Version" + }, + "mypy_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Mypy Version" + }, + "is_library_deployment": { + "default": false, + "title": "Is Library Deployment", + "type": "boolean" + }, + "is_frontend_deployment": { + "default": false, + "title": "Is Frontend Deployment", + "type": "boolean" + }, + "git_info": { + "anyOf": [ + { + "$ref": "#/components/schemas/GitInfo" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "TrussUserEnv", + "type": "object" + }, + "V1InteractiveSessionAuthProvider": { + "enum": [ + "github", + "microsoft" + ], + "title": "V1InteractiveSessionAuthProvider", + "type": "string" + }, + "V1InteractiveSessionProvider": { + "enum": [ + "vs_code", + "cursor" + ], + "title": "V1InteractiveSessionProvider", + "type": "string" + }, + "V1InteractiveSessionTrigger": { + "enum": [ + "on_startup", + "on_failure", + "on_demand" + ], + "title": "V1InteractiveSessionTrigger", + "type": "string" + }, + "CreateTrainingJobRequestV1": { + "description": "A request to create a training job.", + "properties": { + "training_job": { + "$ref": "#/components/schemas/CreateTrainingJobV1", + "description": "The training job to create." + } + }, + "required": [ + "training_job" + ], + "title": "CreateTrainingJobRequestV1", + "type": "object" + }, + "CreateTrainingJobResponseV1": { + "description": "A response to creating a training job.", + "properties": { + "training_job": { + "$ref": "#/components/schemas/TrainingJobV1", + "description": "The created training job." + } + }, + "required": [ + "training_job" + ], + "title": "CreateTrainingJobResponseV1", + "type": "object" + }, + "TrainingJobTombstoneV1": { + "description": "A training job tombstone.", + "properties": { + "id": { + "description": "Unique identifier of the training job", + "title": "Id", + "type": "string" + }, + "deleted": { + "description": "Whether the training job was deleted", + "title": "Deleted", + "type": "boolean" + }, + "training_project_id": { + "description": "Unique identifier of the training project", + "title": "Training Project Id", + "type": "string" + } + }, + "required": [ + "id", + "deleted", + "training_project_id" + ], + "title": "TrainingJobTombstoneV1", + "type": "object" + }, + "GetTrainingJobResponseV1": { + "description": "A response to fetch a training job.", + "properties": { + "training_project": { + "$ref": "#/components/schemas/TrainingProjectV1", + "description": "The training project." + }, + "training_job": { + "$ref": "#/components/schemas/TrainingJobV1", + "description": "The fetched training job." + } + }, + "required": [ + "training_project", + "training_job" + ], + "title": "GetTrainingJobResponseV1", + "type": "object" + }, + "DownloadTrainingJobResponseV1": { + "description": "A response that includes the artifacts for a training job", + "properties": { + "artifact_presigned_urls": { + "description": "Presigned URL's for the artifacts", + "items": { + "type": "string" + }, + "title": "Artifact Presigned Urls", + "type": "array" + } + }, + "required": [ + "artifact_presigned_urls" + ], + "title": "DownloadTrainingJobResponseV1", + "type": "object" + }, + "RecreateTrainingJobResponseV1": { + "description": "A response that sends the new training job", + "properties": { + "training_job": { + "$ref": "#/components/schemas/TrainingJobV1", + "description": "The created training job." + } + }, + "required": [ + "training_job" + ], + "title": "RecreateTrainingJobResponseV1", + "type": "object" + }, + "GetTrainingJobLogsRequestV1": { + "description": "A request to fetch training logs.", + "properties": { + "start_epoch_millis": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Epoch millis timestamp to start fetching logs", + "title": "Start Epoch Millis" + }, + "end_epoch_millis": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Epoch millis timestamp to end fetching logs", + "title": "End Epoch Millis" + }, + "direction": { + "anyOf": [ + { + "$ref": "#/components/schemas/SortOrderV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Sort order for logs" + }, + "limit": { + "anyOf": [ + { + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 500, + "description": "Limit of logs to fetch in a single request", + "title": "Limit" + } + }, + "title": "GetTrainingJobLogsRequestV1", + "type": "object" + }, + "GetTrainingJobMetricsRequestV1": { + "description": "A request to fetch metrics. Allows the user to request metrics over a period of time.", + "properties": { + "end_epoch_millis": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Epoch millis timestamp to end fetching metrics", + "title": "End Epoch Millis" + }, + "start_epoch_millis": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Epoch millis timestamp to start fetching metrics.", + "title": "Start Epoch Millis" + } + }, + "title": "GetTrainingJobMetricsRequestV1", + "type": "object" + }, + "StorageMetricsV1": { + "description": "A metric for a training job.", + "properties": { + "usage_bytes": { + "description": "The number of bytes used on the storage entity.", + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "title": "Usage Bytes", + "type": "array" + }, + "utilization": { + "description": "The utilization of the storage entity as a decimal percentage.", + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "title": "Utilization", + "type": "array" + } + }, + "required": [ + "usage_bytes", + "utilization" + ], + "title": "StorageMetricsV1", + "type": "object" + }, + "TrainingJobMetricV1": { + "description": "A metric for a training job.", + "properties": { + "value": { + "description": "The value of the metric.", + "title": "Value", + "type": "number" + }, + "timestamp": { + "description": "The timestamp of the metric in ISO 8601 format.", + "format": "date-time", + "title": "Timestamp", + "type": "string" + } + }, + "required": [ + "value", + "timestamp" + ], + "title": "TrainingJobMetricV1", + "type": "object" + }, + "TrainingJobMetricsV1": { + "properties": { + "gpu_memory_usage_bytes": { + "additionalProperties": { + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "type": "array" + }, + "description": "A map of GPU rank to memory usage for the training job. For multinode jobs, this is the memory usage of the leader unless specified otherwise.", + "title": "Gpu Memory Usage Bytes", + "type": "object" + }, + "gpu_utilization": { + "additionalProperties": { + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "type": "array" + }, + "description": "A map of GPU rank to fractional GPU utilization. For multinode jobs, this is the GPU utilization of the leader unless specified otherwise.", + "title": "Gpu Utilization", + "type": "object" + }, + "cpu_usage": { + "description": "The CPU usage measured in cores. For multinode jobs, this is the CPU usage of the leader unless specified otherwise.", + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "title": "Cpu Usage", + "type": "array" + }, + "cpu_memory_usage_bytes": { + "description": "The CPU memory usage for the training job. For multinode jobs, this is the CPU memory usage of the leader unless specified otherwise.", + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "title": "Cpu Memory Usage Bytes", + "type": "array" + }, + "ephemeral_storage": { + "$ref": "#/components/schemas/StorageMetricsV1", + "description": "The storage usage for the ephemeral storage. For multinode jobs, this is the ephemeral storage usage of the leader unless specified otherwise." + } + }, + "required": [ + "gpu_memory_usage_bytes", + "gpu_utilization", + "cpu_usage", + "cpu_memory_usage_bytes", + "ephemeral_storage" + ], + "title": "TrainingJobMetricsV1", + "type": "object" + }, + "TrainingJobNodeMetricsV1": { + "description": "A set of metrics for a training job node.", + "properties": { + "node_id": { + "description": "The name of the node.", + "title": "Node Id", + "type": "string" + }, + "metrics": { + "$ref": "#/components/schemas/TrainingJobMetricsV1", + "description": "The metrics for the node." + } + }, + "required": [ + "node_id", + "metrics" + ], + "title": "TrainingJobNodeMetricsV1", + "type": "object" + }, + "GetTrainingJobMetricsResponseV1": { + "description": "A response to fetch training job metrics. The outer list for each metric represents that metric across time.", + "properties": { + "gpu_memory_usage_bytes": { + "additionalProperties": { + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "type": "array" + }, + "description": "A map of GPU rank to memory usage for the training job. For multinode jobs, this is the memory usage of the leader unless specified otherwise.", + "title": "Gpu Memory Usage Bytes", + "type": "object" + }, + "gpu_utilization": { + "additionalProperties": { + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "type": "array" + }, + "description": "A map of GPU rank to fractional GPU utilization. For multinode jobs, this is the GPU utilization of the leader unless specified otherwise.", + "title": "Gpu Utilization", + "type": "object" + }, + "cpu_usage": { + "description": "The CPU usage measured in cores. For multinode jobs, this is the CPU usage of the leader unless specified otherwise.", + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "title": "Cpu Usage", + "type": "array" + }, + "cpu_memory_usage_bytes": { + "description": "The CPU memory usage for the training job. For multinode jobs, this is the CPU memory usage of the leader unless specified otherwise.", + "items": { + "$ref": "#/components/schemas/TrainingJobMetricV1" + }, + "title": "Cpu Memory Usage Bytes", + "type": "array" + }, + "ephemeral_storage": { + "$ref": "#/components/schemas/StorageMetricsV1", + "description": "The storage usage for the ephemeral storage. For multinode jobs, this is the ephemeral storage usage of the leader unless specified otherwise." + }, + "training_job": { + "$ref": "#/components/schemas/TrainingJobV1", + "description": "The training job." + }, + "cache": { + "anyOf": [ + { + "$ref": "#/components/schemas/StorageMetricsV1" + }, + { + "type": "null" + } + ], + "description": "The storage usage for the read-write cache." + }, + "per_node_metrics": { + "description": "The metrics for each node in the training job.", + "items": { + "$ref": "#/components/schemas/TrainingJobNodeMetricsV1" + }, + "title": "Per Node Metrics", + "type": "array" + } + }, + "required": [ + "gpu_memory_usage_bytes", + "gpu_utilization", + "cpu_usage", + "cpu_memory_usage_bytes", + "ephemeral_storage", + "training_job", + "cache", + "per_node_metrics" + ], + "title": "GetTrainingJobMetricsResponseV1", + "type": "object" + }, + "StopTrainingJobRequestV1": { + "description": "A request to stop a training job.", + "properties": {}, + "title": "StopTrainingJobRequestV1", + "type": "object" + }, + "StopTrainingJobResponseV1": { + "description": "A response to stopping a training job.", + "properties": { + "training_job": { + "$ref": "#/components/schemas/TrainingJobV1", + "description": "The stopped training job." + } + }, + "required": [ + "training_job" + ], + "title": "StopTrainingJobResponseV1", + "type": "object" + }, + "TrainingJobCheckpointV1": { + "description": "A checkpoint for a training job.", + "properties": { + "training_job_id": { + "description": "The ID of the training job.", + "title": "Training Job Id", + "type": "string" + }, + "checkpoint_id": { + "description": "The ID of the checkpoint.", + "title": "Checkpoint Id", + "type": "string" + }, + "created_at": { + "description": "The timestamp of the checkpoint in ISO 8601 format.", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "checkpoint_type": { + "description": "The type of checkpoint.", + "title": "Checkpoint Type", + "type": "string" + }, + "base_model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The base model of the checkpoint.", + "title": "Base Model" + }, + "lora_adapter_config": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "The adapter config of the checkpoint.", + "title": "Lora Adapter Config" + }, + "size_bytes": { + "description": "The size of the checkpoint in bytes.", + "title": "Size Bytes", + "type": "integer" + }, + "sync_status": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Sync state of the checkpoint: SYNCING or COMPLETE.", + "title": "Sync Status" + } + }, + "required": [ + "training_job_id", + "checkpoint_id", + "created_at", + "checkpoint_type", + "base_model", + "lora_adapter_config", + "size_bytes" + ], + "title": "TrainingJobCheckpointV1", + "type": "object" + }, + "GetTrainingJobCheckpointsResponseV1": { + "description": "A response to fetch checkpoints for a training job.", + "properties": { + "training_job": { + "$ref": "#/components/schemas/TrainingJobV1", + "description": "The training job." + }, + "checkpoints": { + "description": "The checkpoints for the training job.", + "items": { + "$ref": "#/components/schemas/TrainingJobCheckpointV1" + }, + "title": "Checkpoints", + "type": "array" + } + }, + "required": [ + "training_job", + "checkpoints" + ], + "title": "GetTrainingJobCheckpointsResponseV1", + "type": "object" + }, + "CheckpointFile": { + "properties": { + "url": { + "title": "Url", + "type": "string" + }, + "relative_file_name": { + "title": "Relative File Name", + "type": "string" + }, + "node_rank": { + "title": "Node Rank", + "type": "integer" + }, + "size_bytes": { + "title": "Size Bytes", + "type": "integer" + }, + "last_modified": { + "title": "Last Modified", + "type": "string" + } + }, + "required": [ + "url", + "relative_file_name", + "node_rank", + "size_bytes", + "last_modified" + ], + "title": "CheckpointFile", + "type": "object" + }, + "GetTrainingJobCheckpointFilesResponseV1": { + "description": "A response to fetch presigned URLs for checkpoint files of a training job.", + "properties": { + "presigned_urls": { + "description": "List of presigned URLs for checkpoint files.", + "items": { + "$ref": "#/components/schemas/CheckpointFile" + }, + "title": "Presigned Urls", + "type": "array" + }, + "next_page_token": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Token to use for fetching the next page of results. None when there are no more results.", + "title": "Next Page Token" + }, + "total_count": { + "description": "Total number of checkpoint files available.", + "title": "Total Count", + "type": "integer" + } + }, + "required": [ + "presigned_urls", + "total_count" + ], + "title": "GetTrainingJobCheckpointFilesResponseV1", + "type": "object" + }, + "AuthCodeV1": { + "description": "Authentication code for a training job interactive session node.", + "properties": { + "session_id": { + "description": "Unique identifier of the interactive session.", + "title": "Session Id", + "type": "string" + }, + "replica_id": { + "description": "Replica identifier in gXXrY format (e.g., 'g00r0' for group 0, replica 0).", + "title": "Replica Id", + "type": "string" + }, + "auth_code": { + "description": "The device authentication code (e.g., '4F64-C0D9').", + "title": "Auth Code", + "type": "string" + }, + "auth_url": { + "description": "URL where the user should enter the auth code (e.g., 'https://github.com/login/device').", + "title": "Auth Url", + "type": "string" + }, + "generated_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When the auth code was generated, in ISO 8601 format.", + "title": "Generated At" + }, + "tunnel_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The name of the tunnel node.", + "title": "Tunnel Name" + }, + "expires_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When the session expires, in ISO 8601 format.", + "title": "Expires At" + }, + "working_directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The working directory of the session.", + "title": "Working Directory" + } + }, + "required": [ + "session_id", + "replica_id", + "auth_code", + "auth_url" + ], + "title": "AuthCodeV1", + "type": "object" + }, + "GetAuthCodesResponseV1": { + "description": "Response containing auth codes for all nodes of a training job's interactive sessions.", + "properties": { + "auth_codes": { + "description": "List of auth codes for each node that has an active interactive session.", + "items": { + "$ref": "#/components/schemas/AuthCodeV1" + }, + "title": "Auth Codes", + "type": "array" + } + }, + "required": [ + "auth_codes" + ], + "title": "GetAuthCodesResponseV1", + "type": "object" + }, + "PatchInteractiveSessionRequestV1": { + "description": "Request to patch an interactive session.\n\nOnly fields that are provided (non-None) will be applied.", + "properties": { + "timeout_minutes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "For on_startup sessions, minutes to add to the expiration. For on_demand/on_failure sessions, minutes to add to the timeout. Use -1 for infinite timeout (bumps by 10 years).", + "title": "Timeout Minutes" + }, + "trigger": { + "anyOf": [ + { + "$ref": "#/components/schemas/V1InteractiveSessionTrigger" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Update when the interactive session is created. Cannot be changed if the session trigger is 'on_startup'." + } + }, + "title": "PatchInteractiveSessionRequestV1", + "type": "object" + }, + "InteractiveSessionV1": { + "description": "Representation of a training job interactive session.", + "properties": { + "id": { + "description": "Unique identifier of the interactive session.", + "title": "Id", + "type": "string" + }, + "trigger": { + "description": "When the interactive session is created.", + "title": "Trigger", + "type": "string" + }, + "timeout_minutes": { + "description": "Minutes before the session times out.", + "title": "Timeout Minutes", + "type": "integer" + }, + "session_provider": { + "description": "The IDE client for the session.", + "title": "Session Provider", + "type": "string" + }, + "auth_provider": { + "description": "The authentication provider for the session.", + "title": "Auth Provider", + "type": "string" + }, + "pod_name": { + "description": "Pod name / node rank for the session.", + "title": "Pod Name", + "type": "string" + }, + "expires_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When the session expires, in ISO 8601 format.", + "title": "Expires At" + }, + "tunnel_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The tunnel name for the session.", + "title": "Tunnel Name" + }, + "auth_code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The device authentication code.", + "title": "Auth Code" + }, + "auth_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "URL where the user should enter the auth code.", + "title": "Auth Url" + }, + "auth_code_generated_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When the auth code was generated.", + "title": "Auth Code Generated At" + }, + "authenticated_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When the session was authenticated.", + "title": "Authenticated At" + }, + "working_directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The working directory of the session.", + "title": "Working Directory" + } + }, + "required": [ + "id", + "trigger", + "timeout_minutes", + "session_provider", + "auth_provider", + "pod_name" + ], + "title": "InteractiveSessionV1", + "type": "object" + }, + "PatchInteractiveSessionResponseV1": { + "description": "Response after patching an interactive session.", + "properties": { + "interactive_session": { + "$ref": "#/components/schemas/InteractiveSessionV1", + "description": "The updated interactive session." + }, + "message": { + "description": "Human-readable summary of what was updated.", + "title": "Message", + "type": "string" + } + }, + "required": [ + "interactive_session", + "message" + ], + "title": "PatchInteractiveSessionResponseV1", + "type": "object" + }, + "FileSummary": { + "description": "Information about a file in the cache.", + "properties": { + "path": { + "description": "Relative path of the file in the cache", + "title": "Path", + "type": "string" + }, + "size_bytes": { + "description": "Size of the file in bytes", + "title": "Size Bytes", + "type": "integer" + }, + "modified": { + "description": "Last modification time of the file", + "title": "Modified", + "type": "string" + }, + "file_type": { + "description": "Type of the file", + "title": "File Type", + "type": "string" + }, + "permissions": { + "description": "Permissions of the file", + "title": "Permissions", + "type": "string" + } + }, + "required": [ + "path", + "size_bytes", + "modified", + "file_type", + "permissions" + ], + "title": "FileSummary", + "type": "object" + }, + "GetCacheSummaryResponseV1": { + "description": "Response for getting cache summary.", + "properties": { + "timestamp": { + "description": "Timestamp when the cache summary was captured", + "title": "Timestamp", + "type": "string" + }, + "project_id": { + "description": "Project ID associated with the cache", + "title": "Project Id", + "type": "string" + }, + "file_summaries": { + "description": "List of files in the cache", + "items": { + "$ref": "#/components/schemas/FileSummary" + }, + "title": "File Summaries", + "type": "array" + } + }, + "required": [ + "timestamp", + "project_id", + "file_summaries" + ], + "title": "GetCacheSummaryResponseV1", + "type": "object" + }, + "TrainingProjectTombstoneV1": { + "description": "A training project tombstone.", + "properties": { + "id": { + "description": "Unique identifier of the training project", + "title": "Id", + "type": "string" + }, + "deleted": { + "description": "Whether the training project was deleted", + "title": "Deleted", + "type": "boolean" + } + }, + "required": [ + "id", + "deleted" + ], + "title": "TrainingProjectTombstoneV1", + "type": "object" + }, + "GetTrainingProjectResponseV1": { + "description": "A response to getting a training project.", + "properties": { + "training_project": { + "$ref": "#/components/schemas/TrainingProjectV1", + "description": "The training project." + } + }, + "required": [ + "training_project" + ], + "title": "GetTrainingProjectResponseV1", + "type": "object" + }, + "OrderByV1": { + "description": "A request to order training jobs.", + "properties": { + "field": { + "description": "The field to order by.", + "examples": [ + "created_at" + ], + "title": "Field", + "type": "string" + }, + "order": { + "description": "The direction to order by.", + "examples": [ + "asc", + "desc" + ], + "title": "Order", + "type": "string" + } + }, + "required": [ + "field", + "order" + ], + "title": "OrderByV1", + "type": "object" + }, + "SearchTrainingJobsRequestV1": { + "description": "A request to search training jobs.", + "properties": { + "project_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Filter the training jobs by project ID.", + "examples": [ + "n4q95w5" + ], + "title": "Project Id" + }, + "job_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Filter the training jobs by job ID.", + "examples": [ + "p7qr9qv" + ], + "title": "Job Id" + }, + "statuses": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Filter the training jobs by status.", + "examples": [ + [ + "TRAINING_JOB_RUNNING", + "TRAINING_JOB_COMPLETED" + ] + ], + "title": "Statuses" + }, + "order_by": { + "default": [ + { + "field": "created_at", + "order": "desc" + } + ], + "description": "Order the training jobs by a field. Currently supports created_at", + "items": { + "$ref": "#/components/schemas/OrderByV1" + }, + "title": "Order By", + "type": "array" + } + }, + "title": "SearchTrainingJobsRequestV1", + "type": "object" + }, + "SearchTrainingJobsResponseV1": { + "description": "A response to search training jobs.", + "properties": { + "training_jobs": { + "description": "List of training jobs.", + "items": { + "$ref": "#/components/schemas/TrainingJobV1" + }, + "title": "Training Jobs", + "type": "array" + } + }, + "required": [ + "training_jobs" + ], + "title": "SearchTrainingJobsResponseV1", + "type": "object" + }, + "AWSCredentialsV1": { + "description": "AWS credentials", + "properties": { + "aws_access_key_id": { + "description": "The AWS access key ID", + "title": "Aws Access Key Id", + "type": "string" + }, + "aws_secret_access_key": { + "description": "The AWS secret access key", + "title": "Aws Secret Access Key", + "type": "string" + }, + "aws_session_token": { + "description": "The AWS session token", + "title": "Aws Session Token", + "type": "string" + } + }, + "required": [ + "aws_access_key_id", + "aws_secret_access_key", + "aws_session_token" + ], + "title": "AWSCredentialsV1", + "type": "object" + }, + "GetBlobCredentialsResponseV1": { + "description": "Response to create a new set of credentials for blob upload.", + "properties": { + "creds": { + "$ref": "#/components/schemas/AWSCredentialsV1", + "description": "The credentials to upload the blob to" + }, + "s3_key": { + "description": "The S3 key to upload the blob to", + "title": "S3 Key", + "type": "string" + }, + "s3_bucket": { + "description": "The S3 bucket to upload the blob to", + "title": "S3 Bucket", + "type": "string" + } + }, + "required": [ + "creds", + "s3_key", + "s3_bucket" + ], + "title": "GetBlobCredentialsResponseV1", + "type": "object" + }, + "APIKeyCategory": { + "description": "Enum representing the category of an API key.", + "enum": [ + "PERSONAL", + "WORKSPACE_MANAGE_ALL", + "WORKSPACE_EXPORT_METRICS", + "WORKSPACE_INVOKE" + ], + "title": "APIKeyCategory", + "type": "string" + }, + "CreateAPIKeyRequestV1": { + "description": "Request to create an API key.", + "properties": { + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional name for the API key", + "examples": [ + "my-api-key" + ], + "title": "Name" + }, + "type": { + "$ref": "#/components/schemas/APIKeyCategory", + "description": "Type of the API key.", + "examples": [ + "PERSONAL", + "WORKSPACE_EXPORT_METRICS", + "WORKSPACE_INVOKE", + "WORKSPACE_MANAGE_ALL" + ] + }, + "model_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "List of model IDs to scope the API key to, only present if type is 'WORKSPACE_EXPORT_METRICS' or 'WORKSPACE_INVOKE'", + "examples": [ + [ + "aaaaaaaa" + ] + ], + "title": "Model Ids" + } + }, + "required": [ + "type" + ], + "title": "CreateAPIKeyRequestV1", + "type": "object" + }, + "APIKeyV1": { + "description": "Represents an API key.", + "properties": { + "api_key": { + "description": "The API key string", + "title": "Api Key", + "type": "string" + } + }, + "required": [ + "api_key" + ], + "title": "APIKeyV1", + "type": "object" + }, + "APIKeyInfoV1": { + "description": "Represents the metadata of an API key.", + "properties": { + "prefix": { + "description": "The prefix of the API key", + "title": "Prefix", + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional name for the API key", + "examples": [ + "my-api-key" + ], + "title": "Name" + }, + "type": { + "$ref": "#/components/schemas/APIKeyCategory", + "description": "Type of the API key.", + "examples": [ + "PERSONAL", + "WORKSPACE_EXPORT_METRICS", + "WORKSPACE_INVOKE", + "WORKSPACE_MANAGE_ALL" + ] + }, + "model_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "List of model IDs to scope the API key to, only present if type is 'WORKSPACE_EXPORT_METRICS' or 'WORKSPACE_INVOKE'", + "examples": [ + [ + "aaaaaaaa" + ] + ], + "title": "Model Ids" + }, + "team_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The name of the team associated with the API key", + "title": "Team Name" + } + }, + "required": [ + "prefix", + "type" + ], + "title": "APIKeyInfoV1", + "type": "object" + }, + "APIKeysV1": { + "description": "A list of API keys.", + "properties": { + "keys": { + "description": "A list of API key information", + "items": { + "$ref": "#/components/schemas/APIKeyInfoV1" + }, + "title": "Keys", + "type": "array" + } + }, + "required": [ + "keys" + ], + "title": "APIKeysV1", + "type": "object" + }, + "APIKeyTombstoneV1": { + "description": "An API key tombstone.", + "properties": { + "prefix": { + "description": "Unique prefix of the API key", + "title": "Prefix", + "type": "string" + } + }, + "required": [ + "prefix" + ], + "title": "APIKeyTombstoneV1", + "type": "object" + }, + "ModelWeightSnapshotV1": { + "description": "A model weight snapshot.", + "properties": { + "model": { + "description": "Unique identifier of the model", + "title": "Model", + "type": "string" + }, + "snapshot_uri": { + "description": "Path to the model weight snapshot", + "title": "Snapshot Uri", + "type": "string" + }, + "received_at": { + "description": "Time of the snapshot", + "format": "date-time", + "title": "Received At", + "type": "string" + } + }, + "required": [ + "model", + "snapshot_uri", + "received_at" + ], + "title": "ModelWeightSnapshotV1", + "type": "object" + }, + "CreateModelWeightSnapshotRequestV1": { + "description": "A request to create a model weight snapshot.", + "properties": { + "model": { + "description": "Unique identifier of the model", + "title": "Model", + "type": "string" + }, + "snapshot_uri": { + "description": "Path to the model weight snapshot", + "title": "Snapshot Uri", + "type": "string" + } + }, + "required": [ + "model", + "snapshot_uri" + ], + "title": "CreateModelWeightSnapshotRequestV1", + "type": "object" + }, + "CreateLLMModelRequestV1": { + "description": "A request to create a BIS LLM model", + "properties": { + "resources": { + "additionalProperties": true, + "description": "Resources allocated to the model", + "title": "Resources", + "type": "object" + }, + "llm_version": { + "default": "1.0", + "description": "Version of the helm chart to use", + "title": "Llm Version", + "type": "string" + }, + "llm_config": { + "additionalProperties": true, + "description": "Configuration specific to the LLM model", + "title": "Llm Config", + "type": "object" + }, + "environment_variables": { + "additionalProperties": true, + "description": "Environment variables for the model", + "title": "Environment Variables", + "type": "object" + }, + "autoscaling_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Autoscaling settings for the model", + "examples": [ + { + "autoscaling_window": 600, + "concurrency_target": null, + "max_replica": 5, + "min_replica": 1, + "scale_down_delay": 300, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + } + ] + }, + "additional_autoscaling_config": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Additional autoscaling configuration (e.g. target in-flight tokens)", + "examples": [ + { + "metrics": [ + { + "name": "in_flight_tokens", + "target": 40000 + } + ] + } + ], + "title": "Additional Autoscaling Config" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "User-defined metadata for the deployment", + "examples": [ + { + "environment": "production", + "git_sha": "abc123" + } + ], + "title": "Metadata" + }, + "name": { + "description": "Name of the model", + "title": "Name", + "type": "string" + } + }, + "required": [ + "resources", + "name" + ], + "title": "CreateLLMModelRequestV1", + "type": "object" + }, + "LLMModelV1": { + "description": "A BIS LLM model", + "properties": { + "id": { + "description": "Unique identifier of the model", + "title": "Id", + "type": "string" + }, + "created_at": { + "description": "Time the model was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "name": { + "description": "Name of the model", + "title": "Name", + "type": "string" + } + }, + "required": [ + "id", + "created_at", + "name" + ], + "title": "LLMModelV1", + "type": "object" + }, + "CreateLLMModelVersionRequestV1": { + "description": "A request to create a BIS LLM model version", + "properties": { + "resources": { + "additionalProperties": true, + "description": "Resources allocated to the model", + "title": "Resources", + "type": "object" + }, + "llm_version": { + "default": "1.0", + "description": "Version of the helm chart to use", + "title": "Llm Version", + "type": "string" + }, + "llm_config": { + "additionalProperties": true, + "description": "Configuration specific to the LLM model", + "title": "Llm Config", + "type": "object" + }, + "environment_variables": { + "additionalProperties": true, + "description": "Environment variables for the model", + "title": "Environment Variables", + "type": "object" + }, + "autoscaling_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpdateAutoscalingSettingsV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Autoscaling settings for the model", + "examples": [ + { + "autoscaling_window": 600, + "concurrency_target": null, + "max_replica": 5, + "min_replica": 1, + "scale_down_delay": 300, + "target_in_flight_tokens": null, + "target_utilization_percentage": null + } + ] + }, + "additional_autoscaling_config": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Additional autoscaling configuration (e.g. target in-flight tokens)", + "examples": [ + { + "metrics": [ + { + "name": "in_flight_tokens", + "target": 40000 + } + ] + } + ], + "title": "Additional Autoscaling Config" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "User-defined metadata for the deployment", + "examples": [ + { + "environment": "production", + "git_sha": "abc123" + } + ], + "title": "Metadata" + } + }, + "required": [ + "resources" + ], + "title": "CreateLLMModelVersionRequestV1", + "type": "object" + }, + "LLMModelVersionV1": { + "description": "A BIS LLM model version", + "properties": { + "id": { + "description": "Unique identifier of the model version", + "title": "Id", + "type": "string" + }, + "created_at": { + "description": "Time the model was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "name": { + "description": "Name of the model version", + "title": "Name", + "type": "string" + } + }, + "required": [ + "id", + "created_at", + "name" + ], + "title": "LLMModelVersionV1", + "type": "object" + }, + "LibraryListingV1": { + "description": "A library listing.", + "properties": { + "display_name": { + "description": "Display name of the library listing", + "title": "Display Name", + "type": "string" + }, + "user_defined_id": { + "description": "User-defined identifier of the library listing", + "title": "User Defined Id", + "type": "string" + }, + "is_public": { + "description": "Whether the listing is publicly accessible", + "title": "Is Public", + "type": "boolean" + }, + "created_at": { + "description": "Time the listing was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "modified_at": { + "description": "Time the listing was last modified", + "format": "date-time", + "title": "Modified At", + "type": "string" + } + }, + "required": [ + "display_name", + "user_defined_id", + "is_public", + "created_at", + "modified_at" + ], + "title": "LibraryListingV1", + "type": "object" + }, + "LibraryListingsV1": { + "description": "A list of library listings.", + "properties": { + "listings": { + "items": { + "$ref": "#/components/schemas/LibraryListingV1" + }, + "title": "Listings", + "type": "array" + } + }, + "required": [ + "listings" + ], + "title": "LibraryListingsV1", + "type": "object" + }, + "CreateLibraryListingRequestV1": { + "description": "Request to create a new library listing.", + "properties": { + "display_name": { + "description": "Display name of the library listing", + "title": "Display Name", + "type": "string" + }, + "user_defined_id": { + "description": "User-defined identifier of the library listing", + "title": "User Defined Id", + "type": "string" + }, + "is_public": { + "default": false, + "description": "Whether the listing is publicly accessible", + "title": "Is Public", + "type": "boolean" + } + }, + "required": [ + "display_name", + "user_defined_id" + ], + "title": "CreateLibraryListingRequestV1", + "type": "object" + }, + "LibraryListingTombstoneV1": { + "description": "A library listing tombstone.", + "properties": { + "user_defined_id": { + "description": "User-defined identifier of the library listing", + "title": "User Defined Id", + "type": "string" + }, + "deleted": { + "description": "Whether the library listing was deleted", + "title": "Deleted", + "type": "boolean" + } + }, + "required": [ + "user_defined_id", + "deleted" + ], + "title": "LibraryListingTombstoneV1", + "type": "object" + }, + "UpdateLibraryListingRequestV1": { + "description": "Request to update a library listing.", + "properties": { + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "New display name for the library listing", + "title": "Display Name" + }, + "is_public": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether the listing is publicly accessible", + "title": "Is Public" + } + }, + "title": "UpdateLibraryListingRequestV1", + "type": "object" + }, + "LibraryListingVersionV1": { + "description": "A library listing version.", + "properties": { + "version_tag": { + "description": "Human-readable tag for this version", + "title": "Version Tag", + "type": "string" + }, + "is_live": { + "description": "Whether this version is the live version", + "title": "Is Live", + "type": "boolean" + }, + "allow_truss_download": { + "description": "Whether users deploying this model can download the Truss", + "title": "Allow Truss Download", + "type": "boolean" + }, + "oracle_version_id": { + "description": "Id of the source model version", + "title": "Oracle Version Id", + "type": "string" + }, + "created_at": { + "description": "Time the version was created in ISO 8601 format", + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "modified_at": { + "description": "Time the version was last modified", + "format": "date-time", + "title": "Modified At", + "type": "string" + } + }, + "required": [ + "version_tag", + "is_live", + "allow_truss_download", + "oracle_version_id", + "created_at", + "modified_at" + ], + "title": "LibraryListingVersionV1", + "type": "object" + }, + "LibraryListingVersionsV1": { + "description": "A list of library listing versions.", + "properties": { + "versions": { + "items": { + "$ref": "#/components/schemas/LibraryListingVersionV1" + }, + "title": "Versions", + "type": "array" + } + }, + "required": [ + "versions" + ], + "title": "LibraryListingVersionsV1", + "type": "object" + }, + "CreateLibraryListingVersionRequestV1": { + "description": "Request to create a new library listing version from an existing model version.", + "properties": { + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Display name of the library listing. Required when creating a new listing.", + "title": "Display Name" + }, + "is_public": { + "default": false, + "description": "Whether the listing is publicly accessible. Only used when creating a new listing.", + "title": "Is Public", + "type": "boolean" + }, + "oracle_version_id": { + "description": "Id of the source model version to publish", + "title": "Oracle Version Id", + "type": "string" + }, + "allow_truss_download": { + "default": false, + "description": "Whether users deploying this model can download the Truss", + "title": "Allow Truss Download", + "type": "boolean" + }, + "version_tag": { + "description": "Human-readable tag for this version", + "title": "Version Tag", + "type": "string" + } + }, + "required": [ + "oracle_version_id", + "version_tag" + ], + "title": "CreateLibraryListingVersionRequestV1", + "type": "object" + }, + "LibraryListingVersionTombstoneV1": { + "description": "A library listing version tombstone.", + "properties": { + "version_tag": { + "description": "Human-readable tag for this version", + "title": "Version Tag", + "type": "string" + }, + "deleted": { + "description": "Whether the library listing version was deleted", + "title": "Deleted", + "type": "boolean" + } + }, + "required": [ + "version_tag", + "deleted" + ], + "title": "LibraryListingVersionTombstoneV1", + "type": "object" + }, + "UpdateLibraryListingVersionRequestV1": { + "description": "Request to update a library listing version.", + "properties": { + "is_live": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether this version should be the live version. Setting to true demotes the current live version.", + "title": "Is Live" + }, + "allow_truss_download": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether users deploying this model can download the Truss", + "title": "Allow Truss Download" + } + }, + "title": "UpdateLibraryListingVersionRequestV1", + "type": "object" + }, + "BillableResourceV1": { + "properties": { + "id": { + "description": "Unique identifier of the resource", + "title": "Id", + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/ResourceKind", + "description": "Resource kind (MODEL_DEPLOYMENT, TRAINING_JOB, or CHAINLET)" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the resource", + "title": "Name" + }, + "is_deleted": { + "description": "Indicates if the resource has been deleted", + "title": "Is Deleted", + "type": "boolean" + }, + "instance_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Instance type used", + "title": "Instance Type" + }, + "environment_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Environment name (e.g., 'production', 'staging')", + "title": "Environment Name" + }, + "chain_metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChainMetadataV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Chain metadata if this is a chainlet deployment" + } + }, + "required": [ + "id", + "kind", + "is_deleted" + ], + "title": "BillableResourceV1", + "type": "object" + }, + "ChainMetadataV1": { + "properties": { + "chain_id": { + "description": "Unique identifier of the chain", + "title": "Chain Id", + "type": "string" + }, + "chain_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the chain", + "title": "Chain Name" + }, + "chain_deployment_id": { + "description": "Unique identifier of the chain deployment", + "title": "Chain Deployment Id", + "type": "string" + } + }, + "required": [ + "chain_id", + "chain_deployment_id" + ], + "title": "ChainMetadataV1", + "type": "object" + }, + "DailyDedicatedUsageV1": { + "properties": { + "date": { + "description": "Date of the usage", + "format": "date", + "title": "Date", + "type": "string" + }, + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost incurred on this date in dollars", + "title": "Subtotal" + }, + "minutes": { + "description": "Minutes used on this date", + "title": "Minutes", + "type": "integer" + }, + "inference_requests": { + "description": "Number of inference requests on this date", + "title": "Inference Requests", + "type": "integer" + } + }, + "required": [ + "date", + "subtotal", + "minutes", + "inference_requests" + ], + "title": "DailyDedicatedUsageV1", + "type": "object" + }, + "DailyModelApiUsageV1": { + "properties": { + "date": { + "description": "Date of the usage", + "format": "date", + "title": "Date", + "type": "string" + }, + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost incurred on this date in dollars", + "title": "Subtotal" + }, + "input_tokens": { + "description": "Number of input tokens on this date", + "title": "Input Tokens", + "type": "integer" + }, + "output_tokens": { + "description": "Number of output tokens on this date", + "title": "Output Tokens", + "type": "integer" + }, + "cached_input_tokens": { + "description": "Number of cached input tokens on this date", + "title": "Cached Input Tokens", + "type": "integer" + } + }, + "required": [ + "date", + "subtotal", + "input_tokens", + "output_tokens", + "cached_input_tokens" + ], + "title": "DailyModelApiUsageV1", + "type": "object" + }, + "DailyTrainingUsageV1": { + "properties": { + "date": { + "description": "Date of the usage", + "format": "date", + "title": "Date", + "type": "string" + }, + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost incurred on this date in dollars", + "title": "Subtotal" + }, + "minutes": { + "description": "Minutes used on this date", + "title": "Minutes", + "type": "integer" + } + }, + "required": [ + "date", + "subtotal", + "minutes" + ], + "title": "DailyTrainingUsageV1", + "type": "object" + }, + "DedicatedItemV1": { + "properties": { + "billable_resource": { + "$ref": "#/components/schemas/BillableResourceV1", + "description": "The model deployment resource" + }, + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost in dollars for this billable resource", + "title": "Subtotal" + }, + "minutes": { + "description": "Total minutes used for this billable resource", + "title": "Minutes", + "type": "integer" + }, + "inference_requests": { + "description": "Total inference requests for this billable resource", + "title": "Inference Requests", + "type": "integer" + }, + "daily": { + "description": "Daily usage breakdown", + "items": { + "$ref": "#/components/schemas/DailyDedicatedUsageV1" + }, + "title": "Daily", + "type": "array" + } + }, + "required": [ + "billable_resource", + "subtotal", + "minutes", + "inference_requests" + ], + "title": "DedicatedItemV1", + "type": "object" + }, + "DedicatedUsageV1": { + "properties": { + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost in dollars after applying credits used", + "title": "Subtotal" + }, + "credits_used": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Credits applied in dollars", + "title": "Credits Used" + }, + "total": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Total cost in dollars", + "title": "Total" + }, + "minutes": { + "description": "Total minutes used", + "title": "Minutes", + "type": "integer" + }, + "breakdown": { + "description": "Per-deployment usage breakdown", + "items": { + "$ref": "#/components/schemas/DedicatedItemV1" + }, + "title": "Breakdown", + "type": "array" + } + }, + "required": [ + "subtotal", + "credits_used", + "total", + "minutes" + ], + "title": "DedicatedUsageV1", + "type": "object" + }, + "ModelApiItemV1": { + "properties": { + "model_name": { + "description": "Model name", + "title": "Model Name", + "type": "string" + }, + "model_family": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model family (e.g., llama, mistral)", + "title": "Model Family" + }, + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost in dollars for this model", + "title": "Subtotal" + }, + "input_tokens": { + "description": "Total input tokens for this model", + "title": "Input Tokens", + "type": "integer" + }, + "output_tokens": { + "description": "Total output tokens for this model", + "title": "Output Tokens", + "type": "integer" + }, + "cached_input_tokens": { + "description": "Total cached input tokens for this model", + "title": "Cached Input Tokens", + "type": "integer" + }, + "daily": { + "description": "Daily usage breakdown", + "items": { + "$ref": "#/components/schemas/DailyModelApiUsageV1" + }, + "title": "Daily", + "type": "array" + } + }, + "required": [ + "model_name", + "subtotal", + "input_tokens", + "output_tokens", + "cached_input_tokens" + ], + "title": "ModelApiItemV1", + "type": "object" + }, + "ModelApisUsageV1": { + "properties": { + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost in dollars after applying credits used", + "title": "Subtotal" + }, + "credits_used": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Credits applied in dollars", + "title": "Credits Used" + }, + "total": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Total cost in dollars", + "title": "Total" + }, + "breakdown": { + "description": "Per-model usage breakdown", + "items": { + "$ref": "#/components/schemas/ModelApiItemV1" + }, + "title": "Breakdown", + "type": "array" + } + }, + "required": [ + "subtotal", + "credits_used", + "total" + ], + "title": "ModelApisUsageV1", + "type": "object" + }, + "ResourceKind": { + "enum": [ + "MODEL_DEPLOYMENT", + "TRAINING_JOB", + "CHAINLET" + ], + "title": "ResourceKind", + "type": "string" + }, + "TrainingItemV1": { + "properties": { + "billable_resource": { + "$ref": "#/components/schemas/BillableResourceV1", + "description": "The training job resource" + }, + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost in dollars for this billable resource", + "title": "Subtotal" + }, + "minutes": { + "description": "Total minutes used for this billable resource", + "title": "Minutes", + "type": "integer" + }, + "daily": { + "description": "Daily usage breakdown", + "items": { + "$ref": "#/components/schemas/DailyTrainingUsageV1" + }, + "title": "Daily", + "type": "array" + } + }, + "required": [ + "billable_resource", + "subtotal", + "minutes" + ], + "title": "TrainingItemV1", + "type": "object" + }, + "TrainingUsageV1": { + "properties": { + "subtotal": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Subtotal cost in dollars after applying credits used", + "title": "Subtotal" + }, + "credits_used": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Credits applied in dollars", + "title": "Credits Used" + }, + "total": { + "anyOf": [ + { + "type": "number" + }, + { + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "type": "string" + } + ], + "description": "Total cost in dollars", + "title": "Total" + }, + "minutes": { + "description": "Total minutes used", + "title": "Minutes", + "type": "integer" + }, + "breakdown": { + "description": "Per-job usage breakdown", + "items": { + "$ref": "#/components/schemas/TrainingItemV1" + }, + "title": "Breakdown", + "type": "array" + } + }, + "required": [ + "subtotal", + "credits_used", + "total", + "minutes" + ], + "title": "TrainingUsageV1", + "type": "object" + }, + "UsageSummaryV1": { + "description": "Billing usage summary for the requested date range.", + "properties": { + "dedicated_usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/DedicatedUsageV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Dedicated model serving usage" + }, + "training_usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/TrainingUsageV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Training usage" + }, + "model_apis_usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelApisUsageV1" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Model APIs usage" + } + }, + "title": "UsageSummaryV1", + "type": "object" + } + }, + "parameters": { + "team_id": { + "schema": { + "type": "string" + }, + "name": "team_id", + "in": "path", + "required": true + }, + "model_id": { + "schema": { + "type": "string" + }, + "name": "model_id", + "in": "path", + "required": true + }, + "deployment_id": { + "schema": { + "type": "string" + }, + "name": "deployment_id", + "in": "path", + "required": true + }, + "replica_id": { + "schema": { + "type": "string" + }, + "name": "replica_id", + "in": "path", + "required": true + }, + "env_name": { + "schema": { + "type": "string" + }, + "name": "env_name", + "in": "path", + "required": true + }, + "chain_id": { + "schema": { + "type": "string" + }, + "name": "chain_id", + "in": "path", + "required": true + }, + "chain_deployment_id": { + "schema": { + "type": "string" + }, + "name": "chain_deployment_id", + "in": "path", + "required": true + }, + "training_project_id": { + "schema": { + "type": "string" + }, + "name": "training_project_id", + "in": "path", + "required": true + }, + "training_job_id": { + "schema": { + "type": "string" + }, + "name": "training_job_id", + "in": "path", + "required": true + }, + "session_id": { + "schema": { + "type": "string" + }, + "name": "session_id", + "in": "path", + "required": true + }, + "api_key_prefix": { + "schema": { + "type": "string" + }, + "name": "api_key_prefix", + "in": "path", + "required": true + }, + "user_defined_listing_id": { + "schema": { + "type": "string" + }, + "name": "user_defined_listing_id", + "in": "path", + "required": true + }, + "version_tag": { + "schema": { + "type": "string" + }, + "name": "version_tag", + "in": "path", + "required": true + } + }, + "securitySchemes": { + "ApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "Authorization", + "description": "You must specify the scheme 'Api-Key' in the Authorization header. For example, `Authorization: Api-Key `" + } + } + } +} \ No newline at end of file diff --git a/tests/client/test_client.py b/tests/client/test_client.py new file mode 100644 index 0000000..3a25613 --- /dev/null +++ b/tests/client/test_client.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +import dataclasses + +import pytest + +from baseten.client import ( + AsyncInferenceClient, + AsyncManagementClient, + InferenceClient, + ManagementClient, + ManagementClientOptions, +) + + +def test_management_default_base_url() -> None: + client = ManagementClient(api_key="test-key") + assert client.options.base_url == ManagementClient.default_base_url() + client.close() + + +def test_management_base_url_override() -> None: + client = ManagementClient( + api_key="test-key", base_url_override="https://custom.example.com" + ) + assert client.options.base_url == "https://custom.example.com" + assert client.options.base_url_override == "https://custom.example.com" + client.close() + + +def test_management_options_frozen() -> None: + client = ManagementClient(api_key="test-key") + with pytest.raises(dataclasses.FrozenInstanceError): + client.options.api_key = "other" # type: ignore[misc] # ty: ignore[invalid-assignment] + client.close() + + +def test_management_close_http_client_default_true() -> None: + client = ManagementClient(api_key="test-key") + assert client.close_http_client_on_close is True + client.close() + + +def test_management_close_http_client_default_false_when_provided() -> None: + import httpx + + http_client = httpx.Client() + client = ManagementClient(api_key="test-key", http_client=http_client) + assert client.close_http_client_on_close is False + client.close() + http_client.close() + + +def test_management_context_manager() -> None: + with ManagementClient(api_key="test-key") as client: + assert client.api is not None + + +def test_management_options_splat() -> None: + opts = ManagementClientOptions( + api_key="test-key", base_url_override="https://custom.example.com" + ) + client = ManagementClient(**dataclasses.asdict(opts)) + assert client.options.api_key == "test-key" + assert client.options.base_url == "https://custom.example.com" + client.close() + + +@pytest.mark.asyncio +async def test_async_management_default_base_url() -> None: + client = AsyncManagementClient(api_key="test-key") + assert client.options.base_url == AsyncManagementClient.default_base_url() + await client.close() + + +@pytest.mark.asyncio +async def test_async_management_context_manager() -> None: + async with AsyncManagementClient(api_key="test-key") as client: + assert client.api is not None + + +def test_inference_default_base_url_model() -> None: + client = InferenceClient(api_key="test-key", model_id="abc123") + assert client.options.base_url == "https://model-abc123.api.baseten.co" + client.close() + + +def test_inference_default_base_url_model_with_env() -> None: + client = InferenceClient(api_key="test-key", model_id="abc", environment="prod-us") + assert client.options.base_url == "https://model-abc-prod-us.api.baseten.co" + client.close() + + +def test_inference_default_base_url_chain() -> None: + client = InferenceClient(api_key="test-key", chain_id="def456") + assert client.options.base_url == "https://chain-def456.api.baseten.co" + client.close() + + +def test_inference_base_url_override() -> None: + client = InferenceClient( + api_key="test-key", base_url_override="https://custom.example.com" + ) + assert client.options.base_url == "https://custom.example.com" + client.close() + + +def test_inference_base_url_override_wins_over_ids() -> None: + client = InferenceClient( + api_key="test-key", + model_id="abc", + base_url_override="https://custom.example.com", + ) + assert client.options.base_url == "https://custom.example.com" + client.close() + + +def test_inference_requires_model_or_chain_without_override() -> None: + with pytest.raises(ValueError, match="exactly one"): + InferenceClient(api_key="test-key") + + +def test_inference_model_and_chain_mutually_exclusive() -> None: + with pytest.raises(ValueError, match="exactly one"): + InferenceClient(api_key="test-key", model_id="abc", chain_id="def") + + +def test_inference_options_frozen() -> None: + client = InferenceClient(api_key="test-key", model_id="abc") + with pytest.raises(dataclasses.FrozenInstanceError): + client.options.api_key = "other" # type: ignore[misc] # ty: ignore[invalid-assignment] + client.close() + + +def test_inference_context_manager() -> None: + with InferenceClient(api_key="test-key", model_id="abc") as client: + assert client.api is not None + + +@pytest.mark.asyncio +async def test_async_inference_context_manager() -> None: + async with AsyncInferenceClient(api_key="test-key", model_id="abc") as client: + assert client.api is not None + + +@pytest.mark.asyncio +async def test_async_inference_default_base_url() -> None: + client = AsyncInferenceClient( + api_key="test-key", chain_id="xyz", environment="staging" + ) + assert client.options.base_url == "https://chain-xyz-staging.api.baseten.co" + await client.close() diff --git a/tests/client/test_inference.py b/tests/client/test_inference.py new file mode 100644 index 0000000..eced040 --- /dev/null +++ b/tests/client/test_inference.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +import json + +import pytest + +import baseten.client.inferenceapi +from baseten.client import AsyncInferenceClient, InferenceClient +from baseten.client.inferenceapi import PredictInput +from tests.conftest import FakeTransport + + +def make_sync_client(fake: FakeTransport) -> InferenceClient: + client = InferenceClient(api_key="test-key", model_id="abc123") + client.http_client._transport = fake.sync_transport # type: ignore[attr-defined] + return client + + +def make_async_client(fake: FakeTransport) -> AsyncInferenceClient: + client = AsyncInferenceClient(api_key="test-key", model_id="abc123") + client.http_client._transport = fake.async_transport # type: ignore[attr-defined] + return client + + +def test_predict_production_sync() -> None: + fake = FakeTransport(200, {"result": 42}) + client = make_sync_client(fake) + + resp = client.api.predict_production(body=PredictInput({"input": "hello"})) + assert resp.root["result"] == 42 + assert fake.capture.method == "POST" + assert fake.capture.path == "/production/predict" + assert fake.capture.headers["authorization"] == "Api-Key test-key" + body = json.loads(fake.capture.body) + assert body == {"input": "hello"} + client.close() + + +@pytest.mark.asyncio +async def test_predict_production() -> None: + fake = FakeTransport(200, {"result": 42}) + client = make_async_client(fake) + + resp = await client.api.predict_production(body=PredictInput({"input": "hello"})) + assert resp.root["result"] == 42 + assert fake.capture.method == "POST" + assert fake.capture.path == "/production/predict" + body = json.loads(fake.capture.body) + assert body == {"input": "hello"} + await client.close() + + +@pytest.mark.asyncio +async def test_async_predict_201() -> None: + fake = FakeTransport(201, {"request_id": "req-123"}) + client = make_async_client(fake) + + resp = await client.api.async_predict_production( + body=baseten.client.inferenceapi.AsyncPredictRequest( + model_input={"prompt": "test"}, + ), + ) + assert resp.request_id == "req-123" + body = json.loads(fake.capture.body) + assert body["model_input"] == {"prompt": "test"} + await client.close() + + +@pytest.mark.asyncio +async def test_typed_error() -> None: + fake = FakeTransport(429, {"error": "rate limited", "error_code": "rate_limited"}) + client = make_async_client(fake) + + with pytest.raises(baseten.client.inferenceapi.ResponseErrorResponse) as exc_info: + await client.api.predict_production(body=PredictInput({})) + assert exc_info.value.status_code == 429 + assert exc_info.value.error_response.error == "rate limited" + await client.close() + + +@pytest.mark.asyncio +async def test_unknown_status_falls_back_to_response_error() -> None: + fake = FakeTransport(418, {"error": "teapot"}) + client = make_async_client(fake) + + with pytest.raises(baseten.client.inferenceapi.ResponseError) as exc_info: + await client.api.predict_production(body=PredictInput({})) + assert exc_info.value.status_code == 418 + assert "teapot" in exc_info.value.body + await client.close() + + +@pytest.mark.asyncio +async def test_wake_no_response() -> None: + fake = FakeTransport(202) + client = make_async_client(fake) + + await client.api.wake_production() + assert fake.capture.method == "POST" + assert fake.capture.path == "/production/wake" + assert fake.capture.body == "" + await client.close() + + +@pytest.mark.asyncio +async def test_wake_error() -> None: + fake = FakeTransport(401, {"error": "unauthorized", "error_code": "unauthorized"}) + client = make_async_client(fake) + + with pytest.raises(baseten.client.inferenceapi.ResponseErrorResponse) as exc_info: + await client.api.wake_production() + assert exc_info.value.status_code == 401 + await client.close() diff --git a/tests/client/test_management.py b/tests/client/test_management.py new file mode 100644 index 0000000..98df35d --- /dev/null +++ b/tests/client/test_management.py @@ -0,0 +1,131 @@ +from __future__ import annotations + +import json + +import pytest + +import baseten.client.managementapi +from baseten.client import AsyncManagementClient, ManagementClient +from tests.conftest import FakeTransport + +MINIMAL_MODEL = { + "id": "model-1", + "name": "my-model", + "created_at": "2024-01-01T00:00:00Z", + "deployments_count": 2, + "production_deployment_id": "dep-1", + "development_deployment_id": "dep-2", + "instance_type_name": "A10G", + "team_name": "my-team", +} + +MINIMAL_SECRET = { + "name": "MY_SECRET", + "created_at": "2024-01-01T00:00:00Z", + "team_name": "my-team", +} + + +def make_sync_client(fake: FakeTransport) -> ManagementClient: + client = ManagementClient(api_key="test-key") + client.http_client._transport = fake.sync_transport # type: ignore[attr-defined] + return client + + +def make_async_client(fake: FakeTransport) -> AsyncManagementClient: + client = AsyncManagementClient(api_key="test-key") + client.http_client._transport = fake.async_transport # type: ignore[attr-defined] + return client + + +def test_get_models_sync() -> None: + fake = FakeTransport(200, {"models": [MINIMAL_MODEL]}) + client = make_sync_client(fake) + + resp = client.api.get_models() + assert resp.models is not None + assert len(resp.models) == 1 + assert resp.models[0].name == "my-model" + assert fake.capture.method == "GET" + assert fake.capture.path == "/v1/models" + assert fake.capture.headers["authorization"] == "Api-Key test-key" + client.close() + + +def test_response_error_sync() -> None: + fake = FakeTransport(500, {"detail": "boom"}) + client = make_sync_client(fake) + + with pytest.raises(baseten.client.managementapi.ResponseError) as exc_info: + client.api.get_models() + assert exc_info.value.status_code == 500 + assert "boom" in exc_info.value.body + client.close() + + +@pytest.mark.asyncio +async def test_get_models() -> None: + fake = FakeTransport(200, {"models": [MINIMAL_MODEL]}) + client = make_async_client(fake) + + resp = await client.api.get_models() + assert resp.models is not None + assert len(resp.models) == 1 + assert resp.models[0].name == "my-model" + assert fake.capture.method == "GET" + assert fake.capture.path == "/v1/models" + assert fake.capture.headers["authorization"] == "Api-Key test-key" + await client.close() + + +@pytest.mark.asyncio +async def test_path_params_escaped() -> None: + fake = FakeTransport(200, MINIMAL_MODEL) + client = make_async_client(fake) + + await client.api.get_models_model_id(model_id="abc/def") + assert fake.capture.path == "/v1/models/abc%2Fdef" + await client.close() + + +@pytest.mark.asyncio +async def test_post_with_body() -> None: + fake = FakeTransport(200, MINIMAL_SECRET) + client = make_async_client(fake) + + resp = await client.api.post_secrets( + body=baseten.client.managementapi.UpsertSecretRequest( + name="MY_SECRET", + value="s3cret", + ), + ) + + assert resp.name == "MY_SECRET" + assert fake.capture.method == "POST" + assert fake.capture.headers["content-type"] == "application/json" + body = json.loads(fake.capture.body) + assert body["name"] == "MY_SECRET" + assert body["value"] == "s3cret" + await client.close() + + +@pytest.mark.asyncio +async def test_response_error() -> None: + fake = FakeTransport(500, {"detail": "boom"}) + client = make_async_client(fake) + + with pytest.raises(baseten.client.managementapi.ResponseError) as exc_info: + await client.api.get_models() + assert exc_info.value.status_code == 500 + assert "boom" in exc_info.value.body + await client.close() + + +@pytest.mark.asyncio +async def test_unexpected_content_type() -> None: + fake = FakeTransport(200, None) + client = make_async_client(fake) + + with pytest.raises(ValueError, match="content type"): + await client.api.get_models() + await client.close() diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..133a5e7 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +import json +from dataclasses import dataclass, field +from typing import Any + +import httpx + + +@dataclass +class CapturedRequest: + method: str = "" + path: str = "" + headers: dict[str, str] = field(default_factory=dict) + body: str = "" + + +class FakeTransport: + """Mock HTTP transport that captures requests and returns fixed responses.""" + + def __init__(self, status_code: int = 200, response: Any = None) -> None: + self.status_code = status_code + self.response = response + self.capture = CapturedRequest() + + def _build_response(self, request: httpx.Request) -> httpx.Response: + self.capture = CapturedRequest( + method=request.method, + path=str(request.url.raw_path, "ascii"), + headers={k: v for k, v in request.headers.items()}, + body=request.content.decode() if request.content else "", + ) + body = b"" + headers: dict[str, str] = {} + if self.response is not None: + body = json.dumps(self.response).encode() + headers["content-type"] = "application/json" + return httpx.Response( + status_code=self.status_code, + content=body, + headers=headers, + ) + + @property + def sync_transport(self) -> httpx.MockTransport: + return httpx.MockTransport(self._build_response) + + @property + def async_transport(self) -> httpx.MockTransport: + async def handler(request: httpx.Request) -> httpx.Response: + return self._build_response(request) + + return httpx.MockTransport(handler) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..dab5265 --- /dev/null +++ b/uv.lock @@ -0,0 +1,862 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version < '3.14'", +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "argcomplete" +version = "3.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" }, +] + +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, +] + +[[package]] +name = "baseten" +version = "0.9.0" +source = { editable = "." } +dependencies = [ + { name = "httpx" }, + { name = "pydantic" }, +] + +[package.dev-dependencies] +dev = [ + { name = "datamodel-code-generator" }, + { name = "poethepoet" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, + { name = "ty" }, +] + +[package.metadata] +requires-dist = [ + { name = "httpx", specifier = ">=0.27" }, + { name = "pydantic", specifier = ">=2.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "datamodel-code-generator", specifier = ">=0.55.0" }, + { name = "poethepoet", specifier = ">=0.35" }, + { name = "pytest", specifier = ">=9.0.2" }, + { name = "pytest-asyncio", specifier = ">=1.3.0" }, + { name = "ruff", specifier = ">=0.15.8" }, + { name = "ty", specifier = ">=0.0.26" }, +] + +[[package]] +name = "black" +version = "26.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/a8/11170031095655d36ebc6664fe0897866f6023892396900eec0e8fdc4299/black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2", size = 1866562, upload-time = "2026-03-12T03:39:58.639Z" }, + { url = "https://files.pythonhosted.org/packages/69/ce/9e7548d719c3248c6c2abfd555d11169457cbd584d98d179111338423790/black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b", size = 1703623, upload-time = "2026-03-12T03:40:00.347Z" }, + { url = "https://files.pythonhosted.org/packages/7f/0a/8d17d1a9c06f88d3d030d0b1d4373c1551146e252afe4547ed601c0e697f/black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac", size = 1768388, upload-time = "2026-03-12T03:40:01.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/79/c1ee726e221c863cde5164f925bacf183dfdf0397d4e3f94889439b947b4/black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a", size = 1412969, upload-time = "2026-03-12T03:40:03.252Z" }, + { url = "https://files.pythonhosted.org/packages/73/a5/15c01d613f5756f68ed8f6d4ec0a1e24b82b18889fa71affd3d1f7fad058/black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a", size = 1220345, upload-time = "2026-03-12T03:40:04.892Z" }, + { url = "https://files.pythonhosted.org/packages/17/57/5f11c92861f9c92eb9dddf515530bc2d06db843e44bdcf1c83c1427824bc/black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff", size = 1851987, upload-time = "2026-03-12T03:40:06.248Z" }, + { url = "https://files.pythonhosted.org/packages/54/aa/340a1463660bf6831f9e39646bf774086dbd8ca7fc3cded9d59bbdf4ad0a/black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c", size = 1689499, upload-time = "2026-03-12T03:40:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/01/b726c93d717d72733da031d2de10b92c9fa4c8d0c67e8a8a372076579279/black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5", size = 1754369, upload-time = "2026-03-12T03:40:09.279Z" }, + { url = "https://files.pythonhosted.org/packages/e3/09/61e91881ca291f150cfc9eb7ba19473c2e59df28859a11a88248b5cbbc4d/black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e", size = 1413613, upload-time = "2026-03-12T03:40:10.943Z" }, + { url = "https://files.pythonhosted.org/packages/16/73/544f23891b22e7efe4d8f812371ab85b57f6a01b2fc45e3ba2e52ba985b8/black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5", size = 1219719, upload-time = "2026-03-12T03:40:12.597Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f8/da5eae4fc75e78e6dceb60624e1b9662ab00d6b452996046dfa9b8a6025b/black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1", size = 1895920, upload-time = "2026-03-12T03:40:13.921Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9f/04e6f26534da2e1629b2b48255c264cabf5eedc5141d04516d9d68a24111/black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f", size = 1718499, upload-time = "2026-03-12T03:40:15.239Z" }, + { url = "https://files.pythonhosted.org/packages/04/91/a5935b2a63e31b331060c4a9fdb5a6c725840858c599032a6f3aac94055f/black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7", size = 1794994, upload-time = "2026-03-12T03:40:17.124Z" }, + { url = "https://files.pythonhosted.org/packages/e7/0a/86e462cdd311a3c2a8ece708d22aba17d0b2a0d5348ca34b40cdcbea512e/black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983", size = 1420867, upload-time = "2026-03-12T03:40:18.83Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e5/22515a19cb7eaee3440325a6b0d95d2c0e88dd180cb011b12ae488e031d1/black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb", size = 1230124, upload-time = "2026-03-12T03:40:20.425Z" }, + { url = "https://files.pythonhosted.org/packages/f5/77/5728052a3c0450c53d9bb3945c4c46b91baa62b2cafab6801411b6271e45/black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54", size = 1895034, upload-time = "2026-03-12T03:40:21.813Z" }, + { url = "https://files.pythonhosted.org/packages/52/73/7cae55fdfdfbe9d19e9a8d25d145018965fe2079fa908101c3733b0c55a0/black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f", size = 1718503, upload-time = "2026-03-12T03:40:23.666Z" }, + { url = "https://files.pythonhosted.org/packages/e1/87/af89ad449e8254fdbc74654e6467e3c9381b61472cc532ee350d28cfdafb/black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56", size = 1793557, upload-time = "2026-03-12T03:40:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/43/10/d6c06a791d8124b843bf325ab4ac7d2f5b98731dff84d6064eafd687ded1/black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839", size = 1422766, upload-time = "2026-03-12T03:40:27.14Z" }, + { url = "https://files.pythonhosted.org/packages/59/4f/40a582c015f2d841ac24fed6390bd68f0fc896069ff3a886317959c9daf8/black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2", size = 1232140, upload-time = "2026-03-12T03:40:28.882Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/e36e27c9cebc1311b7579210df6f1c86e50f2d7143ae4fcf8a5017dc8809/black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78", size = 1889234, upload-time = "2026-03-12T03:40:30.964Z" }, + { url = "https://files.pythonhosted.org/packages/0e/7b/9871acf393f64a5fa33668c19350ca87177b181f44bb3d0c33b2d534f22c/black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568", size = 1720522, upload-time = "2026-03-12T03:40:32.346Z" }, + { url = "https://files.pythonhosted.org/packages/03/87/e766c7f2e90c07fb7586cc787c9ae6462b1eedab390191f2b7fc7f6170a9/black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f", size = 1787824, upload-time = "2026-03-12T03:40:33.636Z" }, + { url = "https://files.pythonhosted.org/packages/ac/94/2424338fb2d1875e9e83eed4c8e9c67f6905ec25afd826a911aea2b02535/black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c", size = 1445855, upload-time = "2026-03-12T03:40:35.442Z" }, + { url = "https://files.pythonhosted.org/packages/86/43/0c3338bd928afb8ee7471f1a4eec3bdbe2245ccb4a646092a222e8669840/black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1", size = 1258109, upload-time = "2026-03-12T03:40:36.832Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "datamodel-code-generator" +version = "0.55.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete" }, + { name = "black" }, + { name = "genson" }, + { name = "inflect" }, + { name = "isort" }, + { name = "jinja2" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/36/ec505ce62c143c0f045e82e2bb0360e2ede765c0cfe3a70bf32c5661b8a2/datamodel_code_generator-0.55.0.tar.gz", hash = "sha256:20ae7a4fbbb12be380f0bd02544db4abae96c5b644d4b3f2b9c3fc0bc9ee1184", size = 833828, upload-time = "2026-03-10T20:41:15.796Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/c6/2abc9d11adbbf689b6b4dfb7a136d57b9ccaa3b3f1ba83504462109e8dbb/datamodel_code_generator-0.55.0-py3-none-any.whl", hash = "sha256:efa5a925288ca2a135fdc3361c7d774ae5b24b4fd632868363e249d55ea2f137", size = 256860, upload-time = "2026-03-10T20:41:13.488Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "genson" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/2303c8ad276dcf5ee2ad6cf69c4338fd86ef0f471a5207b069adf7a393cf/genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37", size = 34919, upload-time = "2024-05-15T22:08:49.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470, upload-time = "2024-05-15T22:08:47.056Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "inflect" +version = "7.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, + { name = "typeguard" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751, upload-time = "2024-12-28T17:11:18.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197, upload-time = "2024-12-28T17:11:15.931Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "isort" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "more-itertools" +version = "10.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pastel" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/f1/4594f5e0fcddb6953e5b8fe00da8c317b8b41b547e2b3ae2da7512943c62/pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d", size = 7555, upload-time = "2020-09-16T19:21:12.43Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955, upload-time = "2020-09-16T19:21:11.409Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "poethepoet" +version = "0.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pastel" }, + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/9b/e717572686bbf23e17483389c1bf3a381ca2427c84c7e0af0cdc0f23fccc/poethepoet-0.42.1.tar.gz", hash = "sha256:205747e276062c2aaba8afd8a98838f8a3a0237b7ab94715fab8d82718aac14f", size = 93209, upload-time = "2026-02-26T22:57:50.883Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/68/75fa0a5ef39718ea6ba7ab6a3d031fa93640e57585580cec85539540bb65/poethepoet-0.42.1-py3-none-any.whl", hash = "sha256:d8d1345a5ca521be9255e7c13bc2c4c8698ed5e5ac5e9e94890d239fcd423d0a", size = 119967, upload-time = "2026-02-26T22:57:49.467Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, +] + +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, + { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, + { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, + { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, + { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, + { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, + { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, + { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, + { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, + { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "ty" +version = "0.0.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/94/4879b81f8681117ccaf31544579304f6dc2ddcc0c67f872afb35869643a2/ty-0.0.26.tar.gz", hash = "sha256:0496b62405d62de7b954d6d677dc1cc5d3046197215d7a0a7fef37745d7b6d29", size = 5393643, upload-time = "2026-03-26T16:27:11.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/24/99fe33ecd7e16d23c53b0d4244778c6d1b6eb1663b091236dcba22882d67/ty-0.0.26-py3-none-linux_armv6l.whl", hash = "sha256:35beaa56cf59725fd59ab35d8445bbd40b97fe76db39b052b1fcb31f9bf8adf7", size = 10521856, upload-time = "2026-03-26T16:27:06.335Z" }, + { url = "https://files.pythonhosted.org/packages/55/97/1b5e939e2ff69b9bb279ab680bfa8f677d886309a1ac8d9588fd6ce58146/ty-0.0.26-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:487a0be58ab0eb02e31ba71eb6953812a0f88e50633469b0c0ce3fb795fe0fa1", size = 10320958, upload-time = "2026-03-26T16:27:13.849Z" }, + { url = "https://files.pythonhosted.org/packages/71/25/37081461e13d38a190e5646948d7bc42084f7bd1c6b44f12550be3923e7e/ty-0.0.26-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a01b7de5693379646d423b68f119719a1338a20017ba48a93eefaff1ee56f97b", size = 9799905, upload-time = "2026-03-26T16:26:55.805Z" }, + { url = "https://files.pythonhosted.org/packages/a1/1c/295d8f55a7b0e037dfc3a5ec4bdda3ab3cbca6f492f725bf269f96a4d841/ty-0.0.26-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:628c3ee869d113dd2bd249925662fd39d9d0305a6cb38f640ddaa7436b74a1ef", size = 10317507, upload-time = "2026-03-26T16:27:31.887Z" }, + { url = "https://files.pythonhosted.org/packages/1d/62/48b3875c5d2f48fe017468d4bbdde1164c76a8184374f1d5e6162cf7d9b8/ty-0.0.26-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63d04f35f5370cbc91c0b9675dc83e0c53678125a7b629c9c95769e86f123e65", size = 10319821, upload-time = "2026-03-26T16:27:29.647Z" }, + { url = "https://files.pythonhosted.org/packages/ff/28/cfb2d495046d5bf42d532325cea7412fa1189912d549dbfae417a24fd794/ty-0.0.26-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a53c4e6f6a91927f8b90e584a4b12bcde05b0c1870ddff8d17462168ad7947a", size = 10831757, upload-time = "2026-03-26T16:27:37.441Z" }, + { url = "https://files.pythonhosted.org/packages/26/bf/dbc3e42f448a2d862651de070b4108028c543ca18cab096b38d7de449915/ty-0.0.26-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:caf2ced0e58d898d5e3ba5cb843e0ebd377c8a461464748586049afbd9321f51", size = 11369556, upload-time = "2026-03-26T16:26:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/92/4c/6d2f8f34bc6d502ab778c9345a4a936a72ae113de11329c1764bb1f204f6/ty-0.0.26-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:384807bbcb7d7ce9b97ee5aaa6417a8ae03ccfb426c52b08018ca62cf60f5430", size = 11085679, upload-time = "2026-03-26T16:27:21.746Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f4/f3f61c203bc980dd9bba0ba7ed3c6e81ddfd36b286330f9487c2c7d041aa/ty-0.0.26-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2c766a94d79b4f82995d41229702caf2d76e5c440ec7e543d05c70e98bf8ab", size = 10900581, upload-time = "2026-03-26T16:27:24.39Z" }, + { url = "https://files.pythonhosted.org/packages/3d/fd/3ca1b4e4bdd129829e9ce78677e0f8e0f1038a7702dccecfa52f037c6046/ty-0.0.26-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f41ac45a0f8e3e8e181508d863a0a62156341db0f624ffd004b97ee550a9de80", size = 10294401, upload-time = "2026-03-26T16:27:03.999Z" }, + { url = "https://files.pythonhosted.org/packages/de/20/4ee3d8c3f90e008843795c765cb8bb245f188c23e5e5cc612c7697406fba/ty-0.0.26-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:73eb8327a34d529438dfe4db46796946c4e825167cbee434dc148569892e435f", size = 10351469, upload-time = "2026-03-26T16:27:19.003Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b1/9fb154ade65906d4148f0b999c4a8257c2a34253cb72e15d84c1f04a064e/ty-0.0.26-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4bb53a79259516535a1b55f613ba1619e9c666854946474ca8418c35a5c4fd60", size = 10529488, upload-time = "2026-03-26T16:27:01.378Z" }, + { url = "https://files.pythonhosted.org/packages/a5/70/9b02b03b1862e27b64143db65946d68b138160a5b6bfea193bee0b8bbc34/ty-0.0.26-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2f0e75edc1aeb1b4b84af516c7891f631254a4ca3dcd15e848fa1e061e1fe9da", size = 10999015, upload-time = "2026-03-26T16:27:34.636Z" }, + { url = "https://files.pythonhosted.org/packages/21/16/0a56b8667296e2989b9d48095472d98ebf57a0006c71f2a101bbc62a142d/ty-0.0.26-py3-none-win32.whl", hash = "sha256:943c998c5523ed6b519c899c0c39b26b4c751a9759e460fb964765a44cde226f", size = 9912378, upload-time = "2026-03-26T16:27:08.999Z" }, + { url = "https://files.pythonhosted.org/packages/60/c2/fef0d4bba9cd89a82d725b3b1a66efb1b36629ecf0fb1d8e916cb75b8829/ty-0.0.26-py3-none-win_amd64.whl", hash = "sha256:19c856d343efeb1ecad8ee220848f5d2c424daf7b2feda357763ad3036e2172f", size = 10863737, upload-time = "2026-03-26T16:27:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/4d/05/888ebcb3c4d3b6b72d5d3241fddd299142caa3c516e6d26a9cd887dfed3b/ty-0.0.26-py3-none-win_arm64.whl", hash = "sha256:2cde58ccffa046db1223dc28f3e7d4f2c7da8267e97cc5cd186af6fe85f1758a", size = 10285408, upload-time = "2026-03-26T16:27:16.432Z" }, +] + +[[package]] +name = "typeguard" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/e8/66e25efcc18542d58706ce4e50415710593721aae26e794ab1dec34fb66f/typeguard-4.5.1.tar.gz", hash = "sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274", size = 80121, upload-time = "2026-02-19T16:09:03.392Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl", hash = "sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40", size = 36745, upload-time = "2026-02-19T16:09:01.6Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] From 6c8f60462fcb5a8395bf1edd59020ca19a83b4a1 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Tue, 31 Mar 2026 14:56:32 -0500 Subject: [PATCH 2/2] Add e2e tests and bootstrap script --- .github/workflows/ci.yml | 6 + CONTRIBUTING.md | 24 ++++ pyproject.toml | 3 + scripts/e2e_test_bootstrap/__main__.py | 90 +++++++++++++ tests/test_e2e.py | 176 +++++++++++++++++++++++++ 5 files changed, 299 insertions(+) create mode 100644 scripts/e2e_test_bootstrap/__main__.py create mode 100644 tests/test_e2e.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3ed7bf..dd013c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,4 +38,10 @@ jobs: git diff --exit-code - name: Test + env: + # Forks won't have these secrets so e2e tests skip, but pushes + # to main will run them. + BASETEN_E2E_TEST_API_KEY: ${{ secrets.BASETEN_E2E_TEST_API_KEY }} + BASETEN_E2E_TEST_DOMAIN: ${{ secrets.BASETEN_E2E_TEST_DOMAIN }} + BASETEN_E2E_TEST_MODEL_ID: ${{ secrets.BASETEN_E2E_TEST_MODEL_ID }} run: uv run poe test diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1a7069..e4d8ad7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,3 +15,27 @@ All tasks are run via `uv run poe `: - `lint` - Check formatting and lint (fails on issues) - `typecheck` - Run type checker - `test` - Run tests + +## End-to-End Tests + +E2e tests in `tests/test_e2e.py` run against a live Baseten environment. They are skipped automatically when `BASETEN_E2E_TEST_API_KEY` is not set. + +### Bootstrap + +Deploy a minimal test model (requires `truss`): + +```bash +BASETEN_E2E_TEST_API_KEY=... BASETEN_E2E_TEST_DOMAIN=... \ + uv run --with truss python -m scripts.e2e_test_bootstrap +``` + +This prints the model ID to stdout. Wait for the model to be ready before running tests. + +### Running + +```bash +BASETEN_E2E_TEST_API_KEY=... \ +BASETEN_E2E_TEST_DOMAIN=... \ +BASETEN_E2E_TEST_MODEL_ID=... \ + uv run poe test tests/test_e2e.py +``` diff --git a/pyproject.toml b/pyproject.toml index e40db2f..3c42e13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,9 @@ sequence = [ # [tool.uv] # exclude-newer = "7 days" +[tool.ty.src] +exclude = ["scripts/e2e_test_bootstrap/"] + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" diff --git a/scripts/e2e_test_bootstrap/__main__.py b/scripts/e2e_test_bootstrap/__main__.py new file mode 100644 index 0000000..ddc3775 --- /dev/null +++ b/scripts/e2e_test_bootstrap/__main__.py @@ -0,0 +1,90 @@ +"""Bootstrap a minimal Truss model for e2e testing. + +Reads from the environment: + BASETEN_E2E_TEST_API_KEY — API key for authentication (required) + BASETEN_E2E_TEST_DOMAIN — Domain, e.g. staging.baseten.co (required) + +Pushes a tiny echo model via truss as a library (without mutating +~/.trussrc) and prints the resulting model ID to stdout. + +Usage: + BASETEN_E2E_TEST_API_KEY=... BASETEN_E2E_TEST_DOMAIN=... \ + python -m scripts.e2e_test_bootstrap +""" + +from __future__ import annotations + +import os +import sys +import tempfile +from pathlib import Path + +CONFIG_YAML = """\ +model_name: sdk-e2e-test +python_version: py312 +resources: + cpu: "1" + memory: 2Gi +""" + +MODEL_PY = """\ +class Model: + def load(self): + pass + + def predict(self, request): + return {"message": "ok"} +""" + + +def main() -> None: + api_key = os.environ.get("BASETEN_E2E_TEST_API_KEY", "") + if not api_key: + print("BASETEN_E2E_TEST_API_KEY is required", file=sys.stderr) + sys.exit(1) + + domain = os.environ.get("BASETEN_E2E_TEST_DOMAIN", "") + if not domain: + print("BASETEN_E2E_TEST_DOMAIN is required", file=sys.stderr) + sys.exit(1) + + remote_url = f"https://app.{domain}" + + try: + from truss.remote.baseten.remote import BasetenRemote # type: ignore[import-untyped] + from truss.truss_handle.build import load # type: ignore[import-untyped] + except ImportError: + print( + "truss is required: pip install truss (or uv pip install truss)", + file=sys.stderr, + ) + sys.exit(1) + + with tempfile.TemporaryDirectory() as tmpdir: + root = Path(tmpdir) + (root / "config.yaml").write_text(CONFIG_YAML) + model_dir = root / "model" + model_dir.mkdir() + (model_dir / "__init__.py").write_text("") + (model_dir / "model.py").write_text(MODEL_PY) + + print(f"Pushing minimal Truss model to {remote_url}...", file=sys.stderr) + + # Use BasetenRemote directly to avoid mutating ~/.trussrc. + remote = BasetenRemote(remote_url=remote_url, api_key=api_key) + truss_handle = load(str(root)) + service = remote.push( + truss_handle, + model_name="sdk-e2e-test", + working_dir=root, + publish=True, + ) + + model_id = service.model_id + print(f"Model ID: {model_id}", file=sys.stderr) + # Print bare model ID to stdout for easy capture. + print(model_id) + + +if __name__ == "__main__": + main() diff --git a/tests/test_e2e.py b/tests/test_e2e.py new file mode 100644 index 0000000..6cee07f --- /dev/null +++ b/tests/test_e2e.py @@ -0,0 +1,176 @@ +"""End-to-end tests against a live Baseten environment. + +Requires the following environment variables: + BASETEN_E2E_TEST_API_KEY — API key for authentication + BASETEN_E2E_TEST_DOMAIN — Domain (e.g. baseten.co) + BASETEN_E2E_TEST_MODEL_ID — Model ID of a deployed model (bootstrap first) + +Skipped when BASETEN_E2E_TEST_API_KEY is empty or unset. If the API key is +set but either DOMAIN or MODEL_ID is missing, the test errors. +""" + +from __future__ import annotations + +import functools +import inspect +import os +import uuid +from typing import Any, Callable + +import pytest + +import baseten.client.inferenceapi +import baseten.client.managementapi +from baseten.client import ( + AsyncInferenceClient, + AsyncManagementClient, + ManagementClient, +) + +API_KEY = os.environ.get("BASETEN_E2E_TEST_API_KEY", "") +DOMAIN = os.environ.get("BASETEN_E2E_TEST_DOMAIN", "") +MODEL_ID = os.environ.get("BASETEN_E2E_TEST_MODEL_ID", "") + + +def ensure_e2e_env() -> None: + if not API_KEY: + pytest.skip("BASETEN_E2E_TEST_API_KEY not set") + if not DOMAIN: + raise EnvironmentError( + "BASETEN_E2E_TEST_API_KEY is set but BASETEN_E2E_TEST_DOMAIN is missing" + ) + if not MODEL_ID: + raise EnvironmentError( + "BASETEN_E2E_TEST_API_KEY is set but BASETEN_E2E_TEST_MODEL_ID is missing" + ) + + +def e2e(fn: Callable[..., Any]) -> Callable[..., Any]: + if inspect.iscoroutinefunction(fn): + + @functools.wraps(fn) + async def async_wrapper(*args: Any, **kwargs: Any) -> Any: + ensure_e2e_env() + return await fn(*args, **kwargs) + + return async_wrapper + + @functools.wraps(fn) + def wrapper(*args: Any, **kwargs: Any) -> Any: + ensure_e2e_env() + return fn(*args, **kwargs) + + return wrapper + + +def management_client() -> ManagementClient: + return ManagementClient( + api_key=API_KEY, + base_url_override=f"https://api.{DOMAIN}", + ) + + +def async_management_client() -> AsyncManagementClient: + return AsyncManagementClient( + api_key=API_KEY, + base_url_override=f"https://api.{DOMAIN}", + ) + + +def async_inference_client() -> AsyncInferenceClient: + return AsyncInferenceClient( + api_key=API_KEY, + base_url_override=f"https://model-{MODEL_ID}.api.{DOMAIN}", + ) + + +@e2e +def test_list_models() -> None: + with management_client() as client: + models = client.api.get_models() + assert models.models is not None + ids = [m.id for m in models.models] + assert MODEL_ID in ids + + +@e2e +def test_get_model() -> None: + with management_client() as client: + model = client.api.get_models_model_id(model_id=MODEL_ID) + assert model.id == MODEL_ID + assert model.name is not None + + +@e2e +def test_get_model_not_found() -> None: + with management_client() as client: + with pytest.raises(baseten.client.managementapi.ResponseError) as exc_info: + client.api.get_models_model_id(model_id="nonexistent-model-id") + assert exc_info.value.status_code == 404 + + +@e2e +def test_list_deployments() -> None: + with management_client() as client: + deployments = client.api.get_models_deployments(model_id=MODEL_ID) + assert deployments.deployments is not None + assert len(deployments.deployments) > 0 + + +@e2e +@pytest.mark.asyncio +async def test_get_model_async() -> None: + async with async_management_client() as client: + model = await client.api.get_models_model_id(model_id=MODEL_ID) + assert model.id == MODEL_ID + assert model.name is not None + + +@e2e +@pytest.mark.asyncio +async def test_inference() -> None: + async with async_inference_client() as client: + result = await client.api.predict_production( + body=baseten.client.inferenceapi.PredictInput({"prompt": "hello"}), + ) + assert result.root is not None + + +@e2e +@pytest.mark.asyncio +async def test_api_key_crud() -> None: + key_name = f"e2e-test-{uuid.uuid4()}" + created_prefix: str | None = None + + async with async_management_client() as client: + try: + created = await client.api.post_api_keys( + body=baseten.client.managementapi.CreateAPIKeyRequest( + name=key_name, + type=baseten.client.managementapi.APIKeyCategory.PERSONAL, + ), + ) + assert created.api_key + created_prefix = created.api_key.split(".")[0] + + keys = await client.api.get_api_keys() + names = [k.name for k in keys.keys] + assert key_name in names + + tombstone = await client.api.delete_api_keys( + api_key_prefix=created_prefix, + ) + assert tombstone.prefix == created_prefix + + keys = await client.api.get_api_keys() + prefixes = [k.prefix for k in keys.keys] + assert created_prefix not in prefixes + created_prefix = None + finally: + if created_prefix is not None: + try: + await client.api.delete_api_keys( + api_key_prefix=created_prefix, + ) + except Exception: + pass