From 6dd2af8b32b66b723f9eb2d06d60e36f04ae0a0c Mon Sep 17 00:00:00 2001 From: daavoo <12677733+daavoo@users.noreply.github.com> Date: Tue, 15 Sep 2026 11:59:15 +0000 Subject: [PATCH] chore: regenerate SDK client core from Otari OpenAPI spec --- sdk-endpoints.txt | 7 + src/otari/_client/__init__.py | 22 + src/otari/_client/api/__init__.py | 1 + src/otari/_client/api/catalog_api.py | 819 ++++++++++++++++++ .../_client/api/organization_pricing_api.py | 12 +- src/otari/_client/api/pricing_api.py | 783 +++++++++++++++++ src/otari/_client/models/__init__.py | 10 + .../models/accepted_snapshot_response.py | 95 ++ .../_client/models/catalog_capabilities.py | 96 ++ src/otari/_client/models/catalog_elsewhere.py | 90 ++ .../_client/models/catalog_model_detail.py | 225 +++++ .../_client/models/catalog_model_summary.py | 203 +++++ src/otari/_client/models/catalog_offering.py | 191 ++++ src/otari/_client/models/catalog_response.py | 108 +++ .../_client/models/deployment_bootstrap.py | 4 +- src/otari/_client/models/model_object.py | 6 +- .../_client/models/model_pricing_info.py | 8 +- src/otari/_client/models/offering_usage.py | 108 +++ .../organization_model_pricing_create.py | 18 +- .../organization_model_pricing_public.py | 4 +- .../organization_model_pricing_update.py | 18 +- src/otari/_client/models/pricing_drift_row.py | 138 +++ src/otari/_client/models/pricing_response.py | 13 +- .../_client/models/selector_index_response.py | 92 ++ .../_client/models/set_pricing_request.py | 18 +- .../_client/models/update_settings_request.py | 26 +- 26 files changed, 3090 insertions(+), 25 deletions(-) create mode 100644 src/otari/_client/api/catalog_api.py create mode 100644 src/otari/_client/models/accepted_snapshot_response.py create mode 100644 src/otari/_client/models/catalog_capabilities.py create mode 100644 src/otari/_client/models/catalog_elsewhere.py create mode 100644 src/otari/_client/models/catalog_model_detail.py create mode 100644 src/otari/_client/models/catalog_model_summary.py create mode 100644 src/otari/_client/models/catalog_offering.py create mode 100644 src/otari/_client/models/catalog_response.py create mode 100644 src/otari/_client/models/offering_usage.py create mode 100644 src/otari/_client/models/pricing_drift_row.py create mode 100644 src/otari/_client/models/selector_index_response.py diff --git a/sdk-endpoints.txt b/sdk-endpoints.txt index 53ea0957..fe925672 100644 --- a/sdk-endpoints.txt +++ b/sdk-endpoints.txt @@ -204,6 +204,13 @@ GET /api/v1/routing/status # not yet wrapped POST /api/v1/pricing/refresh # not yet wrapped POST /api/v1/pricing/refresh/confirm # not yet wrapped POST /api/v1/pricing/refresh/reject # not yet wrapped +GET /api/v1/pricing/refresh/pending # not yet wrapped +GET /api/v1/pricing/snapshots # not yet wrapped +GET /api/v1/pricing/drift # not yet wrapped +# Model catalog, grouped by model +GET /api/v1/catalog/models # not yet wrapped +GET /api/v1/catalog/models/{model_id} # not yet wrapped +POST /api/v1/catalog/selectors/refresh # not yet wrapped # Providers GET /api/v1/providers/catalog/{provider_id} # not yet wrapped GET /api/v1/providers/health # not yet wrapped diff --git a/src/otari/_client/__init__.py b/src/otari/_client/__init__.py index e498368a..5c690019 100644 --- a/src/otari/_client/__init__.py +++ b/src/otari/_client/__init__.py @@ -26,6 +26,7 @@ "BatchesApi", "BootstrapApi", "BudgetsApi", + "CatalogApi", "ChatApi", "EmbeddingsApi", "FilesApi", @@ -77,6 +78,7 @@ "ApiException", "AcceptInvitationRequest", "AcceptInvitationResultPublic", + "AcceptedSnapshotResponse", "ActivationApiKeyPublic", "ActivationAttemptPublic", "ActiveOrganizationMemberCreateRequest", @@ -150,6 +152,12 @@ "CallerOrganizationMembershipsPublic", "CallerWorkspaceMembershipPublic", "CandidateResponse", + "CatalogCapabilities", + "CatalogElsewhere", + "CatalogModelDetail", + "CatalogModelSummary", + "CatalogOffering", + "CatalogResponse", "ChatCompletion", "ChatCompletionChunk", "ChatCompletionRequest", @@ -405,6 +413,7 @@ "ModerationResult", "OAuthCallbackRequest", "OAuthSessionResponse", + "OfferingUsage", "OrgProviderKeyCreateRequest", "OrgProviderKeyPublic", "OrgProviderKeyUpdateRequest", @@ -456,6 +465,7 @@ "PolicyRequest", "PolicyResponse", "PoolStatus", + "PricingDriftRow", "PricingRefreshChangeResponse", "PricingRefreshConfirmationResponse", "PricingRefreshPreviewResponse", @@ -497,6 +507,7 @@ "SearchResponse", "SearchResultItem", "SearchToolsResponse", + "SelectorIndexResponse", "SendTestMailRequest", "SendTestMailResponse", "SessionResponse", @@ -606,6 +617,7 @@ from otari._client.api.batches_api import BatchesApi as BatchesApi from otari._client.api.bootstrap_api import BootstrapApi as BootstrapApi from otari._client.api.budgets_api import BudgetsApi as BudgetsApi +from otari._client.api.catalog_api import CatalogApi as CatalogApi from otari._client.api.chat_api import ChatApi as ChatApi from otari._client.api.embeddings_api import EmbeddingsApi as EmbeddingsApi from otari._client.api.files_api import FilesApi as FilesApi @@ -661,6 +673,7 @@ # import models into sdk package from otari._client.models.accept_invitation_request import AcceptInvitationRequest as AcceptInvitationRequest from otari._client.models.accept_invitation_result_public import AcceptInvitationResultPublic as AcceptInvitationResultPublic +from otari._client.models.accepted_snapshot_response import AcceptedSnapshotResponse as AcceptedSnapshotResponse from otari._client.models.activation_api_key_public import ActivationApiKeyPublic as ActivationApiKeyPublic from otari._client.models.activation_attempt_public import ActivationAttemptPublic as ActivationAttemptPublic from otari._client.models.active_organization_member_create_request import ActiveOrganizationMemberCreateRequest as ActiveOrganizationMemberCreateRequest @@ -734,6 +747,12 @@ from otari._client.models.caller_organization_memberships_public import CallerOrganizationMembershipsPublic as CallerOrganizationMembershipsPublic from otari._client.models.caller_workspace_membership_public import CallerWorkspaceMembershipPublic as CallerWorkspaceMembershipPublic from otari._client.models.candidate_response import CandidateResponse as CandidateResponse +from otari._client.models.catalog_capabilities import CatalogCapabilities as CatalogCapabilities +from otari._client.models.catalog_elsewhere import CatalogElsewhere as CatalogElsewhere +from otari._client.models.catalog_model_detail import CatalogModelDetail as CatalogModelDetail +from otari._client.models.catalog_model_summary import CatalogModelSummary as CatalogModelSummary +from otari._client.models.catalog_offering import CatalogOffering as CatalogOffering +from otari._client.models.catalog_response import CatalogResponse as CatalogResponse from otari._client.models.chat_completion import ChatCompletion as ChatCompletion from otari._client.models.chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk from otari._client.models.chat_completion_request import ChatCompletionRequest as ChatCompletionRequest @@ -989,6 +1008,7 @@ from otari._client.models.moderation_result import ModerationResult as ModerationResult from otari._client.models.o_auth_callback_request import OAuthCallbackRequest as OAuthCallbackRequest from otari._client.models.o_auth_session_response import OAuthSessionResponse as OAuthSessionResponse +from otari._client.models.offering_usage import OfferingUsage as OfferingUsage from otari._client.models.org_provider_key_create_request import OrgProviderKeyCreateRequest as OrgProviderKeyCreateRequest from otari._client.models.org_provider_key_public import OrgProviderKeyPublic as OrgProviderKeyPublic from otari._client.models.org_provider_key_update_request import OrgProviderKeyUpdateRequest as OrgProviderKeyUpdateRequest @@ -1040,6 +1060,7 @@ from otari._client.models.policy_request import PolicyRequest as PolicyRequest from otari._client.models.policy_response import PolicyResponse as PolicyResponse from otari._client.models.pool_status import PoolStatus as PoolStatus +from otari._client.models.pricing_drift_row import PricingDriftRow as PricingDriftRow from otari._client.models.pricing_refresh_change_response import PricingRefreshChangeResponse as PricingRefreshChangeResponse from otari._client.models.pricing_refresh_confirmation_response import PricingRefreshConfirmationResponse as PricingRefreshConfirmationResponse from otari._client.models.pricing_refresh_preview_response import PricingRefreshPreviewResponse as PricingRefreshPreviewResponse @@ -1081,6 +1102,7 @@ from otari._client.models.search_response import SearchResponse as SearchResponse from otari._client.models.search_result_item import SearchResultItem as SearchResultItem from otari._client.models.search_tools_response import SearchToolsResponse as SearchToolsResponse +from otari._client.models.selector_index_response import SelectorIndexResponse as SelectorIndexResponse from otari._client.models.send_test_mail_request import SendTestMailRequest as SendTestMailRequest from otari._client.models.send_test_mail_response import SendTestMailResponse as SendTestMailResponse from otari._client.models.session_response import SessionResponse as SessionResponse diff --git a/src/otari/_client/api/__init__.py b/src/otari/_client/api/__init__.py index cb3076c2..7e7af217 100644 --- a/src/otari/_client/api/__init__.py +++ b/src/otari/_client/api/__init__.py @@ -9,6 +9,7 @@ from otari._client.api.batches_api import BatchesApi from otari._client.api.bootstrap_api import BootstrapApi from otari._client.api.budgets_api import BudgetsApi +from otari._client.api.catalog_api import CatalogApi from otari._client.api.chat_api import ChatApi from otari._client.api.embeddings_api import EmbeddingsApi from otari._client.api.files_api import FilesApi diff --git a/src/otari/_client/api/catalog_api.py b/src/otari/_client/api/catalog_api.py new file mode 100644 index 00000000..f3399517 --- /dev/null +++ b/src/otari/_client/api/catalog_api.py @@ -0,0 +1,819 @@ +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import warnings +from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt +from typing import Any, Dict, List, Optional, Tuple, Union +from typing_extensions import Annotated + +from pydantic import Field, StrictStr +from typing import Optional +from typing_extensions import Annotated +from otari._client.models.catalog_model_detail import CatalogModelDetail +from otari._client.models.catalog_response import CatalogResponse +from otari._client.models.selector_index_response import SelectorIndexResponse + +from otari._client.api_client import ApiClient, RequestSerialized +from otari._client.api_response import ApiResponse +from otari._client.rest import RESTResponseType + + +class CatalogApi: + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None) -> None: + if api_client is None: + api_client = ApiClient.get_default() + self.api_client = api_client + + + @validate_call + def catalog_get_catalog_model( + self, + model_id: StrictStr, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> CatalogModelDetail: + """Get Catalog Model + + One model and every offering of it this caller may use. The whole merged catalog is built and grouped to answer for one model. That is deliberate: the identity a model is found by is a property of the group, so narrowing the build to one model would need the grouping done first. The query count is constant; the cost is CPU per page view, growing with the size of the catalog rather than with the number of readers. A model the caller may not see answers 404, the same as one that does not exist, so the route cannot be used to probe the catalog behind an allow-list. A signed-in caller's offerings also carry their organization's own usage of each over the last 30 days. + + :param model_id: (required) + :type model_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_get_catalog_model_serialize( + model_id=model_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "CatalogModelDetail", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def catalog_get_catalog_model_with_http_info( + self, + model_id: StrictStr, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[CatalogModelDetail]: + """Get Catalog Model + + One model and every offering of it this caller may use. The whole merged catalog is built and grouped to answer for one model. That is deliberate: the identity a model is found by is a property of the group, so narrowing the build to one model would need the grouping done first. The query count is constant; the cost is CPU per page view, growing with the size of the catalog rather than with the number of readers. A model the caller may not see answers 404, the same as one that does not exist, so the route cannot be used to probe the catalog behind an allow-list. A signed-in caller's offerings also carry their organization's own usage of each over the last 30 days. + + :param model_id: (required) + :type model_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_get_catalog_model_serialize( + model_id=model_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "CatalogModelDetail", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def catalog_get_catalog_model_without_preload_content( + self, + model_id: StrictStr, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Get Catalog Model + + One model and every offering of it this caller may use. The whole merged catalog is built and grouped to answer for one model. That is deliberate: the identity a model is found by is a property of the group, so narrowing the build to one model would need the grouping done first. The query count is constant; the cost is CPU per page view, growing with the size of the catalog rather than with the number of readers. A model the caller may not see answers 404, the same as one that does not exist, so the route cannot be used to probe the catalog behind an allow-list. A signed-in caller's offerings also carry their organization's own usage of each over the last 30 days. + + :param model_id: (required) + :type model_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_get_catalog_model_serialize( + model_id=model_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "CatalogModelDetail", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _catalog_get_catalog_model_serialize( + self, + model_id, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + if model_id is not None: + _path_params['model_id'] = model_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + 'XApiKeyAuth', + 'ApiKeyAuth' + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/api/v1/catalog/models/{model_id}', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + + @validate_call + def catalog_list_catalog( + self, + at_context: Annotated[Optional[Annotated[int, Field(strict=True, ge=1)]], Field(description="Compare prices for a request of this many input tokens: each model's minimum is taken from the pricing tier that request would settle at. Omitted, the base rates compare.")] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> CatalogResponse: + """List Catalog + + The models this caller may use, one entry each however many providers serve it. Prices are the caller's: an organization's override where one applies, else the deployment's row, else the genai-prices default. Aliases and routing policies are not models and are not listed; see Routing. A visitor, where the catalog is public, sees the configured instances at the deployment's rates and nothing that belongs to a tenant. + + :param at_context: Compare prices for a request of this many input tokens: each model's minimum is taken from the pricing tier that request would settle at. Omitted, the base rates compare. + :type at_context: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_list_catalog_serialize( + at_context=at_context, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "CatalogResponse", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def catalog_list_catalog_with_http_info( + self, + at_context: Annotated[Optional[Annotated[int, Field(strict=True, ge=1)]], Field(description="Compare prices for a request of this many input tokens: each model's minimum is taken from the pricing tier that request would settle at. Omitted, the base rates compare.")] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[CatalogResponse]: + """List Catalog + + The models this caller may use, one entry each however many providers serve it. Prices are the caller's: an organization's override where one applies, else the deployment's row, else the genai-prices default. Aliases and routing policies are not models and are not listed; see Routing. A visitor, where the catalog is public, sees the configured instances at the deployment's rates and nothing that belongs to a tenant. + + :param at_context: Compare prices for a request of this many input tokens: each model's minimum is taken from the pricing tier that request would settle at. Omitted, the base rates compare. + :type at_context: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_list_catalog_serialize( + at_context=at_context, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "CatalogResponse", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def catalog_list_catalog_without_preload_content( + self, + at_context: Annotated[Optional[Annotated[int, Field(strict=True, ge=1)]], Field(description="Compare prices for a request of this many input tokens: each model's minimum is taken from the pricing tier that request would settle at. Omitted, the base rates compare.")] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """List Catalog + + The models this caller may use, one entry each however many providers serve it. Prices are the caller's: an organization's override where one applies, else the deployment's row, else the genai-prices default. Aliases and routing policies are not models and are not listed; see Routing. A visitor, where the catalog is public, sees the configured instances at the deployment's rates and nothing that belongs to a tenant. + + :param at_context: Compare prices for a request of this many input tokens: each model's minimum is taken from the pricing tier that request would settle at. Omitted, the base rates compare. + :type at_context: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_list_catalog_serialize( + at_context=at_context, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "CatalogResponse", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _catalog_list_catalog_serialize( + self, + at_context, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + if at_context is not None: + + _query_params.append(('at_context', at_context)) + + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + 'XApiKeyAuth', + 'ApiKeyAuth' + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/api/v1/catalog/models', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + + @validate_call + def catalog_refresh_selector_index( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> SelectorIndexResponse: + """Refresh Selector Index + + Re-index the short spellings now, rather than on the refresher's next tick. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_refresh_selector_index_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "SelectorIndexResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def catalog_refresh_selector_index_with_http_info( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[SelectorIndexResponse]: + """Refresh Selector Index + + Re-index the short spellings now, rather than on the refresher's next tick. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_refresh_selector_index_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "SelectorIndexResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def catalog_refresh_selector_index_without_preload_content( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Refresh Selector Index + + Re-index the short spellings now, rather than on the refresher's next tick. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._catalog_refresh_selector_index_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "SelectorIndexResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _catalog_refresh_selector_index_serialize( + self, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + 'XApiKeyAuth', + 'ApiKeyAuth' + ] + + return self.api_client.param_serialize( + method='POST', + resource_path='/api/v1/catalog/selectors/refresh', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + diff --git a/src/otari/_client/api/organization_pricing_api.py b/src/otari/_client/api/organization_pricing_api.py index 97f57f91..87b3f0fb 100644 --- a/src/otari/_client/api/organization_pricing_api.py +++ b/src/otari/_client/api/organization_pricing_api.py @@ -61,7 +61,7 @@ def organization_pricing_create_organization_pricing( ) -> OrganizationModelPricingPublic: """Create Organization Pricing - Set the organization's rate for a model over a period. Refused with a 409 when the period overlaps one already stored for that model, naming the period it collides with, rather than shadowing it. The key is normalized to its canonical ``instance:model`` form first, the same call ``POST /api/v1/pricing`` makes, and that is what makes one model one row rather than one per spelling. Stored verbatim, ``openai:gpt-4o`` and ``openai/gpt-4o`` are two keys: the overlap rule would not see them as colliding, and both would resolve, with the canonical one preferred, leaving the other dormant until the first is deleted. Normalizing on the way in is what stops that pair existing at all. + Set the organization's rate for a model over a period. Refused with a 409 when the period overlaps one already stored for that model, naming the period it collides with, rather than shadowing it. Refused with a 403 when the model is addressed through one of the deployment's own provider instances: the deployment holds that credential and settles its upstream bill, so its rate is the deployment price list's rather than a tenant's. The key is normalized to its canonical ``instance:model`` form first, the same call ``POST /api/v1/pricing`` makes, and that is what makes one model one row rather than one per spelling. Stored verbatim, ``openai:gpt-4o`` and ``openai/gpt-4o`` are two keys: the overlap rule would not see them as colliding, and both would resolve, with the canonical one preferred, leaving the other dormant until the first is deleted. Normalizing on the way in is what stops that pair existing at all. :param organization_model_pricing_create: (required) :type organization_model_pricing_create: OrganizationModelPricingCreate @@ -129,7 +129,7 @@ def organization_pricing_create_organization_pricing_with_http_info( ) -> ApiResponse[OrganizationModelPricingPublic]: """Create Organization Pricing - Set the organization's rate for a model over a period. Refused with a 409 when the period overlaps one already stored for that model, naming the period it collides with, rather than shadowing it. The key is normalized to its canonical ``instance:model`` form first, the same call ``POST /api/v1/pricing`` makes, and that is what makes one model one row rather than one per spelling. Stored verbatim, ``openai:gpt-4o`` and ``openai/gpt-4o`` are two keys: the overlap rule would not see them as colliding, and both would resolve, with the canonical one preferred, leaving the other dormant until the first is deleted. Normalizing on the way in is what stops that pair existing at all. + Set the organization's rate for a model over a period. Refused with a 409 when the period overlaps one already stored for that model, naming the period it collides with, rather than shadowing it. Refused with a 403 when the model is addressed through one of the deployment's own provider instances: the deployment holds that credential and settles its upstream bill, so its rate is the deployment price list's rather than a tenant's. The key is normalized to its canonical ``instance:model`` form first, the same call ``POST /api/v1/pricing`` makes, and that is what makes one model one row rather than one per spelling. Stored verbatim, ``openai:gpt-4o`` and ``openai/gpt-4o`` are two keys: the overlap rule would not see them as colliding, and both would resolve, with the canonical one preferred, leaving the other dormant until the first is deleted. Normalizing on the way in is what stops that pair existing at all. :param organization_model_pricing_create: (required) :type organization_model_pricing_create: OrganizationModelPricingCreate @@ -197,7 +197,7 @@ def organization_pricing_create_organization_pricing_without_preload_content( ) -> RESTResponseType: """Create Organization Pricing - Set the organization's rate for a model over a period. Refused with a 409 when the period overlaps one already stored for that model, naming the period it collides with, rather than shadowing it. The key is normalized to its canonical ``instance:model`` form first, the same call ``POST /api/v1/pricing`` makes, and that is what makes one model one row rather than one per spelling. Stored verbatim, ``openai:gpt-4o`` and ``openai/gpt-4o`` are two keys: the overlap rule would not see them as colliding, and both would resolve, with the canonical one preferred, leaving the other dormant until the first is deleted. Normalizing on the way in is what stops that pair existing at all. + Set the organization's rate for a model over a period. Refused with a 409 when the period overlaps one already stored for that model, naming the period it collides with, rather than shadowing it. Refused with a 403 when the model is addressed through one of the deployment's own provider instances: the deployment holds that credential and settles its upstream bill, so its rate is the deployment price list's rather than a tenant's. The key is normalized to its canonical ``instance:model`` form first, the same call ``POST /api/v1/pricing`` makes, and that is what makes one model one row rather than one per spelling. Stored verbatim, ``openai:gpt-4o`` and ``openai/gpt-4o`` are two keys: the overlap rule would not see them as colliding, and both would resolve, with the canonical one preferred, leaving the other dormant until the first is deleted. Normalizing on the way in is what stops that pair existing at all. :param organization_model_pricing_create: (required) :type organization_model_pricing_create: OrganizationModelPricingCreate @@ -889,7 +889,7 @@ def organization_pricing_replace_organization_pricing( ) -> OrganizationModelPricingPublic: """Replace Organization Pricing - Replace an override's rates and period. Future requests in the period price at the new rate; usage already settled keeps the cost it was billed, because a settled cost is stored on the usage row rather than recomputed. + Replace an override's rates and period. Future requests in the period price at the new rate; usage already settled keeps the cost it was billed, because a settled cost is stored on the usage row rather than recomputed. Refused with a 403 on the same deployment-supplied-model rule the create path carries, so a row stored before that rule existed cannot be edited into a rate nobody could create today. :param pricing_id: (required) :type pricing_id: UUID @@ -961,7 +961,7 @@ def organization_pricing_replace_organization_pricing_with_http_info( ) -> ApiResponse[OrganizationModelPricingPublic]: """Replace Organization Pricing - Replace an override's rates and period. Future requests in the period price at the new rate; usage already settled keeps the cost it was billed, because a settled cost is stored on the usage row rather than recomputed. + Replace an override's rates and period. Future requests in the period price at the new rate; usage already settled keeps the cost it was billed, because a settled cost is stored on the usage row rather than recomputed. Refused with a 403 on the same deployment-supplied-model rule the create path carries, so a row stored before that rule existed cannot be edited into a rate nobody could create today. :param pricing_id: (required) :type pricing_id: UUID @@ -1033,7 +1033,7 @@ def organization_pricing_replace_organization_pricing_without_preload_content( ) -> RESTResponseType: """Replace Organization Pricing - Replace an override's rates and period. Future requests in the period price at the new rate; usage already settled keeps the cost it was billed, because a settled cost is stored on the usage row rather than recomputed. + Replace an override's rates and period. Future requests in the period price at the new rate; usage already settled keeps the cost it was billed, because a settled cost is stored on the usage row rather than recomputed. Refused with a 403 on the same deployment-supplied-model rule the create path carries, so a row stored before that rule existed cannot be edited into a rate nobody could create today. :param pricing_id: (required) :type pricing_id: UUID diff --git a/src/otari/_client/api/pricing_api.py b/src/otari/_client/api/pricing_api.py index 528196f9..f7cf4a23 100644 --- a/src/otari/_client/api/pricing_api.py +++ b/src/otari/_client/api/pricing_api.py @@ -19,6 +19,8 @@ from pydantic import Field, StrictStr from typing import List, Optional from typing_extensions import Annotated +from otari._client.models.accepted_snapshot_response import AcceptedSnapshotResponse +from otari._client.models.pricing_drift_row import PricingDriftRow from otari._client.models.pricing_refresh_confirmation_response import PricingRefreshConfirmationResponse from otari._client.models.pricing_refresh_preview_response import PricingRefreshPreviewResponse from otari._client.models.pricing_response import PricingResponse @@ -580,6 +582,253 @@ def _pricing_delete_pricing_serialize( + @validate_call + def pricing_get_pending_pricing_refresh( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> PricingRefreshPreviewResponse: + """Get Pending Pricing Refresh + + The update the scheduled refresh has left waiting for review, if any. What the dashboard's notice reads. 404 when nothing is pending, so a page can ask on load without treating the common case as an error banner. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_get_pending_pricing_refresh_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PricingRefreshPreviewResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def pricing_get_pending_pricing_refresh_with_http_info( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[PricingRefreshPreviewResponse]: + """Get Pending Pricing Refresh + + The update the scheduled refresh has left waiting for review, if any. What the dashboard's notice reads. 404 when nothing is pending, so a page can ask on load without treating the common case as an error banner. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_get_pending_pricing_refresh_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PricingRefreshPreviewResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def pricing_get_pending_pricing_refresh_without_preload_content( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Get Pending Pricing Refresh + + The update the scheduled refresh has left waiting for review, if any. What the dashboard's notice reads. 404 when nothing is pending, so a page can ask on load without treating the common case as an error banner. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_get_pending_pricing_refresh_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PricingRefreshPreviewResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _pricing_get_pending_pricing_refresh_serialize( + self, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + 'XApiKeyAuth', + 'ApiKeyAuth' + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/api/v1/pricing/refresh/pending', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + @validate_call def pricing_get_pricing( self, @@ -1420,6 +1669,540 @@ def _pricing_list_pricing_serialize( + @validate_call + def pricing_list_pricing_drift( + self, + limit: Optional[Annotated[int, Field(le=500, strict=True, ge=1)]] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> List[PricingDriftRow]: + """List Pricing Drift + + Every stored deployment rate in force today, beside today's default for it. A stored row shadows the genai-prices default silently and forever, whether it was a deliberate override or a copy of a then-current default. This is what makes the difference visible: a row that matches the default is a row that could be deleted, and a row far from it is one worth a second look. Tool rows (``otari:``) are per request and have no default to drift from, so they are left out. + + :param limit: + :type limit: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_list_pricing_drift_serialize( + limit=limit, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "List[PricingDriftRow]", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def pricing_list_pricing_drift_with_http_info( + self, + limit: Optional[Annotated[int, Field(le=500, strict=True, ge=1)]] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[List[PricingDriftRow]]: + """List Pricing Drift + + Every stored deployment rate in force today, beside today's default for it. A stored row shadows the genai-prices default silently and forever, whether it was a deliberate override or a copy of a then-current default. This is what makes the difference visible: a row that matches the default is a row that could be deleted, and a row far from it is one worth a second look. Tool rows (``otari:``) are per request and have no default to drift from, so they are left out. + + :param limit: + :type limit: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_list_pricing_drift_serialize( + limit=limit, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "List[PricingDriftRow]", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def pricing_list_pricing_drift_without_preload_content( + self, + limit: Optional[Annotated[int, Field(le=500, strict=True, ge=1)]] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """List Pricing Drift + + Every stored deployment rate in force today, beside today's default for it. A stored row shadows the genai-prices default silently and forever, whether it was a deliberate override or a copy of a then-current default. This is what makes the difference visible: a row that matches the default is a row that could be deleted, and a row far from it is one worth a second look. Tool rows (``otari:``) are per request and have no default to drift from, so they are left out. + + :param limit: + :type limit: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_list_pricing_drift_serialize( + limit=limit, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "List[PricingDriftRow]", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _pricing_list_pricing_drift_serialize( + self, + limit, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + if limit is not None: + + _query_params.append(('limit', limit)) + + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + 'XApiKeyAuth', + 'ApiKeyAuth' + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/api/v1/pricing/drift', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + + @validate_call + def pricing_list_pricing_snapshots( + self, + limit: Optional[Annotated[int, Field(le=200, strict=True, ge=1)]] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> List[AcceptedSnapshotResponse]: + """List Pricing Snapshots + + The accepted default-price snapshots, newest first. + + :param limit: + :type limit: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_list_pricing_snapshots_serialize( + limit=limit, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "List[AcceptedSnapshotResponse]", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def pricing_list_pricing_snapshots_with_http_info( + self, + limit: Optional[Annotated[int, Field(le=200, strict=True, ge=1)]] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[List[AcceptedSnapshotResponse]]: + """List Pricing Snapshots + + The accepted default-price snapshots, newest first. + + :param limit: + :type limit: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_list_pricing_snapshots_serialize( + limit=limit, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "List[AcceptedSnapshotResponse]", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def pricing_list_pricing_snapshots_without_preload_content( + self, + limit: Optional[Annotated[int, Field(le=200, strict=True, ge=1)]] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """List Pricing Snapshots + + The accepted default-price snapshots, newest first. + + :param limit: + :type limit: int + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._pricing_list_pricing_snapshots_serialize( + limit=limit, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "List[AcceptedSnapshotResponse]", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _pricing_list_pricing_snapshots_serialize( + self, + limit, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + if limit is not None: + + _query_params.append(('limit', limit)) + + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + 'XApiKeyAuth', + 'ApiKeyAuth' + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/api/v1/pricing/snapshots', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + @validate_call def pricing_preview_pricing_refresh( self, diff --git a/src/otari/_client/models/__init__.py b/src/otari/_client/models/__init__.py index 85119890..e401d75c 100644 --- a/src/otari/_client/models/__init__.py +++ b/src/otari/_client/models/__init__.py @@ -15,6 +15,7 @@ # import models into model package from otari._client.models.accept_invitation_request import AcceptInvitationRequest from otari._client.models.accept_invitation_result_public import AcceptInvitationResultPublic +from otari._client.models.accepted_snapshot_response import AcceptedSnapshotResponse from otari._client.models.activation_api_key_public import ActivationApiKeyPublic from otari._client.models.activation_attempt_public import ActivationAttemptPublic from otari._client.models.active_organization_member_create_request import ActiveOrganizationMemberCreateRequest @@ -88,6 +89,12 @@ from otari._client.models.caller_organization_memberships_public import CallerOrganizationMembershipsPublic from otari._client.models.caller_workspace_membership_public import CallerWorkspaceMembershipPublic from otari._client.models.candidate_response import CandidateResponse +from otari._client.models.catalog_capabilities import CatalogCapabilities +from otari._client.models.catalog_elsewhere import CatalogElsewhere +from otari._client.models.catalog_model_detail import CatalogModelDetail +from otari._client.models.catalog_model_summary import CatalogModelSummary +from otari._client.models.catalog_offering import CatalogOffering +from otari._client.models.catalog_response import CatalogResponse from otari._client.models.chat_completion import ChatCompletion from otari._client.models.chat_completion_chunk import ChatCompletionChunk from otari._client.models.chat_completion_request import ChatCompletionRequest @@ -343,6 +350,7 @@ from otari._client.models.moderation_result import ModerationResult from otari._client.models.o_auth_callback_request import OAuthCallbackRequest from otari._client.models.o_auth_session_response import OAuthSessionResponse +from otari._client.models.offering_usage import OfferingUsage from otari._client.models.org_provider_key_create_request import OrgProviderKeyCreateRequest from otari._client.models.org_provider_key_public import OrgProviderKeyPublic from otari._client.models.org_provider_key_update_request import OrgProviderKeyUpdateRequest @@ -394,6 +402,7 @@ from otari._client.models.policy_request import PolicyRequest from otari._client.models.policy_response import PolicyResponse from otari._client.models.pool_status import PoolStatus +from otari._client.models.pricing_drift_row import PricingDriftRow from otari._client.models.pricing_refresh_change_response import PricingRefreshChangeResponse from otari._client.models.pricing_refresh_confirmation_response import PricingRefreshConfirmationResponse from otari._client.models.pricing_refresh_preview_response import PricingRefreshPreviewResponse @@ -435,6 +444,7 @@ from otari._client.models.search_response import SearchResponse from otari._client.models.search_result_item import SearchResultItem from otari._client.models.search_tools_response import SearchToolsResponse +from otari._client.models.selector_index_response import SelectorIndexResponse from otari._client.models.send_test_mail_request import SendTestMailRequest from otari._client.models.send_test_mail_response import SendTestMailResponse from otari._client.models.session_response import SessionResponse diff --git a/src/otari/_client/models/accepted_snapshot_response.py b/src/otari/_client/models/accepted_snapshot_response.py new file mode 100644 index 00000000..badd980a --- /dev/null +++ b/src/otari/_client/models/accepted_snapshot_response.py @@ -0,0 +1,95 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from datetime import datetime +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class AcceptedSnapshotResponse(BaseModel): + """ + One accepted genai-prices snapshot in the history. + """ # noqa: E501 + accepted_at: datetime + accepted_by: StrictStr = Field(description="`operator` for a dashboard confirm, `schedule` for the auto policy.") + id: StrictStr + model_count: StrictInt + __properties: ClassVar[List[str]] = ["accepted_at", "accepted_by", "id", "model_count"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of AcceptedSnapshotResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of AcceptedSnapshotResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "accepted_at": obj.get("accepted_at"), + "accepted_by": obj.get("accepted_by"), + "id": obj.get("id"), + "model_count": obj.get("model_count") + }) + return _obj + + diff --git a/src/otari/_client/models/catalog_capabilities.py b/src/otari/_client/models/catalog_capabilities.py new file mode 100644 index 00000000..f831911a --- /dev/null +++ b/src/otari/_client/models/catalog_capabilities.py @@ -0,0 +1,96 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, StrictBool +from typing import Any, ClassVar, Dict, List, Optional +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class CatalogCapabilities(BaseModel): + """ + What a model can do, as models.dev reports it. Any offering's yes is the model's. + """ # noqa: E501 + attachment: Optional[StrictBool] = False + reasoning: Optional[StrictBool] = False + structured_output: Optional[StrictBool] = False + temperature: Optional[StrictBool] = False + tool_call: Optional[StrictBool] = False + __properties: ClassVar[List[str]] = ["attachment", "reasoning", "structured_output", "temperature", "tool_call"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of CatalogCapabilities from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of CatalogCapabilities from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "attachment": obj.get("attachment") if obj.get("attachment") is not None else False, + "reasoning": obj.get("reasoning") if obj.get("reasoning") is not None else False, + "structured_output": obj.get("structured_output") if obj.get("structured_output") is not None else False, + "temperature": obj.get("temperature") if obj.get("temperature") is not None else False, + "tool_call": obj.get("tool_call") if obj.get("tool_call") is not None else False + }) + return _obj + + diff --git a/src/otari/_client/models/catalog_elsewhere.py b/src/otari/_client/models/catalog_elsewhere.py new file mode 100644 index 00000000..725097a7 --- /dev/null +++ b/src/otari/_client/models/catalog_elsewhere.py @@ -0,0 +1,90 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class CatalogElsewhere(BaseModel): + """ + A provider models.dev lists for this model that this deployment has not configured. + """ # noqa: E501 + name: StrictStr + provider_type: StrictStr + __properties: ClassVar[List[str]] = ["name", "provider_type"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of CatalogElsewhere from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of CatalogElsewhere from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "name": obj.get("name"), + "provider_type": obj.get("provider_type") + }) + return _obj + + diff --git a/src/otari/_client/models/catalog_model_detail.py b/src/otari/_client/models/catalog_model_detail.py new file mode 100644 index 00000000..b41c4be4 --- /dev/null +++ b/src/otari/_client/models/catalog_model_detail.py @@ -0,0 +1,225 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictFloat, StrictInt, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List, Optional, Union +from otari._client.models.catalog_capabilities import CatalogCapabilities +from otari._client.models.catalog_elsewhere import CatalogElsewhere +from otari._client.models.catalog_offering import CatalogOffering +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class CatalogModelDetail(BaseModel): + """ + One model with everything the detail page shows. + """ # noqa: E501 + also_available_from: List[CatalogElsewhere] + capabilities: CatalogCapabilities + context_window: Optional[StrictInt] = Field(default=None, description="The largest any offering serves.") + default_pricing: StrictBool = Field(description="Whether an unpriced model is metered at the genai-prices default.") + deprecated: Optional[StrictBool] = Field(default=False, description="True only when every offering with metadata says so.") + description: Optional[StrictStr] = Field(default=None, description="models.dev's, from the offering that named the model.") + discovered: StrictBool = Field(description="Whether any offering was discovered from its provider.") + family: Optional[StrictStr] = None + id: StrictStr = Field(description="The catalog id, vendor-qualified where the vendor is known: `z-ai/glm-5.3`, else the bare slug.") + input_modalities: List[StrictStr] + knowledge_cutoff: Optional[StrictStr] = None + max_output_tokens: Optional[StrictInt] = Field(default=None, description="The largest any offering serves.") + min_input_price_per_million: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="The cheapest offering's, at the comparison context where one was asked for.") + min_output_price_per_million: Optional[Union[StrictFloat, StrictInt]] = None + name: StrictStr + offering_count: StrictInt + offerings: List[CatalogOffering] + open_weights: Optional[StrictBool] = False + output_modalities: List[StrictStr] + price_sources: List[StrictStr] = Field(description="Which price lists the priced offerings came from, distinct and sorted.") + provider_count: StrictInt + providers: List[StrictStr] = Field(description="The provider instances offering it, sorted.") + release_date: Optional[StrictStr] = None + resolves_to: Optional[StrictStr] = Field(default=None, description="The offering `selector` resolves to.") + selector: Optional[StrictStr] = Field(default=None, description="The id as a selector: send it as `model` and the model's cheapest offering answers. Null until the gateway has indexed the catalog.") + selectors: List[StrictStr] = Field(description="Every offering's selector, so the list can be searched by one.") + unpriced_count: StrictInt = Field(description="How many offerings carry no price for this caller.") + vendor: Optional[StrictStr] + __properties: ClassVar[List[str]] = ["also_available_from", "capabilities", "context_window", "default_pricing", "deprecated", "description", "discovered", "family", "id", "input_modalities", "knowledge_cutoff", "max_output_tokens", "min_input_price_per_million", "min_output_price_per_million", "name", "offering_count", "offerings", "open_weights", "output_modalities", "price_sources", "provider_count", "providers", "release_date", "resolves_to", "selector", "selectors", "unpriced_count", "vendor"] + + @field_validator('price_sources') + def price_sources_validate_enum(cls, value): + """Validates the enum""" + for i in value: + if i not in set(['organization', 'deployment', 'defaults']): + raise ValueError("each list item must be one of ('organization', 'deployment', 'defaults')") + return value + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of CatalogModelDetail from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in also_available_from (list) + _items = [] + if self.also_available_from: + for _item_also_available_from in self.also_available_from: + if _item_also_available_from: + _items.append(_item_also_available_from.to_dict()) + _dict['also_available_from'] = _items + # override the default output from pydantic by calling `to_dict()` of capabilities + if self.capabilities: + _dict['capabilities'] = self.capabilities.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in offerings (list) + _items = [] + if self.offerings: + for _item_offerings in self.offerings: + if _item_offerings: + _items.append(_item_offerings.to_dict()) + _dict['offerings'] = _items + # set to None if context_window (nullable) is None + # and model_fields_set contains the field + if self.context_window is None and "context_window" in self.model_fields_set: + _dict['context_window'] = None + + # set to None if description (nullable) is None + # and model_fields_set contains the field + if self.description is None and "description" in self.model_fields_set: + _dict['description'] = None + + # set to None if family (nullable) is None + # and model_fields_set contains the field + if self.family is None and "family" in self.model_fields_set: + _dict['family'] = None + + # set to None if knowledge_cutoff (nullable) is None + # and model_fields_set contains the field + if self.knowledge_cutoff is None and "knowledge_cutoff" in self.model_fields_set: + _dict['knowledge_cutoff'] = None + + # set to None if max_output_tokens (nullable) is None + # and model_fields_set contains the field + if self.max_output_tokens is None and "max_output_tokens" in self.model_fields_set: + _dict['max_output_tokens'] = None + + # set to None if min_input_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.min_input_price_per_million is None and "min_input_price_per_million" in self.model_fields_set: + _dict['min_input_price_per_million'] = None + + # set to None if min_output_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.min_output_price_per_million is None and "min_output_price_per_million" in self.model_fields_set: + _dict['min_output_price_per_million'] = None + + # set to None if release_date (nullable) is None + # and model_fields_set contains the field + if self.release_date is None and "release_date" in self.model_fields_set: + _dict['release_date'] = None + + # set to None if resolves_to (nullable) is None + # and model_fields_set contains the field + if self.resolves_to is None and "resolves_to" in self.model_fields_set: + _dict['resolves_to'] = None + + # set to None if selector (nullable) is None + # and model_fields_set contains the field + if self.selector is None and "selector" in self.model_fields_set: + _dict['selector'] = None + + # set to None if vendor (nullable) is None + # and model_fields_set contains the field + if self.vendor is None and "vendor" in self.model_fields_set: + _dict['vendor'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of CatalogModelDetail from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "also_available_from": [CatalogElsewhere.from_dict(_item) for _item in obj["also_available_from"]] if obj.get("also_available_from") is not None else None, + "capabilities": CatalogCapabilities.from_dict(obj["capabilities"]) if obj.get("capabilities") is not None else None, + "context_window": obj.get("context_window"), + "default_pricing": obj.get("default_pricing"), + "deprecated": obj.get("deprecated") if obj.get("deprecated") is not None else False, + "description": obj.get("description"), + "discovered": obj.get("discovered"), + "family": obj.get("family"), + "id": obj.get("id"), + "input_modalities": obj.get("input_modalities"), + "knowledge_cutoff": obj.get("knowledge_cutoff"), + "max_output_tokens": obj.get("max_output_tokens"), + "min_input_price_per_million": obj.get("min_input_price_per_million"), + "min_output_price_per_million": obj.get("min_output_price_per_million"), + "name": obj.get("name"), + "offering_count": obj.get("offering_count"), + "offerings": [CatalogOffering.from_dict(_item) for _item in obj["offerings"]] if obj.get("offerings") is not None else None, + "open_weights": obj.get("open_weights") if obj.get("open_weights") is not None else False, + "output_modalities": obj.get("output_modalities"), + "price_sources": obj.get("price_sources"), + "provider_count": obj.get("provider_count"), + "providers": obj.get("providers"), + "release_date": obj.get("release_date"), + "resolves_to": obj.get("resolves_to"), + "selector": obj.get("selector"), + "selectors": obj.get("selectors"), + "unpriced_count": obj.get("unpriced_count"), + "vendor": obj.get("vendor") + }) + return _obj + + diff --git a/src/otari/_client/models/catalog_model_summary.py b/src/otari/_client/models/catalog_model_summary.py new file mode 100644 index 00000000..21a7d40e --- /dev/null +++ b/src/otari/_client/models/catalog_model_summary.py @@ -0,0 +1,203 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictFloat, StrictInt, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List, Optional, Union +from otari._client.models.catalog_capabilities import CatalogCapabilities +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class CatalogModelSummary(BaseModel): + """ + One model, as the list shows it. + """ # noqa: E501 + capabilities: CatalogCapabilities + context_window: Optional[StrictInt] = Field(default=None, description="The largest any offering serves.") + deprecated: Optional[StrictBool] = Field(default=False, description="True only when every offering with metadata says so.") + description: Optional[StrictStr] = Field(default=None, description="models.dev's, from the offering that named the model.") + discovered: StrictBool = Field(description="Whether any offering was discovered from its provider.") + family: Optional[StrictStr] = None + id: StrictStr = Field(description="The catalog id, vendor-qualified where the vendor is known: `z-ai/glm-5.3`, else the bare slug.") + input_modalities: List[StrictStr] + knowledge_cutoff: Optional[StrictStr] = None + max_output_tokens: Optional[StrictInt] = Field(default=None, description="The largest any offering serves.") + min_input_price_per_million: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="The cheapest offering's, at the comparison context where one was asked for.") + min_output_price_per_million: Optional[Union[StrictFloat, StrictInt]] = None + name: StrictStr + offering_count: StrictInt + open_weights: Optional[StrictBool] = False + output_modalities: List[StrictStr] + price_sources: List[StrictStr] = Field(description="Which price lists the priced offerings came from, distinct and sorted.") + provider_count: StrictInt + providers: List[StrictStr] = Field(description="The provider instances offering it, sorted.") + release_date: Optional[StrictStr] = None + resolves_to: Optional[StrictStr] = Field(default=None, description="The offering `selector` resolves to.") + selector: Optional[StrictStr] = Field(default=None, description="The id as a selector: send it as `model` and the model's cheapest offering answers. Null until the gateway has indexed the catalog.") + selectors: List[StrictStr] = Field(description="Every offering's selector, so the list can be searched by one.") + unpriced_count: StrictInt = Field(description="How many offerings carry no price for this caller.") + vendor: Optional[StrictStr] + __properties: ClassVar[List[str]] = ["capabilities", "context_window", "deprecated", "description", "discovered", "family", "id", "input_modalities", "knowledge_cutoff", "max_output_tokens", "min_input_price_per_million", "min_output_price_per_million", "name", "offering_count", "open_weights", "output_modalities", "price_sources", "provider_count", "providers", "release_date", "resolves_to", "selector", "selectors", "unpriced_count", "vendor"] + + @field_validator('price_sources') + def price_sources_validate_enum(cls, value): + """Validates the enum""" + for i in value: + if i not in set(['organization', 'deployment', 'defaults']): + raise ValueError("each list item must be one of ('organization', 'deployment', 'defaults')") + return value + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of CatalogModelSummary from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of capabilities + if self.capabilities: + _dict['capabilities'] = self.capabilities.to_dict() + # set to None if context_window (nullable) is None + # and model_fields_set contains the field + if self.context_window is None and "context_window" in self.model_fields_set: + _dict['context_window'] = None + + # set to None if description (nullable) is None + # and model_fields_set contains the field + if self.description is None and "description" in self.model_fields_set: + _dict['description'] = None + + # set to None if family (nullable) is None + # and model_fields_set contains the field + if self.family is None and "family" in self.model_fields_set: + _dict['family'] = None + + # set to None if knowledge_cutoff (nullable) is None + # and model_fields_set contains the field + if self.knowledge_cutoff is None and "knowledge_cutoff" in self.model_fields_set: + _dict['knowledge_cutoff'] = None + + # set to None if max_output_tokens (nullable) is None + # and model_fields_set contains the field + if self.max_output_tokens is None and "max_output_tokens" in self.model_fields_set: + _dict['max_output_tokens'] = None + + # set to None if min_input_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.min_input_price_per_million is None and "min_input_price_per_million" in self.model_fields_set: + _dict['min_input_price_per_million'] = None + + # set to None if min_output_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.min_output_price_per_million is None and "min_output_price_per_million" in self.model_fields_set: + _dict['min_output_price_per_million'] = None + + # set to None if release_date (nullable) is None + # and model_fields_set contains the field + if self.release_date is None and "release_date" in self.model_fields_set: + _dict['release_date'] = None + + # set to None if resolves_to (nullable) is None + # and model_fields_set contains the field + if self.resolves_to is None and "resolves_to" in self.model_fields_set: + _dict['resolves_to'] = None + + # set to None if selector (nullable) is None + # and model_fields_set contains the field + if self.selector is None and "selector" in self.model_fields_set: + _dict['selector'] = None + + # set to None if vendor (nullable) is None + # and model_fields_set contains the field + if self.vendor is None and "vendor" in self.model_fields_set: + _dict['vendor'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of CatalogModelSummary from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "capabilities": CatalogCapabilities.from_dict(obj["capabilities"]) if obj.get("capabilities") is not None else None, + "context_window": obj.get("context_window"), + "deprecated": obj.get("deprecated") if obj.get("deprecated") is not None else False, + "description": obj.get("description"), + "discovered": obj.get("discovered"), + "family": obj.get("family"), + "id": obj.get("id"), + "input_modalities": obj.get("input_modalities"), + "knowledge_cutoff": obj.get("knowledge_cutoff"), + "max_output_tokens": obj.get("max_output_tokens"), + "min_input_price_per_million": obj.get("min_input_price_per_million"), + "min_output_price_per_million": obj.get("min_output_price_per_million"), + "name": obj.get("name"), + "offering_count": obj.get("offering_count"), + "open_weights": obj.get("open_weights") if obj.get("open_weights") is not None else False, + "output_modalities": obj.get("output_modalities"), + "price_sources": obj.get("price_sources"), + "provider_count": obj.get("provider_count"), + "providers": obj.get("providers"), + "release_date": obj.get("release_date"), + "resolves_to": obj.get("resolves_to"), + "selector": obj.get("selector"), + "selectors": obj.get("selectors"), + "unpriced_count": obj.get("unpriced_count"), + "vendor": obj.get("vendor") + }) + return _obj + + diff --git a/src/otari/_client/models/catalog_offering.py b/src/otari/_client/models/catalog_offering.py new file mode 100644 index 00000000..f69c8242 --- /dev/null +++ b/src/otari/_client/models/catalog_offering.py @@ -0,0 +1,191 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictFloat, StrictInt, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List, Optional, Union +from otari._client.models.model_pricing_info import ModelPricingInfo +from otari._client.models.offering_usage import OfferingUsage +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class CatalogOffering(BaseModel): + """ + One way this deployment can call a model: a selector on a provider. + """ # noqa: E501 + context_window: Optional[StrictInt] = None + credential: StrictStr = Field(description="Whose key serves it: `deployment` for a `providers:` instance the operator configured, `organization` for a key the viewer's organization holds.") + discovered: StrictBool = Field(description="Whether the provider itself reported this model.") + max_output_tokens: Optional[StrictInt] = None + metadata_input_price_per_million: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="What models.dev lists this provider charging, for a cross-check. Not billed from: two independent datasets disagreeing is the cheapest stale-price detector there is.") + metadata_output_price_per_million: Optional[Union[StrictFloat, StrictInt]] = None + price_reference: Optional[StrictStr] = Field(default=None, description="For a default, the genai-prices `provider:model` entry that matched; the selector otherwise.") + price_source: Optional[StrictStr] = Field(default=None, description="Which price list `pricing` came from, for this viewer: the organization's own override, the deployment's stored row, or the genai-prices defaults. Null when nothing prices it.") + pricing: Optional[ModelPricingInfo] = None + provider: StrictStr = Field(description="The provider instance the selector names.") + provider_type: StrictStr = Field(description="The any-llm implementation behind the instance.") + quantization: Optional[StrictStr] = Field(default=None, description="From the provider's id, when it names one.") + selector: StrictStr = Field(description="What to send as `model`, in `instance:model` form.") + short_selector: Optional[StrictStr] = Field(default=None, description="A shorter spelling the gateway also accepts: the instance with the model's cleaned id (`fireworks:gpt-oss-120b`). Null where two offerings on the instance would share it, or until the gateway has indexed the catalog.") + usage_30d: Optional[OfferingUsage] = None + __properties: ClassVar[List[str]] = ["context_window", "credential", "discovered", "max_output_tokens", "metadata_input_price_per_million", "metadata_output_price_per_million", "price_reference", "price_source", "pricing", "provider", "provider_type", "quantization", "selector", "short_selector", "usage_30d"] + + @field_validator('credential') + def credential_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['deployment', 'organization', 'hosted']): + raise ValueError("must be one of enum values ('deployment', 'organization', 'hosted')") + return value + + @field_validator('price_source') + def price_source_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['organization', 'deployment', 'defaults']): + raise ValueError("must be one of enum values ('organization', 'deployment', 'defaults')") + return value + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of CatalogOffering from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of pricing + if self.pricing: + _dict['pricing'] = self.pricing.to_dict() + # override the default output from pydantic by calling `to_dict()` of usage_30d + if self.usage_30d: + _dict['usage_30d'] = self.usage_30d.to_dict() + # set to None if context_window (nullable) is None + # and model_fields_set contains the field + if self.context_window is None and "context_window" in self.model_fields_set: + _dict['context_window'] = None + + # set to None if max_output_tokens (nullable) is None + # and model_fields_set contains the field + if self.max_output_tokens is None and "max_output_tokens" in self.model_fields_set: + _dict['max_output_tokens'] = None + + # set to None if metadata_input_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.metadata_input_price_per_million is None and "metadata_input_price_per_million" in self.model_fields_set: + _dict['metadata_input_price_per_million'] = None + + # set to None if metadata_output_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.metadata_output_price_per_million is None and "metadata_output_price_per_million" in self.model_fields_set: + _dict['metadata_output_price_per_million'] = None + + # set to None if price_reference (nullable) is None + # and model_fields_set contains the field + if self.price_reference is None and "price_reference" in self.model_fields_set: + _dict['price_reference'] = None + + # set to None if price_source (nullable) is None + # and model_fields_set contains the field + if self.price_source is None and "price_source" in self.model_fields_set: + _dict['price_source'] = None + + # set to None if pricing (nullable) is None + # and model_fields_set contains the field + if self.pricing is None and "pricing" in self.model_fields_set: + _dict['pricing'] = None + + # set to None if quantization (nullable) is None + # and model_fields_set contains the field + if self.quantization is None and "quantization" in self.model_fields_set: + _dict['quantization'] = None + + # set to None if short_selector (nullable) is None + # and model_fields_set contains the field + if self.short_selector is None and "short_selector" in self.model_fields_set: + _dict['short_selector'] = None + + # set to None if usage_30d (nullable) is None + # and model_fields_set contains the field + if self.usage_30d is None and "usage_30d" in self.model_fields_set: + _dict['usage_30d'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of CatalogOffering from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "context_window": obj.get("context_window"), + "credential": obj.get("credential"), + "discovered": obj.get("discovered"), + "max_output_tokens": obj.get("max_output_tokens"), + "metadata_input_price_per_million": obj.get("metadata_input_price_per_million"), + "metadata_output_price_per_million": obj.get("metadata_output_price_per_million"), + "price_reference": obj.get("price_reference"), + "price_source": obj.get("price_source"), + "pricing": ModelPricingInfo.from_dict(obj["pricing"]) if obj.get("pricing") is not None else None, + "provider": obj.get("provider"), + "provider_type": obj.get("provider_type"), + "quantization": obj.get("quantization"), + "selector": obj.get("selector"), + "short_selector": obj.get("short_selector"), + "usage_30d": OfferingUsage.from_dict(obj["usage_30d"]) if obj.get("usage_30d") is not None else None + }) + return _obj + + diff --git a/src/otari/_client/models/catalog_response.py b/src/otari/_client/models/catalog_response.py new file mode 100644 index 00000000..8ad87de3 --- /dev/null +++ b/src/otari/_client/models/catalog_response.py @@ -0,0 +1,108 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from datetime import datetime +from pydantic import BaseModel, ConfigDict, Field, StrictBool +from typing import Any, ClassVar, Dict, List, Optional +from otari._client.models.catalog_model_summary import CatalogModelSummary +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class CatalogResponse(BaseModel): + """ + The grouped catalog, and the facts a reader needs to interpret its prices. + """ # noqa: E501 + default_pricing: StrictBool = Field(description="Whether an unpriced model is metered at the genai-prices default.") + defaults_as_of: Optional[datetime] = Field(description="When the accepted genai-prices snapshot was taken. Null while the bundled dataset serves.") + metadata_available: StrictBool = Field(description="False when models.dev could not be read; descriptions are then absent.") + models: List[CatalogModelSummary] + __properties: ClassVar[List[str]] = ["default_pricing", "defaults_as_of", "metadata_available", "models"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of CatalogResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in models (list) + _items = [] + if self.models: + for _item_models in self.models: + if _item_models: + _items.append(_item_models.to_dict()) + _dict['models'] = _items + # set to None if defaults_as_of (nullable) is None + # and model_fields_set contains the field + if self.defaults_as_of is None and "defaults_as_of" in self.model_fields_set: + _dict['defaults_as_of'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of CatalogResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "default_pricing": obj.get("default_pricing"), + "defaults_as_of": obj.get("defaults_as_of"), + "metadata_available": obj.get("metadata_available"), + "models": [CatalogModelSummary.from_dict(_item) for _item in obj["models"]] if obj.get("models") is not None else None + }) + return _obj + + diff --git a/src/otari/_client/models/deployment_bootstrap.py b/src/otari/_client/models/deployment_bootstrap.py index 06b3ade0..c259a7c7 100644 --- a/src/otari/_client/models/deployment_bootstrap.py +++ b/src/otari/_client/models/deployment_bootstrap.py @@ -37,11 +37,12 @@ class DeploymentBootstrap(BaseModel): open_signup: StrictBool = Field(description="Whether POST /api/v1/auth/signup creates an account for an address nobody has added yet, each with an organization of its own, or only lets an address an admin already put on the roster set its password. The signup page reads as registration or as claiming an invitation accordingly, and the sign-in screen links to it with the wording that matches. False for a hybrid gateway, which holds no identities.") passkeys_ready: StrictBool = Field(description="Whether this deployment can run a passkey ceremony at all: it has a relying-party ID (webauthn_rp_id, or derived from public_base_url) and an origin to serve one from. Distinct from 'passkey' in sign_in_methods, which is narrower and answers whether a registered passkey could sign somebody in *right now*: an operator with none yet needs this one, or the page that registers the first would be hidden from them. False for a hybrid gateway, which issues no session of its own.") privacy_url: Optional[StrictStr] = Field(description="Where this deployment's privacy notice lives. Set, the account menu's Data & Privacy row links to it; null, no address is configured and that row stays disabled, carrying the standing note that there is nothing to configure there yet. A link target an operator configured, validated at startup as an absolute http(s) URL carrying no credential, since this response is unauthenticated.") + public_catalog: Optional[StrictBool] = Field(default=False, description="Whether the model catalog is served to a visitor with no session: GET /api/v1/catalog/models answers anonymously and the dashboard renders Models ahead of sign-in. False for a hybrid gateway, which serves no catalog of its own.") session_type: StrictStr = Field(description="The kind of session this deployment issues, not whether the caller holds one. 'local_operator' is the standalone operator sign-in (see sign_in_methods for which credential it currently accepts), 'hosted_user' an otari.ai account, and 'none' a deployment that issues no management session at all.") sign_in_methods: List[StrictStr] = Field(description="How POST /api/v1/auth/session may be authenticated right now, sorted. 'master_key' is the first-boot credential and is offered until the operator identity has a password, which is what claiming the deployment means; past that it stays the credential for the management API but is no longer a dashboard login. 'password' is offered while any active identity holds one, which is not the same question and not always the later half of it: a member can hold a password on a deployment whose operator never claimed it, so both typed credentials can appear together. 'passkey' appears alongside either when this deployment is configured for WebAuthn and holds at least one passkey that its current relying-party ID can assert. Empty for a hybrid gateway, which issues no session. The login page renders from this rather than trying a credential to find out.") surfaces: List[StrictStr] = Field(description="Management API groups this deployment serves, sorted, which is what its dashboard pages gate on. Named surfaces, not capabilities: capability is otari.ai's word for the entitlement (licensing) axis, and this is the deployment (topology) axis. Empty for a hybrid gateway.") terms_url: Optional[StrictStr] = Field(description="Where this deployment's terms of service live. Set, the account menu carries a Terms of service row pointing at them; null, no address is configured and the menu carries no such row. A link target an operator configured, validated at startup as an absolute http(s) URL carrying no credential, since this response is unauthenticated.") - __properties: ClassVar[List[str]] = ["data_plane_url", "deployment_type", "docs_url", "mail_ready", "maintenance_mode", "management_url", "oauth_providers", "open_signup", "passkeys_ready", "privacy_url", "session_type", "sign_in_methods", "surfaces", "terms_url"] + __properties: ClassVar[List[str]] = ["data_plane_url", "deployment_type", "docs_url", "mail_ready", "maintenance_mode", "management_url", "oauth_providers", "open_signup", "passkeys_ready", "privacy_url", "public_catalog", "session_type", "sign_in_methods", "surfaces", "terms_url"] @field_validator('deployment_type') def deployment_type_validate_enum(cls, value): @@ -151,6 +152,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "open_signup": obj.get("open_signup"), "passkeys_ready": obj.get("passkeys_ready"), "privacy_url": obj.get("privacy_url"), + "public_catalog": obj.get("public_catalog") if obj.get("public_catalog") is not None else False, "session_type": obj.get("session_type"), "sign_in_methods": obj.get("sign_in_methods"), "surfaces": obj.get("surfaces"), diff --git a/src/otari/_client/models/model_object.py b/src/otari/_client/models/model_object.py index 9c900234..bba2e48e 100644 --- a/src/otari/_client/models/model_object.py +++ b/src/otari/_client/models/model_object.py @@ -17,7 +17,7 @@ import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, StrictBool, StrictInt, StrictStr from typing import Any, ClassVar, Dict, List, Optional from otari._client.models.model_pricing_info import ModelPricingInfo from typing import Optional, Set @@ -30,12 +30,13 @@ class ModelObject(BaseModel): """ # noqa: E501 context_window: Optional[StrictInt] = None created: StrictInt + deployment_managed: Optional[StrictBool] = False id: StrictStr object: Optional[StrictStr] = 'model' owned_by: StrictStr pricing: Optional[ModelPricingInfo] = None pricing_source: Optional[StrictStr] = 'none' - __properties: ClassVar[List[str]] = ["context_window", "created", "id", "object", "owned_by", "pricing", "pricing_source"] + __properties: ClassVar[List[str]] = ["context_window", "created", "deployment_managed", "id", "object", "owned_by", "pricing", "pricing_source"] model_config = ConfigDict( validate_by_name=True, @@ -103,6 +104,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "context_window": obj.get("context_window"), "created": obj.get("created"), + "deployment_managed": obj.get("deployment_managed") if obj.get("deployment_managed") is not None else False, "id": obj.get("id"), "object": obj.get("object") if obj.get("object") is not None else 'model', "owned_by": obj.get("owned_by"), diff --git a/src/otari/_client/models/model_pricing_info.py b/src/otari/_client/models/model_pricing_info.py index a8f8107b..7ad70159 100644 --- a/src/otari/_client/models/model_pricing_info.py +++ b/src/otari/_client/models/model_pricing_info.py @@ -17,7 +17,7 @@ import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, StrictFloat, StrictInt +from pydantic import BaseModel, ConfigDict, StrictFloat, StrictInt, StrictStr from typing import Any, ClassVar, Dict, List, Optional, Union from otari._client.models.pricing_tiers_inner import PricingTiersInner from typing import Optional, Set @@ -34,7 +34,8 @@ class ModelPricingInfo(BaseModel): input_price_per_million: Union[StrictFloat, StrictInt] output_price_per_million: Union[StrictFloat, StrictInt] pricing_tiers: Optional[List[PricingTiersInner]] = None - __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "input_price_per_million", "output_price_per_million", "pricing_tiers"] + unit: Optional[StrictStr] = 'tokens' + __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "input_price_per_million", "output_price_per_million", "pricing_tiers", "unit"] model_config = ConfigDict( validate_by_name=True, @@ -114,7 +115,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "cache_write_price_per_million": obj.get("cache_write_price_per_million"), "input_price_per_million": obj.get("input_price_per_million"), "output_price_per_million": obj.get("output_price_per_million"), - "pricing_tiers": [PricingTiersInner.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None + "pricing_tiers": [PricingTiersInner.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None, + "unit": obj.get("unit") if obj.get("unit") is not None else 'tokens' }) return _obj diff --git a/src/otari/_client/models/offering_usage.py b/src/otari/_client/models/offering_usage.py new file mode 100644 index 00000000..75cd7670 --- /dev/null +++ b/src/otari/_client/models/offering_usage.py @@ -0,0 +1,108 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt +from typing import Any, ClassVar, Dict, List, Optional, Union +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class OfferingUsage(BaseModel): + """ + What the viewer's organization actually paid for one offering, last 30 days. The listed rate is what a token costs; this is what the tokens cost, which is lower wherever prompt caching hit. Absent for a visitor and for an offering the organization never called. + """ # noqa: E501 + cache_hit_rate: Optional[Union[StrictFloat, StrictInt]] = Field(description="Cache-read tokens over prompt tokens. Null when no prompt tokens.") + cache_read_tokens: StrictInt + effective_price_per_million: Optional[Union[StrictFloat, StrictInt]] = Field(description="Spend over every token served, per million. Null when no tokens were served.") + requests: StrictInt + spend_usd: Union[StrictFloat, StrictInt] + total_tokens: StrictInt + __properties: ClassVar[List[str]] = ["cache_hit_rate", "cache_read_tokens", "effective_price_per_million", "requests", "spend_usd", "total_tokens"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of OfferingUsage from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if cache_hit_rate (nullable) is None + # and model_fields_set contains the field + if self.cache_hit_rate is None and "cache_hit_rate" in self.model_fields_set: + _dict['cache_hit_rate'] = None + + # set to None if effective_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.effective_price_per_million is None and "effective_price_per_million" in self.model_fields_set: + _dict['effective_price_per_million'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of OfferingUsage from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "cache_hit_rate": obj.get("cache_hit_rate"), + "cache_read_tokens": obj.get("cache_read_tokens"), + "effective_price_per_million": obj.get("effective_price_per_million"), + "requests": obj.get("requests"), + "spend_usd": obj.get("spend_usd"), + "total_tokens": obj.get("total_tokens") + }) + return _obj + + diff --git a/src/otari/_client/models/organization_model_pricing_create.py b/src/otari/_client/models/organization_model_pricing_create.py index 9e8b133d..c90c7e7b 100644 --- a/src/otari/_client/models/organization_model_pricing_create.py +++ b/src/otari/_client/models/organization_model_pricing_create.py @@ -18,7 +18,7 @@ import json from datetime import datetime -from pydantic import BaseModel, ConfigDict, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional, Union from typing_extensions import Annotated from otari._client.models.pricing_tier import PricingTier @@ -39,7 +39,8 @@ class OrganizationModelPricingCreate(BaseModel): model_key: Annotated[str, Field(min_length=1, strict=True, max_length=255)] = Field(description="Model identifier in 'provider:model' form, matching the key the deployment price list uses. A provider instance name is valid here ('home_lab:llama-3'), because pricing keys on the instance a request resolves to.") output_price_per_million: Union[Annotated[float, Field(strict=True, ge=0.0)], Annotated[int, Field(strict=True, ge=0)]] = Field(description="Price per 1M output tokens") pricing_tiers: Optional[List[PricingTier]] = Field(default=None, description="Whole-request context thresholds. Fields omitted by a tier inherit the base rate.") - __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "effective_from", "effective_to", "input_price_per_million", "model_key", "output_price_per_million", "pricing_tiers"] + unit: Optional[StrictStr] = Field(default='tokens', description="What the rates are per: tokens for a model, requests or images for a non-token endpoint.") + __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "effective_from", "effective_to", "input_price_per_million", "model_key", "output_price_per_million", "pricing_tiers", "unit"] @field_validator('model_key') def model_key_validate_regular_expression(cls, value): @@ -51,6 +52,16 @@ def model_key_validate_regular_expression(cls, value): raise ValueError(r"must validate the regular expression /^[^\s:\/]+[:\/][^\s]+$/") return value + @field_validator('unit') + def unit_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['tokens', 'requests', 'images']): + raise ValueError("must be one of enum values ('tokens', 'requests', 'images')") + return value + model_config = ConfigDict( validate_by_name=True, validate_by_alias=True, @@ -147,7 +158,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "input_price_per_million": obj.get("input_price_per_million"), "model_key": obj.get("model_key"), "output_price_per_million": obj.get("output_price_per_million"), - "pricing_tiers": [PricingTier.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None + "pricing_tiers": [PricingTier.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None, + "unit": obj.get("unit") if obj.get("unit") is not None else 'tokens' }) return _obj diff --git a/src/otari/_client/models/organization_model_pricing_public.py b/src/otari/_client/models/organization_model_pricing_public.py index ae596dd5..91290d21 100644 --- a/src/otari/_client/models/organization_model_pricing_public.py +++ b/src/otari/_client/models/organization_model_pricing_public.py @@ -42,8 +42,9 @@ class OrganizationModelPricingPublic(BaseModel): organization_id: UUID output_price_per_million: Union[StrictFloat, StrictInt] pricing_tiers: List[PricingTier] + unit: StrictStr updated_at: datetime - __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "created_at", "effective_from", "effective_to", "id", "input_price_per_million", "model_key", "organization_id", "output_price_per_million", "pricing_tiers", "updated_at"] + __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "created_at", "effective_from", "effective_to", "id", "input_price_per_million", "model_key", "organization_id", "output_price_per_million", "pricing_tiers", "unit", "updated_at"] model_config = ConfigDict( validate_by_name=True, @@ -135,6 +136,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "organization_id": obj.get("organization_id"), "output_price_per_million": obj.get("output_price_per_million"), "pricing_tiers": [PricingTier.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None, + "unit": obj.get("unit"), "updated_at": obj.get("updated_at") }) return _obj diff --git a/src/otari/_client/models/organization_model_pricing_update.py b/src/otari/_client/models/organization_model_pricing_update.py index e2279ef7..fd12099e 100644 --- a/src/otari/_client/models/organization_model_pricing_update.py +++ b/src/otari/_client/models/organization_model_pricing_update.py @@ -18,7 +18,7 @@ import json from datetime import datetime -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional, Union from typing_extensions import Annotated from otari._client.models.pricing_tier import PricingTier @@ -38,7 +38,18 @@ class OrganizationModelPricingUpdate(BaseModel): input_price_per_million: Union[Annotated[float, Field(strict=True, ge=0.0)], Annotated[int, Field(strict=True, ge=0)]] = Field(description="Price per 1M input tokens") output_price_per_million: Union[Annotated[float, Field(strict=True, ge=0.0)], Annotated[int, Field(strict=True, ge=0)]] = Field(description="Price per 1M output tokens") pricing_tiers: Optional[List[PricingTier]] = Field(default=None, description="Whole-request context thresholds. Fields omitted by a tier inherit the base rate.") - __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "effective_from", "effective_to", "input_price_per_million", "output_price_per_million", "pricing_tiers"] + unit: Optional[StrictStr] = Field(default='tokens', description="What the rates are per: tokens for a model, requests or images for a non-token endpoint.") + __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "effective_from", "effective_to", "input_price_per_million", "output_price_per_million", "pricing_tiers", "unit"] + + @field_validator('unit') + def unit_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['tokens', 'requests', 'images']): + raise ValueError("must be one of enum values ('tokens', 'requests', 'images')") + return value model_config = ConfigDict( validate_by_name=True, @@ -130,7 +141,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "effective_to": obj.get("effective_to"), "input_price_per_million": obj.get("input_price_per_million"), "output_price_per_million": obj.get("output_price_per_million"), - "pricing_tiers": [PricingTier.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None + "pricing_tiers": [PricingTier.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None, + "unit": obj.get("unit") if obj.get("unit") is not None else 'tokens' }) return _obj diff --git a/src/otari/_client/models/pricing_drift_row.py b/src/otari/_client/models/pricing_drift_row.py new file mode 100644 index 00000000..54402ffc --- /dev/null +++ b/src/otari/_client/models/pricing_drift_row.py @@ -0,0 +1,138 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr +from typing import Any, ClassVar, Dict, List, Optional, Union +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class PricingDriftRow(BaseModel): + """ + A stored deployment rate beside the default it shadows. + """ # noqa: E501 + default_input_price_per_million: Optional[Union[StrictFloat, StrictInt]] = Field(description="What genai-prices would meter this key at today. Null when the dataset does not know it.") + default_output_price_per_million: Optional[Union[StrictFloat, StrictInt]] + default_reference: Optional[StrictStr] = Field(description="The genai-prices entry the default came from.") + effective_at: StrictStr + input_delta_percent: Optional[Union[StrictFloat, StrictInt]] = Field(description="(stored - default) / default, as a percentage.") + input_price_per_million: Union[StrictFloat, StrictInt] + model_key: StrictStr + origin: Optional[StrictStr] + output_delta_percent: Optional[Union[StrictFloat, StrictInt]] + output_price_per_million: Union[StrictFloat, StrictInt] + unit: StrictStr + __properties: ClassVar[List[str]] = ["default_input_price_per_million", "default_output_price_per_million", "default_reference", "effective_at", "input_delta_percent", "input_price_per_million", "model_key", "origin", "output_delta_percent", "output_price_per_million", "unit"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of PricingDriftRow from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if default_input_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.default_input_price_per_million is None and "default_input_price_per_million" in self.model_fields_set: + _dict['default_input_price_per_million'] = None + + # set to None if default_output_price_per_million (nullable) is None + # and model_fields_set contains the field + if self.default_output_price_per_million is None and "default_output_price_per_million" in self.model_fields_set: + _dict['default_output_price_per_million'] = None + + # set to None if default_reference (nullable) is None + # and model_fields_set contains the field + if self.default_reference is None and "default_reference" in self.model_fields_set: + _dict['default_reference'] = None + + # set to None if input_delta_percent (nullable) is None + # and model_fields_set contains the field + if self.input_delta_percent is None and "input_delta_percent" in self.model_fields_set: + _dict['input_delta_percent'] = None + + # set to None if origin (nullable) is None + # and model_fields_set contains the field + if self.origin is None and "origin" in self.model_fields_set: + _dict['origin'] = None + + # set to None if output_delta_percent (nullable) is None + # and model_fields_set contains the field + if self.output_delta_percent is None and "output_delta_percent" in self.model_fields_set: + _dict['output_delta_percent'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PricingDriftRow from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "default_input_price_per_million": obj.get("default_input_price_per_million"), + "default_output_price_per_million": obj.get("default_output_price_per_million"), + "default_reference": obj.get("default_reference"), + "effective_at": obj.get("effective_at"), + "input_delta_percent": obj.get("input_delta_percent"), + "input_price_per_million": obj.get("input_price_per_million"), + "model_key": obj.get("model_key"), + "origin": obj.get("origin"), + "output_delta_percent": obj.get("output_delta_percent"), + "output_price_per_million": obj.get("output_price_per_million"), + "unit": obj.get("unit") + }) + return _obj + + diff --git a/src/otari/_client/models/pricing_response.py b/src/otari/_client/models/pricing_response.py index ad1caadd..364978b2 100644 --- a/src/otari/_client/models/pricing_response.py +++ b/src/otari/_client/models/pricing_response.py @@ -17,7 +17,7 @@ import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, StrictFloat, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr from typing import Any, ClassVar, Dict, List, Optional, Union from otari._client.models.pricing_tier import PricingTier from typing import Optional, Set @@ -35,10 +35,12 @@ class PricingResponse(BaseModel): effective_at: StrictStr input_price_per_million: Union[StrictFloat, StrictInt] model_key: StrictStr + origin: Optional[StrictStr] = Field(description="Which writer set this row: config, api, or migration. Null when recorded before origins were.") output_price_per_million: Union[StrictFloat, StrictInt] pricing_tiers: List[PricingTier] + unit: StrictStr = Field(description="What the rates are per: tokens, requests, or images.") updated_at: StrictStr - __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "created_at", "effective_at", "input_price_per_million", "model_key", "output_price_per_million", "pricing_tiers", "updated_at"] + __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "created_at", "effective_at", "input_price_per_million", "model_key", "origin", "output_price_per_million", "pricing_tiers", "unit", "updated_at"] model_config = ConfigDict( validate_by_name=True, @@ -101,6 +103,11 @@ def to_dict(self) -> Dict[str, Any]: if self.cache_write_price_per_million is None and "cache_write_price_per_million" in self.model_fields_set: _dict['cache_write_price_per_million'] = None + # set to None if origin (nullable) is None + # and model_fields_set contains the field + if self.origin is None and "origin" in self.model_fields_set: + _dict['origin'] = None + return _dict @classmethod @@ -120,8 +127,10 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "effective_at": obj.get("effective_at"), "input_price_per_million": obj.get("input_price_per_million"), "model_key": obj.get("model_key"), + "origin": obj.get("origin"), "output_price_per_million": obj.get("output_price_per_million"), "pricing_tiers": [PricingTier.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None, + "unit": obj.get("unit"), "updated_at": obj.get("updated_at") }) return _obj diff --git a/src/otari/_client/models/selector_index_response.py b/src/otari/_client/models/selector_index_response.py new file mode 100644 index 00000000..8447e9f2 --- /dev/null +++ b/src/otari/_client/models/selector_index_response.py @@ -0,0 +1,92 @@ +# coding: utf-8 + +""" + otari + + Otari, an OpenAI-compatible LLM gateway with API key management + + The version of the OpenAPI document: 0.0.0-dev + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictInt +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class SelectorIndexResponse(BaseModel): + """ + What the rebuilt index knows. + """ # noqa: E501 + models: StrictInt = Field(description="Slugs that resolve to an offering.") + offerings: StrictInt = Field(description="Selectors the deployment serves.") + short_selectors: StrictInt = Field(description="Offerings with an unambiguous short spelling.") + __properties: ClassVar[List[str]] = ["models", "offerings", "short_selectors"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of SelectorIndexResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of SelectorIndexResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "models": obj.get("models"), + "offerings": obj.get("offerings"), + "short_selectors": obj.get("short_selectors") + }) + return _obj + + diff --git a/src/otari/_client/models/set_pricing_request.py b/src/otari/_client/models/set_pricing_request.py index 56aac288..3776c5ca 100644 --- a/src/otari/_client/models/set_pricing_request.py +++ b/src/otari/_client/models/set_pricing_request.py @@ -18,7 +18,7 @@ import json from datetime import datetime -from pydantic import BaseModel, ConfigDict, Field, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional, Union from typing_extensions import Annotated from otari._client.models.pricing_tier import PricingTier @@ -38,7 +38,18 @@ class SetPricingRequest(BaseModel): model_key: StrictStr = Field(description="Model identifier in format 'provider:model'") output_price_per_million: Union[Annotated[float, Field(strict=True, ge=0.0)], Annotated[int, Field(strict=True, ge=0)]] = Field(description="Price per 1M output tokens") pricing_tiers: Optional[List[PricingTier]] = Field(default=None, description="Whole-request context thresholds. Fields omitted by a tier inherit the base rate.") - __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "effective_at", "input_price_per_million", "model_key", "output_price_per_million", "pricing_tiers"] + unit: Optional[StrictStr] = Field(default='tokens', description="What the rates are per: 'tokens' for a model, 'requests' for a gateway-run tool or a moderation call (USD per million requests), 'images' for image generation.") + __properties: ClassVar[List[str]] = ["cache_read_price_per_million", "cache_write_1h_price_per_million", "cache_write_price_per_million", "effective_at", "input_price_per_million", "model_key", "output_price_per_million", "pricing_tiers", "unit"] + + @field_validator('unit') + def unit_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['tokens', 'requests', 'images']): + raise ValueError("must be one of enum values ('tokens', 'requests', 'images')") + return value model_config = ConfigDict( validate_by_name=True, @@ -130,7 +141,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "input_price_per_million": obj.get("input_price_per_million"), "model_key": obj.get("model_key"), "output_price_per_million": obj.get("output_price_per_million"), - "pricing_tiers": [PricingTier.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None + "pricing_tiers": [PricingTier.from_dict(_item) for _item in obj["pricing_tiers"]] if obj.get("pricing_tiers") is not None else None, + "unit": obj.get("unit") if obj.get("unit") is not None else 'tokens' }) return _obj diff --git a/src/otari/_client/models/update_settings_request.py b/src/otari/_client/models/update_settings_request.py index 17f58e76..d13ff22e 100644 --- a/src/otari/_client/models/update_settings_request.py +++ b/src/otari/_client/models/update_settings_request.py @@ -37,13 +37,25 @@ class UpdateSettingsRequest(BaseModel): model_discovery_timeout_seconds: Optional[Union[Annotated[float, Field(strict=True, gt=0.0)], Annotated[int, Field(strict=True, gt=0)]]] = None models_dev_cache_ttl_seconds: Optional[Annotated[int, Field(strict=True, ge=0)]] = None models_dev_metadata: Optional[StrictBool] = None + pricing_refresh: Optional[StrictStr] = None + public_catalog: Optional[StrictBool] = None reject_user_mismatch: Optional[StrictBool] = None require_pricing: Optional[StrictBool] = None stream_missing_usage_policy: Optional[StrictStr] = None vision_describe_max_tokens: Optional[Annotated[int, Field(strict=True, gt=0)]] = None vision_describe_model: Optional[StrictStr] = None vision_strategy: Optional[StrictStr] = None - __properties: ClassVar[List[str]] = ["budget_estimate_default_output_tokens", "default_pricing", "file_understanding_enabled", "model_cache_ttl_seconds", "model_discovery", "model_discovery_negative_ttl_seconds", "model_discovery_timeout_seconds", "models_dev_cache_ttl_seconds", "models_dev_metadata", "reject_user_mismatch", "require_pricing", "stream_missing_usage_policy", "vision_describe_max_tokens", "vision_describe_model", "vision_strategy"] + __properties: ClassVar[List[str]] = ["budget_estimate_default_output_tokens", "default_pricing", "file_understanding_enabled", "model_cache_ttl_seconds", "model_discovery", "model_discovery_negative_ttl_seconds", "model_discovery_timeout_seconds", "models_dev_cache_ttl_seconds", "models_dev_metadata", "pricing_refresh", "public_catalog", "reject_user_mismatch", "require_pricing", "stream_missing_usage_policy", "vision_describe_max_tokens", "vision_describe_model", "vision_strategy"] + + @field_validator('pricing_refresh') + def pricing_refresh_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['manual', 'review', 'auto']): + raise ValueError("must be one of enum values ('manual', 'review', 'auto')") + return value @field_validator('stream_missing_usage_policy') def stream_missing_usage_policy_validate_enum(cls, value): @@ -149,6 +161,16 @@ def to_dict(self) -> Dict[str, Any]: if self.models_dev_metadata is None and "models_dev_metadata" in self.model_fields_set: _dict['models_dev_metadata'] = None + # set to None if pricing_refresh (nullable) is None + # and model_fields_set contains the field + if self.pricing_refresh is None and "pricing_refresh" in self.model_fields_set: + _dict['pricing_refresh'] = None + + # set to None if public_catalog (nullable) is None + # and model_fields_set contains the field + if self.public_catalog is None and "public_catalog" in self.model_fields_set: + _dict['public_catalog'] = None + # set to None if reject_user_mismatch (nullable) is None # and model_fields_set contains the field if self.reject_user_mismatch is None and "reject_user_mismatch" in self.model_fields_set: @@ -200,6 +222,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "model_discovery_timeout_seconds": obj.get("model_discovery_timeout_seconds"), "models_dev_cache_ttl_seconds": obj.get("models_dev_cache_ttl_seconds"), "models_dev_metadata": obj.get("models_dev_metadata"), + "pricing_refresh": obj.get("pricing_refresh"), + "public_catalog": obj.get("public_catalog"), "reject_user_mismatch": obj.get("reject_user_mismatch"), "require_pricing": obj.get("require_pricing"), "stream_missing_usage_policy": obj.get("stream_missing_usage_policy"),