diff --git a/src/otari/_client/__init__.py b/src/otari/_client/__init__.py index 035880b..a3c18c1 100644 --- a/src/otari/_client/__init__.py +++ b/src/otari/_client/__init__.py @@ -77,6 +77,7 @@ "ChatCompletion", "ChatCompletionChunk", "ChatCompletionRequest", + "ChatCompletionRequestToolsInner", "ChatMessageInput", "Content", "Content1", @@ -279,6 +280,7 @@ 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 +from otari._client.models.chat_completion_request_tools_inner import ChatCompletionRequestToolsInner as ChatCompletionRequestToolsInner from otari._client.models.chat_message_input import ChatMessageInput as ChatMessageInput from otari._client.models.content import Content as Content from otari._client.models.content1 import Content1 as Content1 diff --git a/src/otari/_client/api/audio_api.py b/src/otari/_client/api/audio_api.py index 6f3d93e..aed06a2 100644 --- a/src/otari/_client/api/audio_api.py +++ b/src/otari/_client/api/audio_api.py @@ -299,6 +299,7 @@ def _create_speech_v1_audio_speech_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -665,6 +666,7 @@ def _create_transcription_v1_audio_transcriptions_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/batches_api.py b/src/otari/_client/api/batches_api.py index 7de0059..fda9adb 100644 --- a/src/otari/_client/api/batches_api.py +++ b/src/otari/_client/api/batches_api.py @@ -297,6 +297,7 @@ def _cancel_batch_v1_batches_batch_id_cancel_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -573,6 +574,7 @@ def _create_batch_v1_batches_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -872,6 +874,7 @@ def _list_batches_v1_batches_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1158,6 +1161,7 @@ def _retrieve_batch_results_v1_batches_batch_id_results_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1438,6 +1442,7 @@ def _retrieve_batch_v1_batches_batch_id_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/budgets_api.py b/src/otari/_client/api/budgets_api.py index a84ade2..ec5a82f 100644 --- a/src/otari/_client/api/budgets_api.py +++ b/src/otari/_client/api/budgets_api.py @@ -296,6 +296,7 @@ def _create_budget_v1_budgets_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -559,6 +560,7 @@ def _delete_budget_v1_budgets_budget_id_delete_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -822,6 +824,7 @@ def _get_budget_v1_budgets_budget_id_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1104,6 +1107,7 @@ def _list_budgets_v1_budgets_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1395,6 +1399,7 @@ def _update_budget_v1_budgets_budget_id_patch_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/chat_api.py b/src/otari/_client/api/chat_api.py index f628a2e..987bcbf 100644 --- a/src/otari/_client/api/chat_api.py +++ b/src/otari/_client/api/chat_api.py @@ -292,6 +292,7 @@ def _chat_completions_v1_chat_completions_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/embeddings_api.py b/src/otari/_client/api/embeddings_api.py index cae5575..db99779 100644 --- a/src/otari/_client/api/embeddings_api.py +++ b/src/otari/_client/api/embeddings_api.py @@ -292,6 +292,7 @@ def _create_embedding_v1_embeddings_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/files_api.py b/src/otari/_client/api/files_api.py index 436f36f..db9509c 100644 --- a/src/otari/_client/api/files_api.py +++ b/src/otari/_client/api/files_api.py @@ -322,6 +322,7 @@ def _create_file_v1_files_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -602,6 +603,7 @@ def _delete_file_v1_files_file_id_delete_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -882,6 +884,7 @@ def _get_file_content_v1_files_file_id_content_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1162,6 +1165,7 @@ def _get_file_v1_files_file_id_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1444,6 +1448,7 @@ def _list_files_v1_files_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/images_api.py b/src/otari/_client/api/images_api.py index 6cfb870..3aeb316 100644 --- a/src/otari/_client/api/images_api.py +++ b/src/otari/_client/api/images_api.py @@ -292,6 +292,7 @@ def _create_image_v1_images_generations_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/keys_api.py b/src/otari/_client/api/keys_api.py index f56483f..7721864 100644 --- a/src/otari/_client/api/keys_api.py +++ b/src/otari/_client/api/keys_api.py @@ -297,6 +297,7 @@ def _create_key_v1_keys_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -560,6 +561,7 @@ def _delete_key_v1_keys_key_id_delete_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -823,6 +825,7 @@ def _get_key_v1_keys_key_id_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1105,6 +1108,7 @@ def _list_keys_v1_keys_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1396,6 +1400,7 @@ def _update_key_v1_keys_key_id_patch_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/messages_api.py b/src/otari/_client/api/messages_api.py index 5044b01..8ef85d5 100644 --- a/src/otari/_client/api/messages_api.py +++ b/src/otari/_client/api/messages_api.py @@ -57,7 +57,7 @@ def count_message_tokens_v1_messages_count_tokens_post( ) -> CountTokensResponse: """Count Message Tokens - Anthropic ``/v1/messages/count_tokens``-compatible endpoint. Returns ``{\"input_tokens\": N}`` without contacting an upstream provider: counting is local, so there is no budget reservation, pricing, or usage logging. Authentication mirrors :func:`create_message` — platform mode resolves the caller's token against the platform, standalone mode validates the API key — so the endpoint is not an open token-counting oracle. + Anthropic ``/v1/messages/count_tokens``-compatible endpoint. Returns ``{\"input_tokens\": N}`` without contacting an upstream provider: counting is local, so there is no budget reservation, pricing, or usage logging. Authentication mirrors :func:`create_message` — hybrid mode resolves the caller's token against the platform, standalone mode validates the API key — so the endpoint is not an open token-counting oracle. :param count_tokens_request: (required) :type count_tokens_request: CountTokensRequest @@ -125,7 +125,7 @@ def count_message_tokens_v1_messages_count_tokens_post_with_http_info( ) -> ApiResponse[CountTokensResponse]: """Count Message Tokens - Anthropic ``/v1/messages/count_tokens``-compatible endpoint. Returns ``{\"input_tokens\": N}`` without contacting an upstream provider: counting is local, so there is no budget reservation, pricing, or usage logging. Authentication mirrors :func:`create_message` — platform mode resolves the caller's token against the platform, standalone mode validates the API key — so the endpoint is not an open token-counting oracle. + Anthropic ``/v1/messages/count_tokens``-compatible endpoint. Returns ``{\"input_tokens\": N}`` without contacting an upstream provider: counting is local, so there is no budget reservation, pricing, or usage logging. Authentication mirrors :func:`create_message` — hybrid mode resolves the caller's token against the platform, standalone mode validates the API key — so the endpoint is not an open token-counting oracle. :param count_tokens_request: (required) :type count_tokens_request: CountTokensRequest @@ -193,7 +193,7 @@ def count_message_tokens_v1_messages_count_tokens_post_without_preload_content( ) -> RESTResponseType: """Count Message Tokens - Anthropic ``/v1/messages/count_tokens``-compatible endpoint. Returns ``{\"input_tokens\": N}`` without contacting an upstream provider: counting is local, so there is no budget reservation, pricing, or usage logging. Authentication mirrors :func:`create_message` — platform mode resolves the caller's token against the platform, standalone mode validates the API key — so the endpoint is not an open token-counting oracle. + Anthropic ``/v1/messages/count_tokens``-compatible endpoint. Returns ``{\"input_tokens\": N}`` without contacting an upstream provider: counting is local, so there is no budget reservation, pricing, or usage logging. Authentication mirrors :func:`create_message` — hybrid mode resolves the caller's token against the platform, standalone mode validates the API key — so the endpoint is not an open token-counting oracle. :param count_tokens_request: (required) :type count_tokens_request: CountTokensRequest @@ -294,6 +294,7 @@ def _count_message_tokens_v1_messages_count_tokens_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -333,7 +334,7 @@ def create_message_v1_messages_post( ) -> MessageResponse: """Create Message - Anthropic Messages API-compatible endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and platform mode. Platform-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. + Anthropic Messages API-compatible endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and hybrid mode. Hybrid-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. :param messages_request: (required) :type messages_request: MessagesRequest @@ -401,7 +402,7 @@ def create_message_v1_messages_post_with_http_info( ) -> ApiResponse[MessageResponse]: """Create Message - Anthropic Messages API-compatible endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and platform mode. Platform-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. + Anthropic Messages API-compatible endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and hybrid mode. Hybrid-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. :param messages_request: (required) :type messages_request: MessagesRequest @@ -469,7 +470,7 @@ def create_message_v1_messages_post_without_preload_content( ) -> RESTResponseType: """Create Message - Anthropic Messages API-compatible endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and platform mode. Platform-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. + Anthropic Messages API-compatible endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and hybrid mode. Hybrid-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. :param messages_request: (required) :type messages_request: MessagesRequest @@ -570,6 +571,7 @@ def _create_message_v1_messages_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/models_api.py b/src/otari/_client/api/models_api.py index 9dada8b..3b158f4 100644 --- a/src/otari/_client/api/models_api.py +++ b/src/otari/_client/api/models_api.py @@ -282,6 +282,7 @@ def _get_model_v1_models_model_id_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -547,6 +548,7 @@ def _list_models_v1_models_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/moderations_api.py b/src/otari/_client/api/moderations_api.py index b5359c7..64512a1 100644 --- a/src/otari/_client/api/moderations_api.py +++ b/src/otari/_client/api/moderations_api.py @@ -311,6 +311,7 @@ def _create_moderation_v1_moderations_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/pricing_api.py b/src/otari/_client/api/pricing_api.py index 89935f2..f68e0ea 100644 --- a/src/otari/_client/api/pricing_api.py +++ b/src/otari/_client/api/pricing_api.py @@ -309,6 +309,7 @@ def _delete_pricing_v1_pricing_model_key_delete_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -572,6 +573,7 @@ def _get_pricing_history_v1_pricing_model_key_history_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -861,6 +863,7 @@ def _get_pricing_v1_pricing_model_key_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1143,6 +1146,7 @@ def _list_pricing_v1_pricing_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1419,6 +1423,7 @@ def _set_pricing_v1_pricing_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/rerank_api.py b/src/otari/_client/api/rerank_api.py index 13e2d00..4cbfd45 100644 --- a/src/otari/_client/api/rerank_api.py +++ b/src/otari/_client/api/rerank_api.py @@ -292,6 +292,7 @@ def _create_rerank_v1_rerank_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/responses_api.py b/src/otari/_client/api/responses_api.py index 330d110..3193bd8 100644 --- a/src/otari/_client/api/responses_api.py +++ b/src/otari/_client/api/responses_api.py @@ -55,7 +55,7 @@ def create_response_v1_responses_post( ) -> object: """Create Response - OpenAI-compatible Responses endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and platform mode. Platform-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. + OpenAI-compatible Responses endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and hybrid mode. Hybrid-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. :param responses_request: (required) :type responses_request: ResponsesRequest @@ -123,7 +123,7 @@ def create_response_v1_responses_post_with_http_info( ) -> ApiResponse[object]: """Create Response - OpenAI-compatible Responses endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and platform mode. Platform-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. + OpenAI-compatible Responses endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and hybrid mode. Hybrid-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. :param responses_request: (required) :type responses_request: ResponsesRequest @@ -191,7 +191,7 @@ def create_response_v1_responses_post_without_preload_content( ) -> RESTResponseType: """Create Response - OpenAI-compatible Responses endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and platform mode. Platform-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. + OpenAI-compatible Responses endpoint. Supports MCP tool-use loops, sandboxed code execution, and SearXNG web_search in both standalone mode and hybrid mode. Hybrid-mode requests resolve credentials via the platform service and (for non-tool-loop requests) get multi-attempt fallback across the resolved route. Tool-loop requests collapse to a single attempt — once ``on_first_response`` lock-in plumbing lands across the codebase, a follow-up will enable pre-lock-in fallback for tool-loop requests too. :param responses_request: (required) :type responses_request: ResponsesRequest @@ -292,6 +292,7 @@ def _create_response_v1_responses_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/usage_api.py b/src/otari/_client/api/usage_api.py index ae174f6..cb2b65a 100644 --- a/src/otari/_client/api/usage_api.py +++ b/src/otari/_client/api/usage_api.py @@ -370,6 +370,7 @@ def _list_usage_v1_usage_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/api/users_api.py b/src/otari/_client/api/users_api.py index dff3cde..1842a20 100644 --- a/src/otari/_client/api/users_api.py +++ b/src/otari/_client/api/users_api.py @@ -297,6 +297,7 @@ def _create_user_v1_users_post_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -560,6 +561,7 @@ def _delete_user_v1_users_user_id_delete_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -857,6 +859,7 @@ def _get_user_usage_v1_users_user_id_usage_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1120,6 +1123,7 @@ def _get_user_v1_users_user_id_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1402,6 +1406,7 @@ def _list_users_v1_users_get_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( @@ -1693,6 +1698,7 @@ def _update_user_v1_users_user_id_patch_serialize( # authentication setting _auth_settings: List[str] = [ + 'ApiKeyAuth' ] return self.api_client.param_serialize( diff --git a/src/otari/_client/configuration.py b/src/otari/_client/configuration.py index dac0797..63ef7be 100644 --- a/src/otari/_client/configuration.py +++ b/src/otari/_client/configuration.py @@ -111,6 +111,7 @@ AuthSettings = TypedDict( "AuthSettings", { + "ApiKeyAuth": APIKeyAuthSetting, }, total=False, ) @@ -175,6 +176,26 @@ class Configuration: :param datetime_format: Datetime format string for serialization. :param date_format: Date format string for serialization. + :Example: + + API Key Authentication Example. + Given the following security scheme in the OpenAPI specification: + components: + securitySchemes: + cookieAuth: # name for the security scheme + type: apiKey + in: cookie + name: JSESSIONID # cookie name + + You can programmatically set the cookie: + +conf = otari._client.Configuration( + api_key={'cookieAuth': 'abc123'} + api_key_prefix={'cookieAuth': 'JSESSIONID'} +) + + The following cookie will be added to the HTTP request: + Cookie: JSESSIONID abc123 """ _default: ClassVar[Optional[Self]] = None @@ -513,6 +534,15 @@ def auth_settings(self)-> AuthSettings: :return: The Auth Settings information dict. """ auth: AuthSettings = {} + if 'ApiKeyAuth' in self.api_key: + auth['ApiKeyAuth'] = { + 'type': 'api_key', + 'in': 'header', + 'key': 'Otari-Key', + 'value': self.get_api_key_with_prefix( + 'ApiKeyAuth', + ), + } return auth def to_debug_report(self) -> str: diff --git a/src/otari/_client/models/__init__.py b/src/otari/_client/models/__init__.py index 371ba6f..e0a75c0 100644 --- a/src/otari/_client/models/__init__.py +++ b/src/otari/_client/models/__init__.py @@ -46,6 +46,7 @@ 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 +from otari._client.models.chat_completion_request_tools_inner import ChatCompletionRequestToolsInner from otari._client.models.chat_message_input import ChatMessageInput from otari._client.models.content import Content from otari._client.models.content1 import Content1 diff --git a/src/otari/_client/models/chat_completion_request.py b/src/otari/_client/models/chat_completion_request.py index 9c81777..e69cbcb 100644 --- a/src/otari/_client/models/chat_completion_request.py +++ b/src/otari/_client/models/chat_completion_request.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional, Union from typing_extensions import Annotated from uuid import UUID +from otari._client.models.chat_completion_request_tools_inner import ChatCompletionRequestToolsInner from otari._client.models.chat_message_input import ChatMessageInput from otari._client.models.guardrail_config import GuardrailConfig from otari._client.models.mcp_server_config import McpServerConfig @@ -51,17 +52,18 @@ class ChatCompletionRequest(BaseModel): reasoning_effort: Optional[StrictStr] = None response_format: Optional[Dict[str, Any]] = None seed: Optional[StrictInt] = None + session_label: Optional[Annotated[str, Field(strict=True, max_length=255)]] = Field(default=None, description="Optional caller-supplied label for cost attribution (per run, experiment, or conversation). In hybrid mode it is forwarded onto the platform usage report so spend can be sliced by session without standing up OpenTelemetry. Stripped before the request is forwarded upstream to the provider. Has no effect in standalone mode, where there is no platform to report it to.") stop: Optional[Stop] = None stream: Optional[StrictBool] = False stream_options: Optional[Dict[str, Any]] = None temperature: Optional[Union[StrictFloat, StrictInt]] = None tool_choice: Optional[ToolChoice] = None - tools: Optional[List[Optional[Dict[str, Any]]]] = None + tools: Optional[List[ChatCompletionRequestToolsInner]] = None tools_header: Optional[Annotated[str, Field(strict=True, max_length=4000)]] = Field(default=None, description="Optional override for the lead-in that the gateway prepends before the per-tool hint block in the system message. Useful for expressing global tool-selection policy (e.g. 'prefer MCP tools over code_execution'). Falls back to OTARI_TOOLS_HEADER env, then to the built-in default.") top_logprobs: Optional[StrictInt] = None top_p: Optional[Union[StrictFloat, StrictInt]] = None user: Optional[StrictStr] = None - __properties: ClassVar[List[str]] = ["frequency_penalty", "guardrails", "logit_bias", "logprobs", "max_completion_tokens", "max_tokens", "max_tool_iterations", "mcp_server_ids", "mcp_servers", "messages", "model", "n", "parallel_tool_calls", "presence_penalty", "reasoning_effort", "response_format", "seed", "stop", "stream", "stream_options", "temperature", "tool_choice", "tools", "tools_header", "top_logprobs", "top_p", "user"] + __properties: ClassVar[List[str]] = ["frequency_penalty", "guardrails", "logit_bias", "logprobs", "max_completion_tokens", "max_tokens", "max_tool_iterations", "mcp_server_ids", "mcp_servers", "messages", "model", "n", "parallel_tool_calls", "presence_penalty", "reasoning_effort", "response_format", "seed", "session_label", "stop", "stream", "stream_options", "temperature", "tool_choice", "tools", "tools_header", "top_logprobs", "top_p", "user"] @field_validator('reasoning_effort') def reasoning_effort_validate_enum(cls, value): @@ -139,6 +141,13 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of tool_choice if self.tool_choice: _dict['tool_choice'] = self.tool_choice.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in tools (list) + _items = [] + if self.tools: + for _item_tools in self.tools: + if _item_tools: + _items.append(_item_tools.to_dict()) + _dict['tools'] = _items # set to None if frequency_penalty (nullable) is None # and model_fields_set contains the field if self.frequency_penalty is None and "frequency_penalty" in self.model_fields_set: @@ -214,6 +223,11 @@ def to_dict(self) -> Dict[str, Any]: if self.seed is None and "seed" in self.model_fields_set: _dict['seed'] = None + # set to None if session_label (nullable) is None + # and model_fields_set contains the field + if self.session_label is None and "session_label" in self.model_fields_set: + _dict['session_label'] = None + # set to None if stop (nullable) is None # and model_fields_set contains the field if self.stop is None and "stop" in self.model_fields_set: @@ -288,12 +302,13 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "reasoning_effort": obj.get("reasoning_effort"), "response_format": obj.get("response_format"), "seed": obj.get("seed"), + "session_label": obj.get("session_label"), "stop": Stop.from_dict(obj["stop"]) if obj.get("stop") is not None else None, "stream": obj.get("stream") if obj.get("stream") is not None else False, "stream_options": obj.get("stream_options"), "temperature": obj.get("temperature"), "tool_choice": ToolChoice.from_dict(obj["tool_choice"]) if obj.get("tool_choice") is not None else None, - "tools": obj.get("tools"), + "tools": [ChatCompletionRequestToolsInner.from_dict(_item) for _item in obj["tools"]] if obj.get("tools") is not None else None, "tools_header": obj.get("tools_header"), "top_logprobs": obj.get("top_logprobs"), "top_p": obj.get("top_p"), diff --git a/src/otari/_client/models/chat_completion_request_tools_inner.py b/src/otari/_client/models/chat_completion_request_tools_inner.py new file mode 100644 index 0000000..4f64c10 --- /dev/null +++ b/src/otari/_client/models/chat_completion_request_tools_inner.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 +from inspect import getfullargspec +import json +import pprint +import re # noqa: F401 +from pydantic import BaseModel, ConfigDict, Field, StrictStr, ValidationError, field_validator +from typing import Any, Dict, Optional +from typing import Union, Any, List, Set, TYPE_CHECKING, Optional, Dict +from typing_extensions import Literal, Self +from pydantic import Field + +CHATCOMPLETIONREQUESTTOOLSINNER_ANY_OF_SCHEMAS = ["Dict[str, object]"] + +class ChatCompletionRequestToolsInner(BaseModel): + """ + ChatCompletionRequestToolsInner + """ + + # data type: Dict[str, object] + anyof_schema_1_validator: Optional[Dict[str, Any]] = None + # data type: Dict[str, object] + anyof_schema_2_validator: Optional[Dict[str, Any]] = None + if TYPE_CHECKING: + actual_instance: Optional[Union[Dict[str, object]]] = None + else: + actual_instance: Any = None + any_of_schemas: Set[str] = { "Dict[str, object]" } + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_anyof(cls, v): + instance = ChatCompletionRequestToolsInner.model_construct() + error_messages = [] + # validate data type: Dict[str, object] + try: + instance.anyof_schema_1_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: Dict[str, object] + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError("No match found when setting the actual_instance in ChatCompletionRequestToolsInner with anyOf schemas: Dict[str, object]. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + # deserialize data into Dict[str, object] + try: + # validation + instance.anyof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_1_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Dict[str, object] + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError("No match found when deserializing the JSON string into ChatCompletionRequestToolsInner with anyOf schemas: Dict[str, object]. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], Dict[str, object]]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/src/otari/_client/models/guardrail_config.py b/src/otari/_client/models/guardrail_config.py index f6a9560..52e06fa 100644 --- a/src/otari/_client/models/guardrail_config.py +++ b/src/otari/_client/models/guardrail_config.py @@ -26,7 +26,7 @@ class GuardrailConfig(BaseModel): """ - A single guardrail check the caller wants the gateway to enforce. URL safety: when ``url`` is supplied it is validated at parse time with the same SSRF guard used for MCP server URLs (loopback allowed by default for same-host sidecars; gated by ``OTARI_MCP_ALLOW_LOOPBACK`` / ``OTARI_MCP_ALLOW_PRIVATE_HOSTS``). Most deployments omit ``url`` and rely on the operator-set ``OTARI_GUARDRAILS_URL`` instead. + A single guardrail check the caller wants the gateway to enforce. URL safety: when ``url`` is supplied it is validated by :func:`gateway.services.guardrails.run_input_guardrails` (not here at parse time — the check does a DNS lookup that must be awaited) with the same SSRF guard used for MCP server URLs (loopback allowed by default for same-host sidecars; gated by ``OTARI_MCP_ALLOW_LOOPBACK`` / ``OTARI_MCP_ALLOW_PRIVATE_HOSTS``). Most deployments omit ``url`` and rely on the operator-set ``OTARI_GUARDRAILS_URL`` instead. """ # noqa: E501 mode: Optional[StrictStr] = 'monitor' on: Optional[List[StrictStr]] = None diff --git a/src/otari/_client/models/mcp_server_config.py b/src/otari/_client/models/mcp_server_config.py index f1f286c..473bd6e 100644 --- a/src/otari/_client/models/mcp_server_config.py +++ b/src/otari/_client/models/mcp_server_config.py @@ -26,7 +26,7 @@ class McpServerConfig(BaseModel): """ - Inline MCP server configuration accepted on the chat completions request. Streamable HTTP transport. The `url` must be reachable from the gateway process. URL safety is enforced at parse time: * SSRF guard rejects private, link-local, and reserved IP ranges. Loopback is allowed by default (sidecars, dev) — set ``OTARI_MCP_ALLOW_LOOPBACK=false`` to disable. * Plain ``http://`` is rejected when ``authorization_token`` is set, to keep bearer tokens off the wire in cleartext. + Inline MCP server configuration accepted on the chat completions request. Streamable HTTP transport. The `url` must be reachable from the gateway process. URL safety (SSRF guard against private/link-local/reserved IP ranges, plus rejecting plain ``http://`` when ``authorization_token`` is set) is enforced by :func:`gateway.services.url_safety.validate_mcp_url`, called from the async request pipeline (``prepare_gateway_tools``) rather than here at parse time: the safety check does a DNS lookup, which must be awaited so it can't block the event loop, and Pydantic validators run synchronously during request-body parsing. """ # noqa: E501 allowed_tools: Optional[List[StrictStr]] = None authorization_token: Optional[StrictStr] = None diff --git a/src/otari/_client/models/messages_request.py b/src/otari/_client/models/messages_request.py index 2722a39..31bbad2 100644 --- a/src/otari/_client/models/messages_request.py +++ b/src/otari/_client/models/messages_request.py @@ -41,6 +41,7 @@ class MessagesRequest(BaseModel): messages: Annotated[List[Dict[str, Any]], Field(min_length=1)] metadata: Optional[Dict[str, Any]] = None model: StrictStr + session_label: Optional[Annotated[str, Field(strict=True, max_length=255)]] = Field(default=None, description="Optional caller-supplied label for cost attribution (per run, experiment, or conversation). In hybrid mode it is forwarded onto the platform usage report so spend can be sliced by session without standing up OpenTelemetry. Stripped before the request is forwarded upstream to the provider. Has no effect in standalone mode, where there is no platform to report it to.") stop_sequences: Optional[List[StrictStr]] = None stream: Optional[StrictBool] = False system: Optional[System] = None @@ -51,7 +52,7 @@ class MessagesRequest(BaseModel): tools_header: Optional[StrictStr] = None top_k: Optional[StrictInt] = None top_p: Optional[Union[StrictFloat, StrictInt]] = None - __properties: ClassVar[List[str]] = ["cache_control", "guardrails", "max_tokens", "max_tool_iterations", "mcp_server_ids", "mcp_servers", "messages", "metadata", "model", "stop_sequences", "stream", "system", "temperature", "thinking", "tool_choice", "tools", "tools_header", "top_k", "top_p"] + __properties: ClassVar[List[str]] = ["cache_control", "guardrails", "max_tokens", "max_tool_iterations", "mcp_server_ids", "mcp_servers", "messages", "metadata", "model", "session_label", "stop_sequences", "stream", "system", "temperature", "thinking", "tool_choice", "tools", "tools_header", "top_k", "top_p"] model_config = ConfigDict( validate_by_name=True, @@ -139,6 +140,11 @@ def to_dict(self) -> Dict[str, Any]: if self.metadata is None and "metadata" in self.model_fields_set: _dict['metadata'] = None + # set to None if session_label (nullable) is None + # and model_fields_set contains the field + if self.session_label is None and "session_label" in self.model_fields_set: + _dict['session_label'] = None + # set to None if stop_sequences (nullable) is None # and model_fields_set contains the field if self.stop_sequences is None and "stop_sequences" in self.model_fields_set: @@ -205,6 +211,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "messages": obj.get("messages"), "metadata": obj.get("metadata"), "model": obj.get("model"), + "session_label": obj.get("session_label"), "stop_sequences": obj.get("stop_sequences"), "stream": obj.get("stream") if obj.get("stream") is not None else False, "system": System.from_dict(obj["system"]) if obj.get("system") is not None else None, diff --git a/src/otari/_client/models/responses_request.py b/src/otari/_client/models/responses_request.py index a37f476..996da94 100644 --- a/src/otari/_client/models/responses_request.py +++ b/src/otari/_client/models/responses_request.py @@ -56,11 +56,12 @@ class ResponsesRequest(BaseModel): response_format: Optional[Dict[str, Any]] = None safety_identifier: Optional[StrictStr] = None service_tier: Optional[StrictStr] = None + session_label: Optional[Annotated[str, Field(strict=True, max_length=255)]] = Field(default=None, description="Optional caller-supplied label for cost attribution (per run, experiment, or conversation). In hybrid mode it is forwarded onto the platform usage report so spend can be sliced by session without standing up OpenTelemetry. Stripped before the request is forwarded upstream to the provider. Has no effect in standalone mode, where there is no platform to report it to.") store: Optional[StrictBool] = None stream: Optional[StrictBool] = False stream_options: Optional[Dict[str, Any]] = None temperature: Optional[Union[StrictFloat, StrictInt]] = None - text: Optional[Any] = None + text: Optional[Dict[str, Any]] = None tool_choice: Optional[ToolChoice1] = None tools: Optional[List[Dict[str, Any]]] = None tools_header: Optional[StrictStr] = None @@ -69,7 +70,7 @@ class ResponsesRequest(BaseModel): truncation: Optional[StrictStr] = None user: Optional[StrictStr] = None additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["background", "conversation", "frequency_penalty", "guardrails", "include", "input", "instructions", "max_output_tokens", "max_tool_calls", "max_tool_iterations", "mcp_server_ids", "mcp_servers", "metadata", "model", "parallel_tool_calls", "presence_penalty", "previous_response_id", "prompt_cache_key", "prompt_cache_retention", "reasoning", "response_format", "safety_identifier", "service_tier", "store", "stream", "stream_options", "temperature", "text", "tool_choice", "tools", "tools_header", "top_logprobs", "top_p", "truncation", "user"] + __properties: ClassVar[List[str]] = ["background", "conversation", "frequency_penalty", "guardrails", "include", "input", "instructions", "max_output_tokens", "max_tool_calls", "max_tool_iterations", "mcp_server_ids", "mcp_servers", "metadata", "model", "parallel_tool_calls", "presence_penalty", "previous_response_id", "prompt_cache_key", "prompt_cache_retention", "reasoning", "response_format", "safety_identifier", "service_tier", "session_label", "store", "stream", "stream_options", "temperature", "text", "tool_choice", "tools", "tools_header", "top_logprobs", "top_p", "truncation", "user"] model_config = ConfigDict( validate_by_name=True, @@ -129,9 +130,6 @@ def to_dict(self) -> Dict[str, Any]: if _item_mcp_servers: _items.append(_item_mcp_servers.to_dict()) _dict['mcp_servers'] = _items - # override the default output from pydantic by calling `to_dict()` of text - if self.text: - _dict['text'] = self.text.to_dict() # override the default output from pydantic by calling `to_dict()` of tool_choice if self.tool_choice: _dict['tool_choice'] = self.tool_choice.to_dict() @@ -250,6 +248,11 @@ def to_dict(self) -> Dict[str, Any]: if self.service_tier is None and "service_tier" in self.model_fields_set: _dict['service_tier'] = None + # set to None if session_label (nullable) is None + # and model_fields_set contains the field + if self.session_label is None and "session_label" in self.model_fields_set: + _dict['session_label'] = None + # set to None if store (nullable) is None # and model_fields_set contains the field if self.store is None and "store" in self.model_fields_set: @@ -340,11 +343,12 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "response_format": obj.get("response_format"), "safety_identifier": obj.get("safety_identifier"), "service_tier": obj.get("service_tier"), + "session_label": obj.get("session_label"), "store": obj.get("store"), "stream": obj.get("stream") if obj.get("stream") is not None else False, "stream_options": obj.get("stream_options"), "temperature": obj.get("temperature"), - "text": AnyOf.from_dict(obj["text"]) if obj.get("text") is not None else None, + "text": obj.get("text"), "tool_choice": ToolChoice1.from_dict(obj["tool_choice"]) if obj.get("tool_choice") is not None else None, "tools": obj.get("tools"), "tools_header": obj.get("tools_header"), diff --git a/src/otari/_client/models/usage_entry.py b/src/otari/_client/models/usage_entry.py index 7966543..307ae09 100644 --- a/src/otari/_client/models/usage_entry.py +++ b/src/otari/_client/models/usage_entry.py @@ -28,6 +28,8 @@ class UsageEntry(BaseModel): A single usage log entry. """ # noqa: E501 api_key_id: Optional[StrictStr] + cache_read_tokens: Optional[StrictInt] + cache_write_tokens: Optional[StrictInt] completion_tokens: Optional[StrictInt] cost: Optional[Union[StrictFloat, StrictInt]] endpoint: StrictStr @@ -40,7 +42,7 @@ class UsageEntry(BaseModel): timestamp: StrictStr total_tokens: Optional[StrictInt] user_id: Optional[StrictStr] - __properties: ClassVar[List[str]] = ["api_key_id", "completion_tokens", "cost", "endpoint", "error_message", "id", "model", "prompt_tokens", "provider", "status", "timestamp", "total_tokens", "user_id"] + __properties: ClassVar[List[str]] = ["api_key_id", "cache_read_tokens", "cache_write_tokens", "completion_tokens", "cost", "endpoint", "error_message", "id", "model", "prompt_tokens", "provider", "status", "timestamp", "total_tokens", "user_id"] model_config = ConfigDict( validate_by_name=True, @@ -86,6 +88,16 @@ 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 cache_read_tokens (nullable) is None + # and model_fields_set contains the field + if self.cache_read_tokens is None and "cache_read_tokens" in self.model_fields_set: + _dict['cache_read_tokens'] = None + + # set to None if cache_write_tokens (nullable) is None + # and model_fields_set contains the field + if self.cache_write_tokens is None and "cache_write_tokens" in self.model_fields_set: + _dict['cache_write_tokens'] = None + # set to None if completion_tokens (nullable) is None # and model_fields_set contains the field if self.completion_tokens is None and "completion_tokens" in self.model_fields_set: @@ -134,6 +146,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "api_key_id": obj.get("api_key_id"), + "cache_read_tokens": obj.get("cache_read_tokens"), + "cache_write_tokens": obj.get("cache_write_tokens"), "completion_tokens": obj.get("completion_tokens"), "cost": obj.get("cost"), "endpoint": obj.get("endpoint"),