From 3f9383375c47ec961037e934f485e1ca6f30f951 Mon Sep 17 00:00:00 2001 From: njbrake <33383515+njbrake@users.noreply.github.com> Date: Fri, 7 Aug 2026 17:15:38 +0000 Subject: [PATCH] chore: regenerate SDK client core from Otari OpenAPI spec --- sdk-endpoints.txt | 2 + src/otari/_client/__init__.py | 16 + src/otari/_client/api/models_api.py | 34 +- src/otari/_client/api/routing_api.py | 548 ++++++++++++++++++ src/otari/_client/models/__init__.py | 8 + .../_client/models/discoverable_provider.py | 9 +- src/otari/_client/models/explain_response.py | 11 +- src/otari/_client/models/learned_policy.py | 94 +++ src/otari/_client/models/pool_status.py | 90 +++ src/otari/_client/models/rank_request.py | 99 ++++ src/otari/_client/models/rank_response.py | 100 ++++ src/otari/_client/models/recorded_pool.py | 97 ++++ src/otari/_client/models/router_status.py | 126 ++++ src/otari/_client/models/scored_example.py | 100 ++++ src/otari/_client/models/task_pool.py | 92 +++ src/otari/_client/models/usage_entry.py | 16 +- src/otari/_client/models/usage_group_row.py | 9 +- 17 files changed, 1440 insertions(+), 11 deletions(-) create mode 100644 src/otari/_client/models/learned_policy.py create mode 100644 src/otari/_client/models/pool_status.py create mode 100644 src/otari/_client/models/rank_request.py create mode 100644 src/otari/_client/models/rank_response.py create mode 100644 src/otari/_client/models/recorded_pool.py create mode 100644 src/otari/_client/models/router_status.py create mode 100644 src/otari/_client/models/scored_example.py create mode 100644 src/otari/_client/models/task_pool.py diff --git a/sdk-endpoints.txt b/sdk-endpoints.txt index d0786aa..d1617a2 100644 --- a/sdk-endpoints.txt +++ b/sdk-endpoints.txt @@ -124,6 +124,8 @@ GET /v1/routing/policies # not yet wrapped POST /v1/routing/policies # not yet wrapped DELETE /v1/routing/policies/{name} # not yet wrapped POST /v1/routing/policies/explain # not yet wrapped +POST /v1/routing/preferences/rank # not yet wrapped +GET /v1/routing/status # not yet wrapped # Pricing refresh workflow POST /v1/pricing/refresh # not yet wrapped POST /v1/pricing/refresh/confirm # not yet wrapped diff --git a/src/otari/_client/__init__.py b/src/otari/_client/__init__.py index a816d47..e417bea 100644 --- a/src/otari/_client/__init__.py +++ b/src/otari/_client/__init__.py @@ -153,6 +153,7 @@ "KeyInfo", "KnownProviderSchema", "KnownProviderSummarySchema", + "LearnedPolicy", "LocationInner", "MRAnthropicTypesThinkingBlockThinkingBlock", "MRAnyLlmTypesMessagesThinkingBlock", @@ -294,6 +295,7 @@ "ModerationResult", "PolicyRequest", "PolicyResponse", + "PoolStatus", "PricingRefreshChangeResponse", "PricingRefreshConfirmationResponse", "PricingRefreshPreviewResponse", @@ -308,11 +310,16 @@ "RRRerankMeta", "RRRerankResult", "RRRerankUsage", + "RankRequest", + "RankResponse", + "RecordedPool", "ReencryptProviderCredentialsResponse", "RerankRequest", "RerankResponse", "ResponsesRequest", "RotateMasterKeyResponse", + "RouterStatus", + "ScoredExample", "SearchRequest", "SearchResponse", "SearchResultItem", @@ -323,6 +330,7 @@ "Stop", "StoredProviderResponse", "System", + "TaskPool", "TestProviderRequest", "TestProviderResponse", "TestServiceRequest", @@ -502,6 +510,7 @@ from otari._client.models.key_info import KeyInfo as KeyInfo from otari._client.models.known_provider_schema import KnownProviderSchema as KnownProviderSchema from otari._client.models.known_provider_summary_schema import KnownProviderSummarySchema as KnownProviderSummarySchema +from otari._client.models.learned_policy import LearnedPolicy as LearnedPolicy from otari._client.models.location_inner import LocationInner as LocationInner from otari._client.models.mr_anthropic_types_thinking_block_thinking_block import MRAnthropicTypesThinkingBlockThinkingBlock as MRAnthropicTypesThinkingBlockThinkingBlock from otari._client.models.mr_any_llm_types_messages_thinking_block import MRAnyLlmTypesMessagesThinkingBlock as MRAnyLlmTypesMessagesThinkingBlock @@ -643,6 +652,7 @@ from otari._client.models.moderation_result import ModerationResult as ModerationResult 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_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 @@ -657,11 +667,16 @@ from otari._client.models.rr_rerank_meta import RRRerankMeta as RRRerankMeta from otari._client.models.rr_rerank_result import RRRerankResult as RRRerankResult from otari._client.models.rr_rerank_usage import RRRerankUsage as RRRerankUsage +from otari._client.models.rank_request import RankRequest as RankRequest +from otari._client.models.rank_response import RankResponse as RankResponse +from otari._client.models.recorded_pool import RecordedPool as RecordedPool from otari._client.models.reencrypt_provider_credentials_response import ReencryptProviderCredentialsResponse as ReencryptProviderCredentialsResponse from otari._client.models.rerank_request import RerankRequest as RerankRequest from otari._client.models.rerank_response import RerankResponse as RerankResponse from otari._client.models.responses_request import ResponsesRequest as ResponsesRequest from otari._client.models.rotate_master_key_response import RotateMasterKeyResponse as RotateMasterKeyResponse +from otari._client.models.router_status import RouterStatus as RouterStatus +from otari._client.models.scored_example import ScoredExample as ScoredExample from otari._client.models.search_request import SearchRequest as SearchRequest from otari._client.models.search_response import SearchResponse as SearchResponse from otari._client.models.search_result_item import SearchResultItem as SearchResultItem @@ -672,6 +687,7 @@ from otari._client.models.stop import Stop as Stop from otari._client.models.stored_provider_response import StoredProviderResponse as StoredProviderResponse from otari._client.models.system import System as System +from otari._client.models.task_pool import TaskPool as TaskPool from otari._client.models.test_provider_request import TestProviderRequest as TestProviderRequest from otari._client.models.test_provider_response import TestProviderResponse as TestProviderResponse from otari._client.models.test_service_request import TestServiceRequest as TestServiceRequest diff --git a/src/otari/_client/api/models_api.py b/src/otari/_client/api/models_api.py index 9b98553..0c4e5bf 100644 --- a/src/otari/_client/api/models_api.py +++ b/src/otari/_client/api/models_api.py @@ -15,7 +15,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union from typing_extensions import Annotated -from pydantic import Field, StrictStr +from pydantic import Field, StrictBool, StrictStr from typing import Optional from typing_extensions import Annotated from otari._client.models.discoverable_models_response import DiscoverableModelsResponse @@ -309,6 +309,7 @@ def _get_model_v1_models_model_id_get_serialize( @validate_call def list_discoverable_models_v1_models_discoverable_get( self, + refresh: Annotated[Optional[StrictBool], Field(description="Re-dial every provider instead of answering from the discovery cache.")] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -324,8 +325,10 @@ def list_discoverable_models_v1_models_discoverable_get( ) -> DiscoverableModelsResponse: """List Discoverable Models - List every model the configured provider credentials can reach. Operator-facing counterpart to GET /v1/models, which serves a curated catalog to API callers. This reports each provider separately and keeps its error, so a provider with a bad key is distinguishable from one with no models. It is master-key gated because a provider error message describes the gateway's own configuration. + List every model the configured provider credentials can reach. Operator-facing counterpart to GET /v1/models, which serves a curated catalog to API callers. This reports each provider separately and keeps its error, so a provider with a bad key is distinguishable from one with no models. It is master-key gated because a provider error message describes the gateway's own configuration. Answers from the discovery cache, which a background refresher keeps warm, so the call does not wait on a slow or unreachable provider. Each provider carries the ``checked_at`` its result was produced at; a null one has not been dialed yet. Pass ``refresh=true`` to force a live re-dial of every provider. + :param refresh: Re-dial every provider instead of answering from the discovery cache. + :type refresh: bool :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 @@ -349,6 +352,7 @@ def list_discoverable_models_v1_models_discoverable_get( """ # noqa: E501 _param = self._list_discoverable_models_v1_models_discoverable_get_serialize( + refresh=refresh, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, @@ -357,6 +361,7 @@ def list_discoverable_models_v1_models_discoverable_get( _response_types_map: Dict[str, Optional[str]] = { '200': "DiscoverableModelsResponse", + '422': "HTTPValidationError", } response_data = self.api_client.call_api( *_param, @@ -372,6 +377,7 @@ def list_discoverable_models_v1_models_discoverable_get( @validate_call def list_discoverable_models_v1_models_discoverable_get_with_http_info( self, + refresh: Annotated[Optional[StrictBool], Field(description="Re-dial every provider instead of answering from the discovery cache.")] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -387,8 +393,10 @@ def list_discoverable_models_v1_models_discoverable_get_with_http_info( ) -> ApiResponse[DiscoverableModelsResponse]: """List Discoverable Models - List every model the configured provider credentials can reach. Operator-facing counterpart to GET /v1/models, which serves a curated catalog to API callers. This reports each provider separately and keeps its error, so a provider with a bad key is distinguishable from one with no models. It is master-key gated because a provider error message describes the gateway's own configuration. + List every model the configured provider credentials can reach. Operator-facing counterpart to GET /v1/models, which serves a curated catalog to API callers. This reports each provider separately and keeps its error, so a provider with a bad key is distinguishable from one with no models. It is master-key gated because a provider error message describes the gateway's own configuration. Answers from the discovery cache, which a background refresher keeps warm, so the call does not wait on a slow or unreachable provider. Each provider carries the ``checked_at`` its result was produced at; a null one has not been dialed yet. Pass ``refresh=true`` to force a live re-dial of every provider. + :param refresh: Re-dial every provider instead of answering from the discovery cache. + :type refresh: bool :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 @@ -412,6 +420,7 @@ def list_discoverable_models_v1_models_discoverable_get_with_http_info( """ # noqa: E501 _param = self._list_discoverable_models_v1_models_discoverable_get_serialize( + refresh=refresh, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, @@ -420,6 +429,7 @@ def list_discoverable_models_v1_models_discoverable_get_with_http_info( _response_types_map: Dict[str, Optional[str]] = { '200': "DiscoverableModelsResponse", + '422': "HTTPValidationError", } response_data = self.api_client.call_api( *_param, @@ -435,6 +445,7 @@ def list_discoverable_models_v1_models_discoverable_get_with_http_info( @validate_call def list_discoverable_models_v1_models_discoverable_get_without_preload_content( self, + refresh: Annotated[Optional[StrictBool], Field(description="Re-dial every provider instead of answering from the discovery cache.")] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -450,8 +461,10 @@ def list_discoverable_models_v1_models_discoverable_get_without_preload_content( ) -> RESTResponseType: """List Discoverable Models - List every model the configured provider credentials can reach. Operator-facing counterpart to GET /v1/models, which serves a curated catalog to API callers. This reports each provider separately and keeps its error, so a provider with a bad key is distinguishable from one with no models. It is master-key gated because a provider error message describes the gateway's own configuration. + List every model the configured provider credentials can reach. Operator-facing counterpart to GET /v1/models, which serves a curated catalog to API callers. This reports each provider separately and keeps its error, so a provider with a bad key is distinguishable from one with no models. It is master-key gated because a provider error message describes the gateway's own configuration. Answers from the discovery cache, which a background refresher keeps warm, so the call does not wait on a slow or unreachable provider. Each provider carries the ``checked_at`` its result was produced at; a null one has not been dialed yet. Pass ``refresh=true`` to force a live re-dial of every provider. + :param refresh: Re-dial every provider instead of answering from the discovery cache. + :type refresh: bool :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 @@ -475,6 +488,7 @@ def list_discoverable_models_v1_models_discoverable_get_without_preload_content( """ # noqa: E501 _param = self._list_discoverable_models_v1_models_discoverable_get_serialize( + refresh=refresh, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, @@ -483,6 +497,7 @@ def list_discoverable_models_v1_models_discoverable_get_without_preload_content( _response_types_map: Dict[str, Optional[str]] = { '200': "DiscoverableModelsResponse", + '422': "HTTPValidationError", } response_data = self.api_client.call_api( *_param, @@ -493,6 +508,7 @@ def list_discoverable_models_v1_models_discoverable_get_without_preload_content( def _list_discoverable_models_v1_models_discoverable_get_serialize( self, + refresh, _request_auth, _content_type, _headers, @@ -515,6 +531,10 @@ def _list_discoverable_models_v1_models_discoverable_get_serialize( # process the path parameters # process the query parameters + if refresh is not None: + + _query_params.append(('refresh', refresh)) + # process the header parameters # process the form parameters # process the body parameter @@ -571,7 +591,7 @@ def list_model_metadata_v1_models_metadata_get( ) -> ModelMetadataResponse: """List Model Metadata - Per-model metadata for the dashboard's detail view, from models.dev. Covers every model models.dev lists under a configured provider, keyed by the ``instance:model`` selector the dashboard uses. ``available`` is false when enrichment is disabled (``models_dev_metadata``) or models.dev could not be reached; the response is then empty and the UI falls back to bundled data. Master-key gated: it describes the gateway's configured providers. + Per-model metadata for the dashboard's detail view, from models.dev. Covers every model models.dev lists under a configured provider, keyed by the ``instance:model`` selector the dashboard uses. ``available`` is false when enrichment is disabled (``models_dev_metadata``) or models.dev could not be reached; the response is then empty and the UI falls back to bundled data. Master-key gated: it describes the gateway's configured providers. Answers from the cached catalog, kept warm by a background refresher, so the dashboard never waits on the models.dev fetch timeout. :param _request_timeout: timeout setting for this request. If one number provided, it will be total request @@ -634,7 +654,7 @@ def list_model_metadata_v1_models_metadata_get_with_http_info( ) -> ApiResponse[ModelMetadataResponse]: """List Model Metadata - Per-model metadata for the dashboard's detail view, from models.dev. Covers every model models.dev lists under a configured provider, keyed by the ``instance:model`` selector the dashboard uses. ``available`` is false when enrichment is disabled (``models_dev_metadata``) or models.dev could not be reached; the response is then empty and the UI falls back to bundled data. Master-key gated: it describes the gateway's configured providers. + Per-model metadata for the dashboard's detail view, from models.dev. Covers every model models.dev lists under a configured provider, keyed by the ``instance:model`` selector the dashboard uses. ``available`` is false when enrichment is disabled (``models_dev_metadata``) or models.dev could not be reached; the response is then empty and the UI falls back to bundled data. Master-key gated: it describes the gateway's configured providers. Answers from the cached catalog, kept warm by a background refresher, so the dashboard never waits on the models.dev fetch timeout. :param _request_timeout: timeout setting for this request. If one number provided, it will be total request @@ -697,7 +717,7 @@ def list_model_metadata_v1_models_metadata_get_without_preload_content( ) -> RESTResponseType: """List Model Metadata - Per-model metadata for the dashboard's detail view, from models.dev. Covers every model models.dev lists under a configured provider, keyed by the ``instance:model`` selector the dashboard uses. ``available`` is false when enrichment is disabled (``models_dev_metadata``) or models.dev could not be reached; the response is then empty and the UI falls back to bundled data. Master-key gated: it describes the gateway's configured providers. + Per-model metadata for the dashboard's detail view, from models.dev. Covers every model models.dev lists under a configured provider, keyed by the ``instance:model`` selector the dashboard uses. ``available`` is false when enrichment is disabled (``models_dev_metadata``) or models.dev could not be reached; the response is then empty and the UI falls back to bundled data. Master-key gated: it describes the gateway's configured providers. Answers from the cached catalog, kept warm by a background refresher, so the dashboard never waits on the models.dev fetch timeout. :param _request_timeout: timeout setting for this request. If one number provided, it will be total request diff --git a/src/otari/_client/api/routing_api.py b/src/otari/_client/api/routing_api.py index d321625..3761f9a 100644 --- a/src/otari/_client/api/routing_api.py +++ b/src/otari/_client/api/routing_api.py @@ -22,6 +22,9 @@ from otari._client.models.explain_response import ExplainResponse from otari._client.models.policy_request import PolicyRequest from otari._client.models.policy_response import PolicyResponse +from otari._client.models.rank_request import RankRequest +from otari._client.models.rank_response import RankResponse +from otari._client.models.router_status import RouterStatus from otari._client.api_client import ApiClient, RequestSerialized from otari._client.api_response import ApiResponse @@ -848,6 +851,551 @@ def _list_policies_v1_routing_policies_get_serialize( + @validate_call + def rank_candidates_v1_routing_preferences_rank_post( + self, + rank_request: RankRequest, + _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, + ) -> RankResponse: + """Rank Candidates + + Record scored examples: one routing-memory record each, plus an audit row. The routing-memory record is written before its audit row for each example, because it is the load-bearing one (the router votes over it) and embedding it can fail; writing the audit row only afterwards means a failed embedding never leaves an orphan audit row. A failed embedding is a 502 that names the model, not a 500. Every example in the batch is embedded, so this is the call an operator makes most often and the one most likely to meet a misconfigured ``router_embedding_model``. Score keys are stored in the spelling the policy uses (see :func:`_validated_scores`), because the router matches them against its candidate selectors by exact string. + + :param rank_request: (required) + :type rank_request: RankRequest + :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._rank_candidates_v1_routing_preferences_rank_post_serialize( + rank_request=rank_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "RankResponse", + '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 rank_candidates_v1_routing_preferences_rank_post_with_http_info( + self, + rank_request: RankRequest, + _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[RankResponse]: + """Rank Candidates + + Record scored examples: one routing-memory record each, plus an audit row. The routing-memory record is written before its audit row for each example, because it is the load-bearing one (the router votes over it) and embedding it can fail; writing the audit row only afterwards means a failed embedding never leaves an orphan audit row. A failed embedding is a 502 that names the model, not a 500. Every example in the batch is embedded, so this is the call an operator makes most often and the one most likely to meet a misconfigured ``router_embedding_model``. Score keys are stored in the spelling the policy uses (see :func:`_validated_scores`), because the router matches them against its candidate selectors by exact string. + + :param rank_request: (required) + :type rank_request: RankRequest + :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._rank_candidates_v1_routing_preferences_rank_post_serialize( + rank_request=rank_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "RankResponse", + '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 rank_candidates_v1_routing_preferences_rank_post_without_preload_content( + self, + rank_request: RankRequest, + _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: + """Rank Candidates + + Record scored examples: one routing-memory record each, plus an audit row. The routing-memory record is written before its audit row for each example, because it is the load-bearing one (the router votes over it) and embedding it can fail; writing the audit row only afterwards means a failed embedding never leaves an orphan audit row. A failed embedding is a 502 that names the model, not a 500. Every example in the batch is embedded, so this is the call an operator makes most often and the one most likely to meet a misconfigured ``router_embedding_model``. Score keys are stored in the spelling the policy uses (see :func:`_validated_scores`), because the router matches them against its candidate selectors by exact string. + + :param rank_request: (required) + :type rank_request: RankRequest + :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._rank_candidates_v1_routing_preferences_rank_post_serialize( + rank_request=rank_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "RankResponse", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _rank_candidates_v1_routing_preferences_rank_post_serialize( + self, + rank_request, + _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 + if rank_request is not None: + _body_params = rank_request + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + # set the HTTP header `Content-Type` + if _content_type: + _header_params['Content-Type'] = _content_type + else: + _default_content_type = ( + self.api_client.select_header_content_type( + [ + 'application/json' + ] + ) + ) + if _default_content_type is not None: + _header_params['Content-Type'] = _default_content_type + + # authentication setting + _auth_settings: List[str] = [ + 'XApiKeyAuth', + 'ApiKeyAuth' + ] + + return self.api_client.param_serialize( + method='POST', + resource_path='/v1/routing/preferences/rank', + 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 routing_memory_status_v1_routing_status_get( + self, + user_id: Annotated[StrictStr, Field(description="Whose routing memory to report on.")], + _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, + ) -> RouterStatus: + """Routing Memory Status + + Report how warm one user's routing memory is, per pool. ``user_id`` is required rather than optional because there is no aggregate answer: warmth is per user, and a total across users would describe a pool that no request ever votes over. + + :param user_id: Whose routing memory to report on. (required) + :type user_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._routing_memory_status_v1_routing_status_get_serialize( + user_id=user_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "RouterStatus", + '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 routing_memory_status_v1_routing_status_get_with_http_info( + self, + user_id: Annotated[StrictStr, Field(description="Whose routing memory to report on.")], + _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[RouterStatus]: + """Routing Memory Status + + Report how warm one user's routing memory is, per pool. ``user_id`` is required rather than optional because there is no aggregate answer: warmth is per user, and a total across users would describe a pool that no request ever votes over. + + :param user_id: Whose routing memory to report on. (required) + :type user_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._routing_memory_status_v1_routing_status_get_serialize( + user_id=user_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "RouterStatus", + '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 routing_memory_status_v1_routing_status_get_without_preload_content( + self, + user_id: Annotated[StrictStr, Field(description="Whose routing memory to report on.")], + _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: + """Routing Memory Status + + Report how warm one user's routing memory is, per pool. ``user_id`` is required rather than optional because there is no aggregate answer: warmth is per user, and a total across users would describe a pool that no request ever votes over. + + :param user_id: Whose routing memory to report on. (required) + :type user_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._routing_memory_status_v1_routing_status_get_serialize( + user_id=user_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "RouterStatus", + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _routing_memory_status_v1_routing_status_get_serialize( + self, + user_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 + # process the query parameters + if user_id is not None: + + _query_params.append(('user_id', user_id)) + + # 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='/v1/routing/status', + 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 set_policy_v1_routing_policies_post( self, diff --git a/src/otari/_client/models/__init__.py b/src/otari/_client/models/__init__.py index 8b7e859..ab88bc8 100644 --- a/src/otari/_client/models/__init__.py +++ b/src/otari/_client/models/__init__.py @@ -113,6 +113,7 @@ from otari._client.models.key_info import KeyInfo from otari._client.models.known_provider_schema import KnownProviderSchema from otari._client.models.known_provider_summary_schema import KnownProviderSummarySchema +from otari._client.models.learned_policy import LearnedPolicy from otari._client.models.location_inner import LocationInner from otari._client.models.mr_anthropic_types_thinking_block_thinking_block import MRAnthropicTypesThinkingBlockThinkingBlock from otari._client.models.mr_any_llm_types_messages_thinking_block import MRAnyLlmTypesMessagesThinkingBlock @@ -254,6 +255,7 @@ from otari._client.models.moderation_result import ModerationResult 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_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 @@ -268,11 +270,16 @@ from otari._client.models.rr_rerank_meta import RRRerankMeta from otari._client.models.rr_rerank_result import RRRerankResult from otari._client.models.rr_rerank_usage import RRRerankUsage +from otari._client.models.rank_request import RankRequest +from otari._client.models.rank_response import RankResponse +from otari._client.models.recorded_pool import RecordedPool from otari._client.models.reencrypt_provider_credentials_response import ReencryptProviderCredentialsResponse from otari._client.models.rerank_request import RerankRequest from otari._client.models.rerank_response import RerankResponse from otari._client.models.responses_request import ResponsesRequest from otari._client.models.rotate_master_key_response import RotateMasterKeyResponse +from otari._client.models.router_status import RouterStatus +from otari._client.models.scored_example import ScoredExample from otari._client.models.search_request import SearchRequest from otari._client.models.search_response import SearchResponse from otari._client.models.search_result_item import SearchResultItem @@ -283,6 +290,7 @@ from otari._client.models.stop import Stop from otari._client.models.stored_provider_response import StoredProviderResponse from otari._client.models.system import System +from otari._client.models.task_pool import TaskPool from otari._client.models.test_provider_request import TestProviderRequest from otari._client.models.test_provider_response import TestProviderResponse from otari._client.models.test_service_request import TestServiceRequest diff --git a/src/otari/_client/models/discoverable_provider.py b/src/otari/_client/models/discoverable_provider.py index 7ee8c84..b74f53a 100644 --- a/src/otari/_client/models/discoverable_provider.py +++ b/src/otari/_client/models/discoverable_provider.py @@ -28,12 +28,13 @@ class DiscoverableProvider(BaseModel): """ One provider instance's discovery result. """ # noqa: E501 + checked_at: Optional[StrictStr] = Field(default=None, description="When this instance was last dialed, ISO 8601. Null when it has not been checked yet, which is what the first read after a restart sees while the background refresh runs.") discovery_unsupported: Optional[StrictBool] = Field(default=False, description="True when discovery failed only because this backend serves no model-listing endpoint. The provider may still handle requests for models declared in config.") error: Optional[StrictStr] = Field(default=None, description="Why discovery failed. Null when `ok` is true.") models: List[DiscoverableModel] ok: StrictBool = Field(description="False when this instance could not be queried.") provider: StrictStr - __properties: ClassVar[List[str]] = ["discovery_unsupported", "error", "models", "ok", "provider"] + __properties: ClassVar[List[str]] = ["checked_at", "discovery_unsupported", "error", "models", "ok", "provider"] model_config = ConfigDict( validate_by_name=True, @@ -81,6 +82,11 @@ def to_dict(self) -> Dict[str, Any]: if _item_models: _items.append(_item_models.to_dict()) _dict['models'] = _items + # set to None if checked_at (nullable) is None + # and model_fields_set contains the field + if self.checked_at is None and "checked_at" in self.model_fields_set: + _dict['checked_at'] = None + # set to None if error (nullable) is None # and model_fields_set contains the field if self.error is None and "error" in self.model_fields_set: @@ -98,6 +104,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ + "checked_at": obj.get("checked_at"), "discovery_unsupported": obj.get("discovery_unsupported") if obj.get("discovery_unsupported") is not None else False, "error": obj.get("error"), "models": [DiscoverableModel.from_dict(_item) for _item in obj["models"]] if obj.get("models") is not None else None, diff --git a/src/otari/_client/models/explain_response.py b/src/otari/_client/models/explain_response.py index fa4bc3d..b551d80 100644 --- a/src/otari/_client/models/explain_response.py +++ b/src/otari/_client/models/explain_response.py @@ -34,8 +34,10 @@ class ExplainResponse(BaseModel): guardrails: List[Optional[Dict[str, Any]]] is_dynamic: StrictBool name: StrictStr + router_backend: Optional[StrictStr] = None + router_candidates: Optional[List[StrictStr]] = None selection_reason: StrictStr - __properties: ClassVar[List[str]] = ["candidates", "dropped", "guardrails", "is_dynamic", "name", "selection_reason"] + __properties: ClassVar[List[str]] = ["candidates", "dropped", "guardrails", "is_dynamic", "name", "router_backend", "router_candidates", "selection_reason"] model_config = ConfigDict( validate_by_name=True, @@ -90,6 +92,11 @@ def to_dict(self) -> Dict[str, Any]: if _item_dropped: _items.append(_item_dropped.to_dict()) _dict['dropped'] = _items + # set to None if router_backend (nullable) is None + # and model_fields_set contains the field + if self.router_backend is None and "router_backend" in self.model_fields_set: + _dict['router_backend'] = None + return _dict @classmethod @@ -107,6 +114,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "guardrails": obj.get("guardrails"), "is_dynamic": obj.get("is_dynamic"), "name": obj.get("name"), + "router_backend": obj.get("router_backend"), + "router_candidates": obj.get("router_candidates"), "selection_reason": obj.get("selection_reason") }) return _obj diff --git a/src/otari/_client/models/learned_policy.py b/src/otari/_client/models/learned_policy.py new file mode 100644 index 0000000..120748a --- /dev/null +++ b/src/otari/_client/models/learned_policy.py @@ -0,0 +1,94 @@ +# 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 LearnedPolicy(BaseModel): + """ + A policy whose selection depends on the router, for the status overview. + """ # noqa: E501 + backend: StrictStr + candidates: List[StrictStr] + default_target: StrictStr + name: StrictStr + __properties: ClassVar[List[str]] = ["backend", "candidates", "default_target", "name"] + + 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 LearnedPolicy 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 LearnedPolicy from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "backend": obj.get("backend"), + "candidates": obj.get("candidates"), + "default_target": obj.get("default_target"), + "name": obj.get("name") + }) + return _obj + + diff --git a/src/otari/_client/models/pool_status.py b/src/otari/_client/models/pool_status.py new file mode 100644 index 0000000..f77cb56 --- /dev/null +++ b/src/otari/_client/models/pool_status.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, StrictBool, 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 PoolStatus(BaseModel): + """ + PoolStatus + """ # noqa: E501 + records: StrictInt + warm: StrictBool + __properties: ClassVar[List[str]] = ["records", "warm"] + + 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 PoolStatus 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 PoolStatus from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "records": obj.get("records"), + "warm": obj.get("warm") + }) + return _obj + + diff --git a/src/otari/_client/models/rank_request.py b/src/otari/_client/models/rank_request.py new file mode 100644 index 0000000..56d4acb --- /dev/null +++ b/src/otari/_client/models/rank_request.py @@ -0,0 +1,99 @@ +# 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, StrictStr +from typing import Any, ClassVar, Dict, List +from typing_extensions import Annotated +from otari._client.models.scored_example import ScoredExample +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class RankRequest(BaseModel): + """ + Record how well each candidate did, for one or many prompts. A batch because of the arithmetic: a pool routes nothing until it holds ``router_seed_count`` examples (20 by default) and the vote reads the ``k`` nearest (5 by default), so a useful first teaching pass is dozens of examples across the kinds of prompt you care about. + """ # noqa: E501 + examples: Annotated[List[ScoredExample], Field(min_length=1, max_length=100)] = Field(description="The scored prompts to record.") + user_id: StrictStr = Field(description="Whose routing memory these examples belong to.") + __properties: ClassVar[List[str]] = ["examples", "user_id"] + + 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 RankRequest 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 examples (list) + _items = [] + if self.examples: + for _item_examples in self.examples: + if _item_examples: + _items.append(_item_examples.to_dict()) + _dict['examples'] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of RankRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "examples": [ScoredExample.from_dict(_item) for _item in obj["examples"]] if obj.get("examples") is not None else None, + "user_id": obj.get("user_id") + }) + return _obj + + diff --git a/src/otari/_client/models/rank_response.py b/src/otari/_client/models/rank_response.py new file mode 100644 index 0000000..8f6ca96 --- /dev/null +++ b/src/otari/_client/models/rank_response.py @@ -0,0 +1,100 @@ +# 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, StrictInt +from typing import Any, ClassVar, Dict, List +from otari._client.models.recorded_pool import RecordedPool +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class RankResponse(BaseModel): + """ + RankResponse + """ # noqa: E501 + pools: List[RecordedPool] + recorded: StrictInt + seed_count: StrictInt + __properties: ClassVar[List[str]] = ["pools", "recorded", "seed_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 RankResponse 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 pools (list) + _items = [] + if self.pools: + for _item_pools in self.pools: + if _item_pools: + _items.append(_item_pools.to_dict()) + _dict['pools'] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of RankResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "pools": [RecordedPool.from_dict(_item) for _item in obj["pools"]] if obj.get("pools") is not None else None, + "recorded": obj.get("recorded"), + "seed_count": obj.get("seed_count") + }) + return _obj + + diff --git a/src/otari/_client/models/recorded_pool.py b/src/otari/_client/models/recorded_pool.py new file mode 100644 index 0000000..23ebb3b --- /dev/null +++ b/src/otari/_client/models/recorded_pool.py @@ -0,0 +1,97 @@ +# 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, StrictInt, StrictStr +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 RecordedPool(BaseModel): + """ + How warm one pool is after the write. + """ # noqa: E501 + records: StrictInt + task_id: Optional[StrictStr] + warm: StrictBool + __properties: ClassVar[List[str]] = ["records", "task_id", "warm"] + + 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 RecordedPool 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 task_id (nullable) is None + # and model_fields_set contains the field + if self.task_id is None and "task_id" in self.model_fields_set: + _dict['task_id'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of RecordedPool from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "records": obj.get("records"), + "task_id": obj.get("task_id"), + "warm": obj.get("warm") + }) + return _obj + + diff --git a/src/otari/_client/models/router_status.py b/src/otari/_client/models/router_status.py new file mode 100644 index 0000000..569d49f --- /dev/null +++ b/src/otari/_client/models/router_status.py @@ -0,0 +1,126 @@ +# 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, StrictFloat, StrictInt, StrictStr +from typing import Any, ClassVar, Dict, List, Union +from otari._client.models.learned_policy import LearnedPolicy +from otari._client.models.pool_status import PoolStatus +from otari._client.models.task_pool import TaskPool +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class RouterStatus(BaseModel): + """ + How warm this user's routing memory is, and what depends on it. Routing memory has no single warmth: it is a set of independent pools. ``default_pool`` is what a request with no ``Otari-Router-Task`` header votes over (every record the user has, labelled or not) and ``tasks`` lists each partition, which only requests carrying that label use. Each crosses ``seed_count`` on its own. + """ # noqa: E501 + alpha: Union[StrictFloat, StrictInt] + confidence_floor: Union[StrictFloat, StrictInt] + default_pool: PoolStatus + embedding_model: StrictStr + granularity: StrictStr + k: StrictInt + policies: List[LearnedPolicy] + seed_count: StrictInt + tasks: List[TaskPool] + user_id: StrictStr + __properties: ClassVar[List[str]] = ["alpha", "confidence_floor", "default_pool", "embedding_model", "granularity", "k", "policies", "seed_count", "tasks", "user_id"] + + 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 RouterStatus 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 default_pool + if self.default_pool: + _dict['default_pool'] = self.default_pool.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in policies (list) + _items = [] + if self.policies: + for _item_policies in self.policies: + if _item_policies: + _items.append(_item_policies.to_dict()) + _dict['policies'] = _items + # override the default output from pydantic by calling `to_dict()` of each item in tasks (list) + _items = [] + if self.tasks: + for _item_tasks in self.tasks: + if _item_tasks: + _items.append(_item_tasks.to_dict()) + _dict['tasks'] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of RouterStatus from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "alpha": obj.get("alpha"), + "confidence_floor": obj.get("confidence_floor"), + "default_pool": PoolStatus.from_dict(obj["default_pool"]) if obj.get("default_pool") is not None else None, + "embedding_model": obj.get("embedding_model"), + "granularity": obj.get("granularity"), + "k": obj.get("k"), + "policies": [LearnedPolicy.from_dict(_item) for _item in obj["policies"]] if obj.get("policies") is not None else None, + "seed_count": obj.get("seed_count"), + "tasks": [TaskPool.from_dict(_item) for _item in obj["tasks"]] if obj.get("tasks") is not None else None, + "user_id": obj.get("user_id") + }) + return _obj + + diff --git a/src/otari/_client/models/scored_example.py b/src/otari/_client/models/scored_example.py new file mode 100644 index 0000000..2585404 --- /dev/null +++ b/src/otari/_client/models/scored_example.py @@ -0,0 +1,100 @@ +# 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, StrictStr +from typing import Any, ClassVar, Dict, List, Optional, Union +from typing_extensions import Annotated +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class ScoredExample(BaseModel): + """ + One prompt and how well each candidate answered it. + """ # noqa: E501 + label_source: Optional[StrictStr] = Field(default='human', description="Provenance of the scores: 'human' or 'judge'.") + prompt: Annotated[str, Field(min_length=1, strict=True)] = Field(description="The prompt that was tried.") + scores: Dict[str, Union[Annotated[float, Field(le=1.0, strict=True, ge=0.0)], Annotated[int, Field(le=1, strict=True, ge=0)]]] = Field(description="Selector -> quality in [0.0, 1.0], where 1.0 is a great answer. Ties are fine and meaningful: two models that both answered well is exactly the case where the router should take the cheaper one.") + task_id: Optional[StrictStr] = Field(default=None, description="Partition this example belongs to, matching the Otari-Router-Task header requests send. Omit to file it in the user's default pool.") + __properties: ClassVar[List[str]] = ["label_source", "prompt", "scores", "task_id"] + + 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 ScoredExample 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 task_id (nullable) is None + # and model_fields_set contains the field + if self.task_id is None and "task_id" in self.model_fields_set: + _dict['task_id'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ScoredExample from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "label_source": obj.get("label_source") if obj.get("label_source") is not None else 'human', + "prompt": obj.get("prompt"), + "scores": obj.get("scores"), + "task_id": obj.get("task_id") + }) + return _obj + + diff --git a/src/otari/_client/models/task_pool.py b/src/otari/_client/models/task_pool.py new file mode 100644 index 0000000..34fa473 --- /dev/null +++ b/src/otari/_client/models/task_pool.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, StrictBool, 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 TaskPool(BaseModel): + """ + TaskPool + """ # noqa: E501 + records: StrictInt + task_id: StrictStr + warm: StrictBool + __properties: ClassVar[List[str]] = ["records", "task_id", "warm"] + + 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 TaskPool 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 TaskPool from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "records": obj.get("records"), + "task_id": obj.get("task_id"), + "warm": obj.get("warm") + }) + return _obj + + diff --git a/src/otari/_client/models/usage_entry.py b/src/otari/_client/models/usage_entry.py index 0b75866..f5d2779 100644 --- a/src/otari/_client/models/usage_entry.py +++ b/src/otari/_client/models/usage_entry.py @@ -29,6 +29,7 @@ class UsageEntry(BaseModel): A single usage log entry. """ # noqa: E501 api_key_id: Optional[StrictStr] + api_key_name: Optional[StrictStr] = None attempt_count: Optional[StrictInt] = None attempt_position: Optional[StrictInt] = None billing_meters: Optional[Dict[str, Any]] = Field(description="An unsaved policy body to explain.") @@ -55,8 +56,9 @@ class UsageEntry(BaseModel): status_code: Optional[StrictInt] timestamp: StrictStr total_tokens: Optional[StrictInt] + user_alias: Optional[StrictStr] = None user_id: Optional[StrictStr] - __properties: ClassVar[List[str]] = ["api_key_id", "attempt_count", "attempt_position", "billing_meters", "cache_read_tokens", "cache_write_1h_tokens", "cache_write_tokens", "completion_tokens", "cost", "counts_toward_budget", "endpoint", "error_message", "id", "latency_ms", "model", "policy_name", "pricing_breakdown", "prompt_tokens", "provider", "request_group_id", "selection_reason", "source", "source_label", "status", "status_code", "timestamp", "total_tokens", "user_id"] + __properties: ClassVar[List[str]] = ["api_key_id", "api_key_name", "attempt_count", "attempt_position", "billing_meters", "cache_read_tokens", "cache_write_1h_tokens", "cache_write_tokens", "completion_tokens", "cost", "counts_toward_budget", "endpoint", "error_message", "id", "latency_ms", "model", "policy_name", "pricing_breakdown", "prompt_tokens", "provider", "request_group_id", "selection_reason", "source", "source_label", "status", "status_code", "timestamp", "total_tokens", "user_alias", "user_id"] model_config = ConfigDict( validate_by_name=True, @@ -111,6 +113,11 @@ def to_dict(self) -> Dict[str, Any]: if self.api_key_id is None and "api_key_id" in self.model_fields_set: _dict['api_key_id'] = None + # set to None if api_key_name (nullable) is None + # and model_fields_set contains the field + if self.api_key_name is None and "api_key_name" in self.model_fields_set: + _dict['api_key_name'] = None + # set to None if attempt_count (nullable) is None # and model_fields_set contains the field if self.attempt_count is None and "attempt_count" in self.model_fields_set: @@ -206,6 +213,11 @@ def to_dict(self) -> Dict[str, Any]: if self.total_tokens is None and "total_tokens" in self.model_fields_set: _dict['total_tokens'] = None + # set to None if user_alias (nullable) is None + # and model_fields_set contains the field + if self.user_alias is None and "user_alias" in self.model_fields_set: + _dict['user_alias'] = None + # set to None if user_id (nullable) is None # and model_fields_set contains the field if self.user_id is None and "user_id" in self.model_fields_set: @@ -224,6 +236,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "api_key_id": obj.get("api_key_id"), + "api_key_name": obj.get("api_key_name"), "attempt_count": obj.get("attempt_count"), "attempt_position": obj.get("attempt_position"), "billing_meters": obj.get("billing_meters"), @@ -253,6 +266,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status_code": obj.get("status_code"), "timestamp": obj.get("timestamp"), "total_tokens": obj.get("total_tokens"), + "user_alias": obj.get("user_alias"), "user_id": obj.get("user_id") }) return _obj diff --git a/src/otari/_client/models/usage_group_row.py b/src/otari/_client/models/usage_group_row.py index 7ee1e10..9ada095 100644 --- a/src/otari/_client/models/usage_group_row.py +++ b/src/otari/_client/models/usage_group_row.py @@ -30,9 +30,10 @@ class UsageGroupRow(BaseModel): cost: Union[StrictFloat, StrictInt] is_other: Optional[StrictBool] = False key: Optional[StrictStr] + label: Optional[StrictStr] = None requests: StrictInt tokens: StrictInt - __properties: ClassVar[List[str]] = ["cost", "is_other", "key", "requests", "tokens"] + __properties: ClassVar[List[str]] = ["cost", "is_other", "key", "label", "requests", "tokens"] model_config = ConfigDict( validate_by_name=True, @@ -78,6 +79,11 @@ def to_dict(self) -> Dict[str, Any]: if self.key is None and "key" in self.model_fields_set: _dict['key'] = None + # set to None if label (nullable) is None + # and model_fields_set contains the field + if self.label is None and "label" in self.model_fields_set: + _dict['label'] = None + return _dict @classmethod @@ -93,6 +99,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "cost": obj.get("cost"), "is_other": obj.get("is_other") if obj.get("is_other") is not None else False, "key": obj.get("key"), + "label": obj.get("label"), "requests": obj.get("requests"), "tokens": obj.get("tokens") })