diff --git a/src/otari/_client/__init__.py b/src/otari/_client/__init__.py index b5e75428..c43a0666 100644 --- a/src/otari/_client/__init__.py +++ b/src/otari/_client/__init__.py @@ -168,6 +168,7 @@ "ChatCompletionRequestToolsInner", "ChatMessageInput", "CheckVerdictRequest", + "CodeExecutor", "ConfigField", "ConfigSearchToolSchema", "Content", @@ -778,6 +779,7 @@ 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.check_verdict_request import CheckVerdictRequest as CheckVerdictRequest +from otari._client.models.code_executor import CodeExecutor as CodeExecutor from otari._client.models.config_field import ConfigField as ConfigField from otari._client.models.config_search_tool_schema import ConfigSearchToolSchema as ConfigSearchToolSchema from otari._client.models.content import Content as Content diff --git a/src/otari/_client/api/files_api.py b/src/otari/_client/api/files_api.py index df62bd4d..8dd17f0d 100644 --- a/src/otari/_client/api/files_api.py +++ b/src/otari/_client/api/files_api.py @@ -15,8 +15,9 @@ from typing import Any, Dict, List, Optional, Tuple, Union from typing_extensions import Annotated -from pydantic import StrictBytes, StrictStr +from pydantic import Field, StrictBytes, StrictStr, field_validator from typing import Any, Dict, Optional, Tuple, Union +from typing_extensions import Annotated from uuid import UUID from otari._client.api_client import ApiClient, RequestSerialized @@ -58,7 +59,7 @@ def files_create_file( ) -> Dict[str, object]: """Create File - OpenAI-compatible file upload endpoint. + Upload a file. Answers in the OpenAI or Anthropic file shape, following the caller's headers. :param file: (required) :type file: str @@ -134,7 +135,7 @@ def files_create_file_with_http_info( ) -> ApiResponse[Dict[str, object]]: """Create File - OpenAI-compatible file upload endpoint. + Upload a file. Answers in the OpenAI or Anthropic file shape, following the caller's headers. :param file: (required) :type file: str @@ -210,7 +211,7 @@ def files_create_file_without_preload_content( ) -> RESTResponseType: """Create File - OpenAI-compatible file upload endpoint. + Upload a file. Answers in the OpenAI or Anthropic file shape, following the caller's headers. :param file: (required) :type file: str @@ -1199,6 +1200,10 @@ def files_list_files( user: Optional[StrictStr] = None, purpose: Optional[StrictStr] = None, workspace_id: Optional[UUID] = None, + limit: Optional[Annotated[int, Field(le=1000, strict=True, ge=1)]] = None, + after: Optional[StrictStr] = None, + after_id: Optional[StrictStr] = None, + order: Optional[StrictStr] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -1214,7 +1219,7 @@ def files_list_files( ) -> Dict[str, object]: """List Files - List the authenticated user's uploaded files in the request's workspace. ``workspace_id`` narrows a master-key listing to one workspace; a keyed request is already confined to its key's own and cannot widen or move it. + List the authenticated user's uploaded files in the request's workspace. ``workspace_id`` narrows a master-key listing to one workspace; a keyed request is already confined to its key's own and cannot widen or move it. Pages are cursor-based: ``after`` (OpenAI) or ``after_id`` (Anthropic) names the last file of the previous page, and ``has_more`` says whether to ask again. A cursor that has since been deleted or has expired is still a position; one the caller never owned is a 404. :param user: :type user: str @@ -1222,6 +1227,14 @@ def files_list_files( :type purpose: str :param workspace_id: :type workspace_id: UUID + :param limit: + :type limit: int + :param after: + :type after: str + :param after_id: + :type after_id: str + :param order: + :type order: 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 @@ -1248,6 +1261,10 @@ def files_list_files( user=user, purpose=purpose, workspace_id=workspace_id, + limit=limit, + after=after, + after_id=after_id, + order=order, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, @@ -1275,6 +1292,10 @@ def files_list_files_with_http_info( user: Optional[StrictStr] = None, purpose: Optional[StrictStr] = None, workspace_id: Optional[UUID] = None, + limit: Optional[Annotated[int, Field(le=1000, strict=True, ge=1)]] = None, + after: Optional[StrictStr] = None, + after_id: Optional[StrictStr] = None, + order: Optional[StrictStr] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -1290,7 +1311,7 @@ def files_list_files_with_http_info( ) -> ApiResponse[Dict[str, object]]: """List Files - List the authenticated user's uploaded files in the request's workspace. ``workspace_id`` narrows a master-key listing to one workspace; a keyed request is already confined to its key's own and cannot widen or move it. + List the authenticated user's uploaded files in the request's workspace. ``workspace_id`` narrows a master-key listing to one workspace; a keyed request is already confined to its key's own and cannot widen or move it. Pages are cursor-based: ``after`` (OpenAI) or ``after_id`` (Anthropic) names the last file of the previous page, and ``has_more`` says whether to ask again. A cursor that has since been deleted or has expired is still a position; one the caller never owned is a 404. :param user: :type user: str @@ -1298,6 +1319,14 @@ def files_list_files_with_http_info( :type purpose: str :param workspace_id: :type workspace_id: UUID + :param limit: + :type limit: int + :param after: + :type after: str + :param after_id: + :type after_id: str + :param order: + :type order: 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 @@ -1324,6 +1353,10 @@ def files_list_files_with_http_info( user=user, purpose=purpose, workspace_id=workspace_id, + limit=limit, + after=after, + after_id=after_id, + order=order, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, @@ -1351,6 +1384,10 @@ def files_list_files_without_preload_content( user: Optional[StrictStr] = None, purpose: Optional[StrictStr] = None, workspace_id: Optional[UUID] = None, + limit: Optional[Annotated[int, Field(le=1000, strict=True, ge=1)]] = None, + after: Optional[StrictStr] = None, + after_id: Optional[StrictStr] = None, + order: Optional[StrictStr] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -1366,7 +1403,7 @@ def files_list_files_without_preload_content( ) -> RESTResponseType: """List Files - List the authenticated user's uploaded files in the request's workspace. ``workspace_id`` narrows a master-key listing to one workspace; a keyed request is already confined to its key's own and cannot widen or move it. + List the authenticated user's uploaded files in the request's workspace. ``workspace_id`` narrows a master-key listing to one workspace; a keyed request is already confined to its key's own and cannot widen or move it. Pages are cursor-based: ``after`` (OpenAI) or ``after_id`` (Anthropic) names the last file of the previous page, and ``has_more`` says whether to ask again. A cursor that has since been deleted or has expired is still a position; one the caller never owned is a 404. :param user: :type user: str @@ -1374,6 +1411,14 @@ def files_list_files_without_preload_content( :type purpose: str :param workspace_id: :type workspace_id: UUID + :param limit: + :type limit: int + :param after: + :type after: str + :param after_id: + :type after_id: str + :param order: + :type order: 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 @@ -1400,6 +1445,10 @@ def files_list_files_without_preload_content( user=user, purpose=purpose, workspace_id=workspace_id, + limit=limit, + after=after, + after_id=after_id, + order=order, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, @@ -1422,6 +1471,10 @@ def _files_list_files_serialize( user, purpose, workspace_id, + limit, + after, + after_id, + order, _request_auth, _content_type, _headers, @@ -1456,6 +1509,22 @@ def _files_list_files_serialize( _query_params.append(('workspace_id', workspace_id)) + if limit is not None: + + _query_params.append(('limit', limit)) + + if after is not None: + + _query_params.append(('after', after)) + + if after_id is not None: + + _query_params.append(('after_id', after_id)) + + if order is not None: + + _query_params.append(('order', order)) + # process the header parameters # process the form parameters # process the body parameter diff --git a/src/otari/_client/models/__init__.py b/src/otari/_client/models/__init__.py index 1cf0ccd8..9b5d79fd 100644 --- a/src/otari/_client/models/__init__.py +++ b/src/otari/_client/models/__init__.py @@ -103,6 +103,7 @@ from otari._client.models.chat_completion_request_tools_inner import ChatCompletionRequestToolsInner from otari._client.models.chat_message_input import ChatMessageInput from otari._client.models.check_verdict_request import CheckVerdictRequest +from otari._client.models.code_executor import CodeExecutor from otari._client.models.config_field import ConfigField from otari._client.models.config_search_tool_schema import ConfigSearchToolSchema from otari._client.models.content import Content diff --git a/src/otari/_client/models/code_executor.py b/src/otari/_client/models/code_executor.py new file mode 100644 index 00000000..dae2ccae --- /dev/null +++ b/src/otari/_client/models/code_executor.py @@ -0,0 +1,38 @@ +# 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 json +from enum import Enum +from typing_extensions import Self + + +class CodeExecutor(str, Enum): + """ + Who runs the code a request's code-execution tool asks for. The one vocabulary shared by the deployment setting, the workspace policy, the per-request header and the platform's resolve payload, so a value read from any of them means the same thing at admission. + """ + + """ + allowed enum values + """ + AUTO = 'auto' + OTARI = 'otari' + PROVIDER = 'provider' + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of CodeExecutor from a JSON string""" + return cls(json.loads(json_str)) + + diff --git a/src/otari/_client/models/managed_tool.py b/src/otari/_client/models/managed_tool.py index 76252041..872fd55d 100644 --- a/src/otari/_client/models/managed_tool.py +++ b/src/otari/_client/models/managed_tool.py @@ -27,7 +27,7 @@ class ManagedTool(BaseModel): """ One tool the gateway can run itself. """ # noqa: E501 - accepted_types: List[StrictStr] = Field(description="Every `tools[].type` this deployment currently routes to the tool. Always includes the canonical `otari_*` type; for web search it also includes the provider-named keywords when interception is enabled.") + accepted_types: List[StrictStr] = Field(description="Every `tools[].type` this deployment currently routes to the tool. Always includes the canonical `otari_*` type; for web search it also includes the provider-named keywords when interception is enabled, and for code execution the provider-named keywords unless the deployment's executor is `provider`.") available: StrictBool = Field(description="Whether this deployment has enabled and configured the tool. A request declaring an unavailable tool is rejected with 400.") description: StrictStr = Field(description="What the tool does, as the model is told.") example: Dict[str, Any] = Field(description="A ready-to-use `tools[]` entry.") diff --git a/src/otari/_client/models/tool_setting_field.py b/src/otari/_client/models/tool_setting_field.py index 66ee0720..9d61790f 100644 --- a/src/otari/_client/models/tool_setting_field.py +++ b/src/otari/_client/models/tool_setting_field.py @@ -28,12 +28,13 @@ class ToolSettingField(BaseModel): """ One editable tool/guardrail field surfaced to the dashboard. """ # noqa: E501 + choices: Optional[List[StrictStr]] = None description: Optional[StrictStr] = None key: StrictStr service: StrictStr type: StrictStr value: Optional[Value1] - __properties: ClassVar[List[str]] = ["description", "key", "service", "type", "value"] + __properties: ClassVar[List[str]] = ["choices", "description", "key", "service", "type", "value"] @field_validator('service') def service_validate_enum(cls, value): @@ -91,6 +92,11 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of value if self.value: _dict['value'] = self.value.to_dict() + # set to None if choices (nullable) is None + # and model_fields_set contains the field + if self.choices is None and "choices" in self.model_fields_set: + _dict['choices'] = None + # set to None if description (nullable) is None # and model_fields_set contains the field if self.description is None and "description" in self.model_fields_set: @@ -113,6 +119,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ + "choices": obj.get("choices"), "description": obj.get("description"), "key": obj.get("key"), "service": obj.get("service"), diff --git a/src/otari/_client/models/update_tool_settings_request.py b/src/otari/_client/models/update_tool_settings_request.py index 83534c46..659f5d80 100644 --- a/src/otari/_client/models/update_tool_settings_request.py +++ b/src/otari/_client/models/update_tool_settings_request.py @@ -28,6 +28,7 @@ class UpdateToolSettingsRequest(BaseModel): """ Change one or more tool settings. Omitted fields are left unchanged; an explicit ``null`` clears a field back to the configured env/YAML default. """ # noqa: E501 + code_execution_executor: Optional[StrictStr] = None guardrails_url: Optional[StrictStr] = None sandbox_purpose_hint: Optional[StrictStr] = None sandbox_session_image: Optional[StrictStr] = None @@ -38,7 +39,7 @@ class UpdateToolSettingsRequest(BaseModel): web_search_max_results: Optional[Annotated[int, Field(strict=True, ge=1)]] = None web_search_purpose_hint: Optional[StrictStr] = None web_search_url: Optional[StrictStr] = None - __properties: ClassVar[List[str]] = ["guardrails_url", "sandbox_purpose_hint", "sandbox_session_image", "sandbox_url", "web_search_engines", "web_search_extract", "web_search_intercept", "web_search_max_results", "web_search_purpose_hint", "web_search_url"] + __properties: ClassVar[List[str]] = ["code_execution_executor", "guardrails_url", "sandbox_purpose_hint", "sandbox_session_image", "sandbox_url", "web_search_engines", "web_search_extract", "web_search_intercept", "web_search_max_results", "web_search_purpose_hint", "web_search_url"] model_config = ConfigDict( validate_by_name=True, @@ -79,6 +80,11 @@ def to_dict(self) -> Dict[str, Any]: exclude=excluded_fields, exclude_none=True, ) + # set to None if code_execution_executor (nullable) is None + # and model_fields_set contains the field + if self.code_execution_executor is None and "code_execution_executor" in self.model_fields_set: + _dict['code_execution_executor'] = None + # set to None if guardrails_url (nullable) is None # and model_fields_set contains the field if self.guardrails_url is None and "guardrails_url" in self.model_fields_set: @@ -141,6 +147,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ + "code_execution_executor": obj.get("code_execution_executor"), "guardrails_url": obj.get("guardrails_url"), "sandbox_purpose_hint": obj.get("sandbox_purpose_hint"), "sandbox_session_image": obj.get("sandbox_session_image"), diff --git a/src/otari/_client/models/workspace_code_execution_policy_public.py b/src/otari/_client/models/workspace_code_execution_policy_public.py index 60e030fb..c2d5b316 100644 --- a/src/otari/_client/models/workspace_code_execution_policy_public.py +++ b/src/otari/_client/models/workspace_code_execution_policy_public.py @@ -20,6 +20,7 @@ from pydantic import BaseModel, ConfigDict, StrictBool, StrictInt, StrictStr from typing import Any, ClassVar, Dict, List, Optional from uuid import UUID +from otari._client.models.code_executor import CodeExecutor from typing import Optional, Set from typing_extensions import Self from pydantic_core import to_jsonable_python @@ -35,13 +36,14 @@ class WorkspaceCodeExecutionPolicyPublic(BaseModel): default_purpose_hint: Optional[StrictStr] enabled: StrictBool exec_timeout_s: Optional[StrictInt] + executor: Optional[CodeExecutor] image: Optional[StrictStr] max_iterations: Optional[StrictInt] sandbox_configured: StrictBool tools: Optional[List[StrictStr]] updated_at: Optional[StrictStr] workspace_id: UUID - __properties: ClassVar[List[str]] = ["allowed_images", "available_tools", "configured", "created_at", "default_purpose_hint", "enabled", "exec_timeout_s", "image", "max_iterations", "sandbox_configured", "tools", "updated_at", "workspace_id"] + __properties: ClassVar[List[str]] = ["allowed_images", "available_tools", "configured", "created_at", "default_purpose_hint", "enabled", "exec_timeout_s", "executor", "image", "max_iterations", "sandbox_configured", "tools", "updated_at", "workspace_id"] model_config = ConfigDict( validate_by_name=True, @@ -97,6 +99,11 @@ def to_dict(self) -> Dict[str, Any]: if self.exec_timeout_s is None and "exec_timeout_s" in self.model_fields_set: _dict['exec_timeout_s'] = None + # set to None if executor (nullable) is None + # and model_fields_set contains the field + if self.executor is None and "executor" in self.model_fields_set: + _dict['executor'] = None + # set to None if image (nullable) is None # and model_fields_set contains the field if self.image is None and "image" in self.model_fields_set: @@ -136,6 +143,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "default_purpose_hint": obj.get("default_purpose_hint"), "enabled": obj.get("enabled"), "exec_timeout_s": obj.get("exec_timeout_s"), + "executor": obj.get("executor"), "image": obj.get("image"), "max_iterations": obj.get("max_iterations"), "sandbox_configured": obj.get("sandbox_configured"), diff --git a/src/otari/_client/models/workspace_code_execution_policy_update.py b/src/otari/_client/models/workspace_code_execution_policy_update.py index ae98cade..3a995fc3 100644 --- a/src/otari/_client/models/workspace_code_execution_policy_update.py +++ b/src/otari/_client/models/workspace_code_execution_policy_update.py @@ -20,6 +20,7 @@ from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr from typing import Any, ClassVar, Dict, List, Optional from typing_extensions import Annotated +from otari._client.models.code_executor import CodeExecutor from typing import Optional, Set from typing_extensions import Self from pydantic_core import to_jsonable_python @@ -31,10 +32,11 @@ class WorkspaceCodeExecutionPolicyUpdate(BaseModel): default_purpose_hint: Optional[Annotated[str, Field(strict=True, max_length=2048)]] = Field(default=None, description="Hint used when a request declares otari_code_execution without one of its own") enabled: StrictBool = Field(description="False refuses code execution for this workspace") exec_timeout_s: Optional[Annotated[int, Field(le=60, strict=True, gt=0)]] = Field(default=None, description="Ceiling on one execution's runtime in seconds; only ever lowers the effective limit, so at most 60") + executor: Optional[CodeExecutor] = Field(default=None, description="Who runs a provider-native code-execution declaration for this workspace: 'auto' (the provider when it runs the tool natively for the model, else this gateway's sandbox), 'otari' or 'provider'. Pins over the deployment default and over the request's X-Otari-Code-Execution header; null leaves both in charge") image: Optional[Annotated[str, Field(strict=True, max_length=255)]] = Field(default=None, description="Sandbox image this workspace's code runs in. Must be one the operator curated into sandbox_allowed_session_images (or the deployment's own sandbox_session_image); null uses the deployment's") max_iterations: Optional[Annotated[int, Field(le=25, strict=True, gt=0)]] = Field(default=None, description="Ceiling on tool-loop iterations; only ever lowers the effective limit, so at most 25") tools: Optional[List[StrictStr]] = Field(default=None, description="Code-execution tool kinds this workspace may use, from code_execution, bash_code_execution, text_editor_code_execution. Only ever removes one the backend serves; null exposes whatever it serves") - __properties: ClassVar[List[str]] = ["default_purpose_hint", "enabled", "exec_timeout_s", "image", "max_iterations", "tools"] + __properties: ClassVar[List[str]] = ["default_purpose_hint", "enabled", "exec_timeout_s", "executor", "image", "max_iterations", "tools"] model_config = ConfigDict( validate_by_name=True, @@ -85,6 +87,11 @@ def to_dict(self) -> Dict[str, Any]: if self.exec_timeout_s is None and "exec_timeout_s" in self.model_fields_set: _dict['exec_timeout_s'] = None + # set to None if executor (nullable) is None + # and model_fields_set contains the field + if self.executor is None and "executor" in self.model_fields_set: + _dict['executor'] = None + # set to None if image (nullable) is None # and model_fields_set contains the field if self.image is None and "image" in self.model_fields_set: @@ -115,6 +122,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "default_purpose_hint": obj.get("default_purpose_hint"), "enabled": obj.get("enabled"), "exec_timeout_s": obj.get("exec_timeout_s"), + "executor": obj.get("executor"), "image": obj.get("image"), "max_iterations": obj.get("max_iterations"), "tools": obj.get("tools")