From a7776f7526580d586cc4de4de43b775039a590db Mon Sep 17 00:00:00 2001 From: stainless-bot Date: Tue, 22 Oct 2024 01:14:40 +0000 Subject: [PATCH] feat(api): api update --- .stats.yml | 2 +- README.md | 2 +- api.md | 14 +- pyproject.toml | 8 +- requirements-dev.lock | 2 +- src/arcadepy/_base_client.py | 2 +- src/arcadepy/pagination.py | 72 +++++++ src/arcadepy/resources/auth.py | 4 +- src/arcadepy/resources/chat/completions.py | 12 +- src/arcadepy/resources/tools.py | 181 +++++++++++++++--- src/arcadepy/types/__init__.py | 2 +- src/arcadepy/types/auth_status_params.py | 2 +- .../types/chat/completion_create_params.py | 6 +- src/arcadepy/types/choice.py | 4 +- src/arcadepy/types/response_output.py | 4 +- .../types/shared/authorization_response.py | 3 +- src/arcadepy/types/tool_get_params.py | 17 ++ src/arcadepy/types/tool_list_params.py | 8 +- tests/api_resources/chat/test_completions.py | 12 +- tests/api_resources/test_tools.py | 93 ++++++++- tests/test_client.py | 21 +- tests/test_models.py | 2 +- 22 files changed, 384 insertions(+), 89 deletions(-) create mode 100644 src/arcadepy/pagination.py create mode 100644 src/arcadepy/types/tool_get_params.py diff --git a/.stats.yml b/.stats.yml index 099fd9aa..cd03fb95 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-98d52eb3c876e59ab21e8f1301f4e6eeb046c308eb6ffd2dc47b65d0d7b9873a.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-8b9d1faf866c909561b771928d9c93b9de29ccdb684b0a9b05a49390e233cf39.yml diff --git a/README.md b/README.md index 337f50ce..d0c3e41d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It is generated with [Stainless](https://www.stainlessapi.com/). ## Documentation -The REST API documentation can be found on [arcade-ai.com](https://arcade-ai.com). The full API of this library can be found in [api.md](api.md). +The REST API documentation can be found on [docs.arcade-ai.com](https://docs.arcade-ai.com). The full API of this library can be found in [api.md](api.md). ## Installation diff --git a/api.md b/api.md index 4b003758..11c82381 100644 --- a/api.md +++ b/api.md @@ -59,18 +59,12 @@ from arcadepy.types import ( ResponseOutput, ToolkitDefinition, ValueSchema, - ToolListResponse, ) ``` Methods: -- client.tools.list(\*\*params) -> ToolListResponse -- client.tools.authorize(\*\*params) -> AuthorizationResponse -- client.tools.execute(\*\*params) -> Response - -## Definition - -Methods: - -- client.tools.definition.get(\*\*params) -> ToolDefinition +- client.tools.list(\*\*params) -> SyncOffsetPage[ToolDefinition] +- client.tools.authorize(\*\*params) -> AuthorizationResponse +- client.tools.execute(\*\*params) -> Response +- client.tools.get(\*\*params) -> ToolDefinition diff --git a/pyproject.toml b/pyproject.toml index b71b9a8c..a4c5b0c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,11 +63,11 @@ format = { chain = [ "format:ruff", "format:docs", "fix:ruff", + # run formatting again to fix any inconsistencies when imports are stripped + "format:ruff", ]} -"format:black" = "black ." "format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" "format:ruff" = "ruff format" -"format:isort" = "isort ." "lint" = { chain = [ "check:ruff", @@ -125,10 +125,6 @@ path = "README.md" pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' replacement = '[\1](https://github.com/ArcadeAI/arcade-py/tree/main/\g<2>)' -[tool.black] -line-length = 120 -target-version = ["py37"] - [tool.pytest.ini_options] testpaths = ["tests"] addopts = "--tb=short" diff --git a/requirements-dev.lock b/requirements-dev.lock index f59e439d..d8e5806c 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -80,7 +80,7 @@ pytz==2023.3.post1 # via dirty-equals respx==0.20.2 rich==13.7.1 -ruff==0.6.5 +ruff==0.6.9 setuptools==68.2.2 # via nodeenv six==1.16.0 diff --git a/src/arcadepy/_base_client.py b/src/arcadepy/_base_client.py index a83fa527..a83a23a7 100644 --- a/src/arcadepy/_base_client.py +++ b/src/arcadepy/_base_client.py @@ -1575,7 +1575,7 @@ async def _request( except Exception as err: log.debug("Encountered Exception", exc_info=True) - if retries_taken > 0: + if remaining_retries > 0: return await self._retry_request( input_options, cast_to, diff --git a/src/arcadepy/pagination.py b/src/arcadepy/pagination.py new file mode 100644 index 00000000..ae6e85df --- /dev/null +++ b/src/arcadepy/pagination.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Generic, TypeVar, Optional +from typing_extensions import override + +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = ["SyncOffsetPage", "AsyncOffsetPage"] + +_T = TypeVar("_T") + + +class SyncOffsetPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + items: List[_T] + total_count: Optional[int] = None + page_count: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + items = self.items + if not items: + return [] + return items + + @override + def next_page_info(self) -> Optional[PageInfo]: + page_count = self.page_count + if page_count is None: + return None + + length = len(self._get_page_items()) + current_count = page_count + length + + total_count = self.total_count + if total_count is None: + return None + + if current_count < total_count: + return PageInfo(params={"offset": current_count}) + + return None + + +class AsyncOffsetPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + items: List[_T] + total_count: Optional[int] = None + page_count: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + items = self.items + if not items: + return [] + return items + + @override + def next_page_info(self) -> Optional[PageInfo]: + page_count = self.page_count + if page_count is None: + return None + + length = len(self._get_page_items()) + current_count = page_count + length + + total_count = self.total_count + if total_count is None: + return None + + if current_count < total_count: + return PageInfo(params={"offset": current_count}) + + return None diff --git a/src/arcadepy/resources/auth.py b/src/arcadepy/resources/auth.py index 467eb94c..29c49e29 100644 --- a/src/arcadepy/resources/auth.py +++ b/src/arcadepy/resources/auth.py @@ -114,7 +114,7 @@ def status( scopes: Scopes - wait: Timeout in seconds (max 60) + wait: Timeout in seconds (max 59) extra_headers: Send extra headers @@ -234,7 +234,7 @@ async def status( scopes: Scopes - wait: Timeout in seconds (max 60) + wait: Timeout in seconds (max 59) extra_headers: Send extra headers diff --git a/src/arcadepy/resources/chat/completions.py b/src/arcadepy/resources/chat/completions.py index 415cb47c..ca188130 100644 --- a/src/arcadepy/resources/chat/completions.py +++ b/src/arcadepy/resources/chat/completions.py @@ -58,7 +58,7 @@ def create( messages: Iterable[ChatMessageParam] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, n: int | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + parallel_tool_calls: object | NotGiven = NOT_GIVEN, presence_penalty: int | NotGiven = NOT_GIVEN, response_format: Literal["json_object", "text"] | NotGiven = NOT_GIVEN, seed: int | NotGiven = NOT_GIVEN, @@ -66,8 +66,8 @@ def create( stream: bool | NotGiven = NOT_GIVEN, stream_options: completion_create_params.StreamOptions | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, - tool_choice: Dict[str, object] | NotGiven = NOT_GIVEN, - tools: Dict[str, object] | NotGiven = NOT_GIVEN, + tool_choice: object | NotGiven = NOT_GIVEN, + tools: object | NotGiven = NOT_GIVEN, top_logprobs: int | NotGiven = NOT_GIVEN, top_p: float | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -181,7 +181,7 @@ async def create( messages: Iterable[ChatMessageParam] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, n: int | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + parallel_tool_calls: object | NotGiven = NOT_GIVEN, presence_penalty: int | NotGiven = NOT_GIVEN, response_format: Literal["json_object", "text"] | NotGiven = NOT_GIVEN, seed: int | NotGiven = NOT_GIVEN, @@ -189,8 +189,8 @@ async def create( stream: bool | NotGiven = NOT_GIVEN, stream_options: completion_create_params.StreamOptions | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, - tool_choice: Dict[str, object] | NotGiven = NOT_GIVEN, - tools: Dict[str, object] | NotGiven = NOT_GIVEN, + tool_choice: object | NotGiven = NOT_GIVEN, + tools: object | NotGiven = NOT_GIVEN, top_logprobs: int | NotGiven = NOT_GIVEN, top_p: float | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, diff --git a/src/arcadepy/resources/tools.py b/src/arcadepy/resources/tools.py index 2d904456..813ec783 100644 --- a/src/arcadepy/resources/tools.py +++ b/src/arcadepy/resources/tools.py @@ -4,7 +4,7 @@ import httpx -from ..types import tool_execute_params, tool_authorize_params, tool_retrieve_definition_params +from ..types import tool_get_params, tool_list_params, tool_execute_params, tool_authorize_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import ( maybe_transform, @@ -18,10 +18,11 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.tool_response import ToolResponse -from ..types.tool_definition import ToolDefinition -from ..types.authorization_response import AuthorizationResponse +from ..pagination import SyncOffsetPage, AsyncOffsetPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.response import Response +from ..types.shared.tool_definition import ToolDefinition +from ..types.shared.authorization_response import AuthorizationResponse __all__ = ["ToolsResource", "AsyncToolsResource"] @@ -46,6 +47,57 @@ def with_streaming_response(self) -> ToolsResourceWithStreamingResponse: """ return ToolsResourceWithStreamingResponse(self) + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + offset: int | NotGiven = NOT_GIVEN, + toolkit: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncOffsetPage[ToolDefinition]: + """ + Returns a list of tools, optionally filtered by toolkit or auth provider + + Args: + limit: Number of items to return (default: 25, max: 100) + + offset: Offset from the start of the list (default: 0) + + toolkit: Toolkit name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/v1/tools/list", + page=SyncOffsetPage[ToolDefinition], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + "toolkit": toolkit, + }, + tool_list_params.ToolListParams, + ), + ), + model=ToolDefinition, + ) + def authorize( self, *, @@ -64,7 +116,7 @@ def authorize( Authorizes a user for a specific tool by name Args: - tool_version: Optional: if not provided, latest version is assumed + tool_version: Optional: if not provided, any version is used extra_headers: Send extra headers @@ -99,10 +151,10 @@ def authorize( def execute( self, *, - inputs: str, tool_name: str, - tool_version: str, - user_id: str, + inputs: str | NotGiven = NOT_GIVEN, + tool_version: str | NotGiven = NOT_GIVEN, + user_id: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -110,13 +162,15 @@ def execute( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, idempotency_key: str | None = None, - ) -> ToolResponse: + ) -> Response: """ Executes a tool by name and arguments Args: inputs: Serialized JSON string + tool_version: Optional: if not provided, any version is used + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -131,8 +185,8 @@ def execute( "/v1/tools/execute", body=maybe_transform( { - "inputs": inputs, "tool_name": tool_name, + "inputs": inputs, "tool_version": tool_version, "user_id": user_id, }, @@ -145,10 +199,10 @@ def execute( timeout=timeout, idempotency_key=idempotency_key, ), - cast_to=ToolResponse, + cast_to=Response, ) - def retrieve_definition( + def get( self, *, director_id: str, @@ -188,7 +242,7 @@ def retrieve_definition( "director_id": director_id, "tool_id": tool_id, }, - tool_retrieve_definition_params.ToolRetrieveDefinitionParams, + tool_get_params.ToolGetParams, ), ), cast_to=ToolDefinition, @@ -215,6 +269,57 @@ def with_streaming_response(self) -> AsyncToolsResourceWithStreamingResponse: """ return AsyncToolsResourceWithStreamingResponse(self) + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + offset: int | NotGiven = NOT_GIVEN, + toolkit: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[ToolDefinition, AsyncOffsetPage[ToolDefinition]]: + """ + Returns a list of tools, optionally filtered by toolkit or auth provider + + Args: + limit: Number of items to return (default: 25, max: 100) + + offset: Offset from the start of the list (default: 0) + + toolkit: Toolkit name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/v1/tools/list", + page=AsyncOffsetPage[ToolDefinition], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + "toolkit": toolkit, + }, + tool_list_params.ToolListParams, + ), + ), + model=ToolDefinition, + ) + async def authorize( self, *, @@ -233,7 +338,7 @@ async def authorize( Authorizes a user for a specific tool by name Args: - tool_version: Optional: if not provided, latest version is assumed + tool_version: Optional: if not provided, any version is used extra_headers: Send extra headers @@ -268,10 +373,10 @@ async def authorize( async def execute( self, *, - inputs: str, tool_name: str, - tool_version: str, - user_id: str, + inputs: str | NotGiven = NOT_GIVEN, + tool_version: str | NotGiven = NOT_GIVEN, + user_id: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -279,13 +384,15 @@ async def execute( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, idempotency_key: str | None = None, - ) -> ToolResponse: + ) -> Response: """ Executes a tool by name and arguments Args: inputs: Serialized JSON string + tool_version: Optional: if not provided, any version is used + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -300,8 +407,8 @@ async def execute( "/v1/tools/execute", body=await async_maybe_transform( { - "inputs": inputs, "tool_name": tool_name, + "inputs": inputs, "tool_version": tool_version, "user_id": user_id, }, @@ -314,10 +421,10 @@ async def execute( timeout=timeout, idempotency_key=idempotency_key, ), - cast_to=ToolResponse, + cast_to=Response, ) - async def retrieve_definition( + async def get( self, *, director_id: str, @@ -357,7 +464,7 @@ async def retrieve_definition( "director_id": director_id, "tool_id": tool_id, }, - tool_retrieve_definition_params.ToolRetrieveDefinitionParams, + tool_get_params.ToolGetParams, ), ), cast_to=ToolDefinition, @@ -368,14 +475,17 @@ class ToolsResourceWithRawResponse: def __init__(self, tools: ToolsResource) -> None: self._tools = tools + self.list = to_raw_response_wrapper( + tools.list, + ) self.authorize = to_raw_response_wrapper( tools.authorize, ) self.execute = to_raw_response_wrapper( tools.execute, ) - self.retrieve_definition = to_raw_response_wrapper( - tools.retrieve_definition, + self.get = to_raw_response_wrapper( + tools.get, ) @@ -383,14 +493,17 @@ class AsyncToolsResourceWithRawResponse: def __init__(self, tools: AsyncToolsResource) -> None: self._tools = tools + self.list = async_to_raw_response_wrapper( + tools.list, + ) self.authorize = async_to_raw_response_wrapper( tools.authorize, ) self.execute = async_to_raw_response_wrapper( tools.execute, ) - self.retrieve_definition = async_to_raw_response_wrapper( - tools.retrieve_definition, + self.get = async_to_raw_response_wrapper( + tools.get, ) @@ -398,14 +511,17 @@ class ToolsResourceWithStreamingResponse: def __init__(self, tools: ToolsResource) -> None: self._tools = tools + self.list = to_streamed_response_wrapper( + tools.list, + ) self.authorize = to_streamed_response_wrapper( tools.authorize, ) self.execute = to_streamed_response_wrapper( tools.execute, ) - self.retrieve_definition = to_streamed_response_wrapper( - tools.retrieve_definition, + self.get = to_streamed_response_wrapper( + tools.get, ) @@ -413,12 +529,15 @@ class AsyncToolsResourceWithStreamingResponse: def __init__(self, tools: AsyncToolsResource) -> None: self._tools = tools + self.list = async_to_streamed_response_wrapper( + tools.list, + ) self.authorize = async_to_streamed_response_wrapper( tools.authorize, ) self.execute = async_to_streamed_response_wrapper( tools.execute, ) - self.retrieve_definition = async_to_streamed_response_wrapper( - tools.retrieve_definition, + self.get = async_to_streamed_response_wrapper( + tools.get, ) diff --git a/src/arcadepy/types/__init__.py b/src/arcadepy/types/__init__.py index 4da6d4b7..0fb4e720 100644 --- a/src/arcadepy/types/__init__.py +++ b/src/arcadepy/types/__init__.py @@ -15,10 +15,10 @@ from .chat_response import ChatResponse as ChatResponse from .health_schema import HealthSchema as HealthSchema from .response_output import ResponseOutput as ResponseOutput +from .tool_get_params import ToolGetParams as ToolGetParams from .tool_list_params import ToolListParams as ToolListParams from .auth_status_params import AuthStatusParams as AuthStatusParams from .chat_message_param import ChatMessageParam as ChatMessageParam -from .tool_list_response import ToolListResponse as ToolListResponse from .toolkit_definition import ToolkitDefinition as ToolkitDefinition from .tool_execute_params import ToolExecuteParams as ToolExecuteParams from .auth_authorize_params import AuthAuthorizeParams as AuthAuthorizeParams diff --git a/src/arcadepy/types/auth_status_params.py b/src/arcadepy/types/auth_status_params.py index 8eadbfeb..c6195630 100644 --- a/src/arcadepy/types/auth_status_params.py +++ b/src/arcadepy/types/auth_status_params.py @@ -17,4 +17,4 @@ class AuthStatusParams(TypedDict, total=False): """Scopes""" wait: int - """Timeout in seconds (max 60)""" + """Timeout in seconds (max 59)""" diff --git a/src/arcadepy/types/chat/completion_create_params.py b/src/arcadepy/types/chat/completion_create_params.py index 47213f74..43b86239 100644 --- a/src/arcadepy/types/chat/completion_create_params.py +++ b/src/arcadepy/types/chat/completion_create_params.py @@ -37,7 +37,7 @@ class CompletionCreateParams(TypedDict, total=False): n: int - parallel_tool_calls: bool + parallel_tool_calls: object """Disable the default behavior of parallel tool calls by setting it: false.""" presence_penalty: int @@ -55,10 +55,10 @@ class CompletionCreateParams(TypedDict, total=False): temperature: float - tool_choice: Dict[str, object] + tool_choice: object """This can be either a string or an ToolChoice object.""" - tools: Dict[str, object] + tools: object top_logprobs: int """ diff --git a/src/arcadepy/types/choice.py b/src/arcadepy/types/choice.py index 89d7571d..578f1d96 100644 --- a/src/arcadepy/types/choice.py +++ b/src/arcadepy/types/choice.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict, List, Optional +from typing import List, Optional from .._models import BaseModel from .chat_message import ChatMessage @@ -14,7 +14,7 @@ class Choice(BaseModel): index: Optional[int] = None - logprobs: Optional[Dict[str, object]] = None + logprobs: Optional[object] = None message: Optional[ChatMessage] = None diff --git a/src/arcadepy/types/response_output.py b/src/arcadepy/types/response_output.py index 5c9f05c5..087b8087 100644 --- a/src/arcadepy/types/response_output.py +++ b/src/arcadepy/types/response_output.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict, Optional +from typing import Optional from .._models import BaseModel from .shared.authorization_response import AuthorizationResponse @@ -25,4 +25,4 @@ class ResponseOutput(BaseModel): requires_authorization: Optional[AuthorizationResponse] = None - value: Optional[Dict[str, object]] = None + value: Optional[object] = None diff --git a/src/arcadepy/types/shared/authorization_response.py b/src/arcadepy/types/shared/authorization_response.py index 6d08e805..aebe87dd 100644 --- a/src/arcadepy/types/shared/authorization_response.py +++ b/src/arcadepy/types/shared/authorization_response.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, List, Optional +from typing_extensions import Literal from ..._models import BaseModel @@ -22,4 +23,4 @@ class AuthorizationResponse(BaseModel): scopes: Optional[List[str]] = None - status: Optional[str] = None + status: Optional[Literal["pending", "completed", "failed"]] = None diff --git a/src/arcadepy/types/tool_get_params.py b/src/arcadepy/types/tool_get_params.py new file mode 100644 index 00000000..5208ee51 --- /dev/null +++ b/src/arcadepy/types/tool_get_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ToolGetParams"] + + +class ToolGetParams(TypedDict, total=False): + director_id: Required[Annotated[str, PropertyInfo(alias="directorId")]] + """Director ID""" + + tool_id: Required[Annotated[str, PropertyInfo(alias="toolId")]] + """Tool ID""" diff --git a/src/arcadepy/types/tool_list_params.py b/src/arcadepy/types/tool_list_params.py index a623f325..336d56a3 100644 --- a/src/arcadepy/types/tool_list_params.py +++ b/src/arcadepy/types/tool_list_params.py @@ -8,5 +8,11 @@ class ToolListParams(TypedDict, total=False): + limit: int + """Number of items to return (default: 25, max: 100)""" + + offset: int + """Offset from the start of the list (default: 0)""" + toolkit: str - """Toolkit Name""" + """Toolkit name""" diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index edd17fd0..3cdade05 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -129,7 +129,7 @@ def test_method_create_with_all_params(self, client: Arcade) -> None: ], model="model", n=0, - parallel_tool_calls=True, + parallel_tool_calls={}, presence_penalty=0, response_format="json_object", seed=0, @@ -137,8 +137,8 @@ def test_method_create_with_all_params(self, client: Arcade) -> None: stream=True, stream_options={"include_usage": True}, temperature=0, - tool_choice={"foo": "bar"}, - tools={"foo": "bar"}, + tool_choice={}, + tools={}, top_logprobs=0, top_p=0, user="user", @@ -281,7 +281,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncArcade) -> ], model="model", n=0, - parallel_tool_calls=True, + parallel_tool_calls={}, presence_penalty=0, response_format="json_object", seed=0, @@ -289,8 +289,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncArcade) -> stream=True, stream_options={"include_usage": True}, temperature=0, - tool_choice={"foo": "bar"}, - tools={"foo": "bar"}, + tool_choice={}, + tools={}, top_logprobs=0, top_p=0, user="user", diff --git a/tests/api_resources/test_tools.py b/tests/api_resources/test_tools.py index 33f01552..f4776ef5 100644 --- a/tests/api_resources/test_tools.py +++ b/tests/api_resources/test_tools.py @@ -9,8 +9,9 @@ from arcadepy import Arcade, AsyncArcade from tests.utils import assert_matches_type -from arcadepy.types import Response, ToolListResponse -from arcadepy.types.shared import AuthorizationResponse +from arcadepy.types import Response +from arcadepy.pagination import SyncOffsetPage, AsyncOffsetPage +from arcadepy.types.shared import ToolDefinition, AuthorizationResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -21,14 +22,16 @@ class TestTools: @parametrize def test_method_list(self, client: Arcade) -> None: tool = client.tools.list() - assert_matches_type(ToolListResponse, tool, path=["response"]) + assert_matches_type(SyncOffsetPage[ToolDefinition], tool, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Arcade) -> None: tool = client.tools.list( + limit=0, + offset=0, toolkit="toolkit", ) - assert_matches_type(ToolListResponse, tool, path=["response"]) + assert_matches_type(SyncOffsetPage[ToolDefinition], tool, path=["response"]) @parametrize def test_raw_response_list(self, client: Arcade) -> None: @@ -37,7 +40,7 @@ def test_raw_response_list(self, client: Arcade) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tool = response.parse() - assert_matches_type(ToolListResponse, tool, path=["response"]) + assert_matches_type(SyncOffsetPage[ToolDefinition], tool, path=["response"]) @parametrize def test_streaming_response_list(self, client: Arcade) -> None: @@ -46,7 +49,7 @@ def test_streaming_response_list(self, client: Arcade) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" tool = response.parse() - assert_matches_type(ToolListResponse, tool, path=["response"]) + assert_matches_type(SyncOffsetPage[ToolDefinition], tool, path=["response"]) assert cast(Any, response.is_closed) is True @@ -134,6 +137,40 @@ def test_streaming_response_execute(self, client: Arcade) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_get(self, client: Arcade) -> None: + tool = client.tools.get( + director_id="directorId", + tool_id="toolId", + ) + assert_matches_type(ToolDefinition, tool, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Arcade) -> None: + response = client.tools.with_raw_response.get( + director_id="directorId", + tool_id="toolId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tool = response.parse() + assert_matches_type(ToolDefinition, tool, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Arcade) -> None: + with client.tools.with_streaming_response.get( + director_id="directorId", + tool_id="toolId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tool = response.parse() + assert_matches_type(ToolDefinition, tool, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncTools: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -141,14 +178,16 @@ class TestAsyncTools: @parametrize async def test_method_list(self, async_client: AsyncArcade) -> None: tool = await async_client.tools.list() - assert_matches_type(ToolListResponse, tool, path=["response"]) + assert_matches_type(AsyncOffsetPage[ToolDefinition], tool, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncArcade) -> None: tool = await async_client.tools.list( + limit=0, + offset=0, toolkit="toolkit", ) - assert_matches_type(ToolListResponse, tool, path=["response"]) + assert_matches_type(AsyncOffsetPage[ToolDefinition], tool, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncArcade) -> None: @@ -157,7 +196,7 @@ async def test_raw_response_list(self, async_client: AsyncArcade) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tool = await response.parse() - assert_matches_type(ToolListResponse, tool, path=["response"]) + assert_matches_type(AsyncOffsetPage[ToolDefinition], tool, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncArcade) -> None: @@ -166,7 +205,7 @@ async def test_streaming_response_list(self, async_client: AsyncArcade) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" tool = await response.parse() - assert_matches_type(ToolListResponse, tool, path=["response"]) + assert_matches_type(AsyncOffsetPage[ToolDefinition], tool, path=["response"]) assert cast(Any, response.is_closed) is True @@ -253,3 +292,37 @@ async def test_streaming_response_execute(self, async_client: AsyncArcade) -> No assert_matches_type(Response, tool, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_get(self, async_client: AsyncArcade) -> None: + tool = await async_client.tools.get( + director_id="directorId", + tool_id="toolId", + ) + assert_matches_type(ToolDefinition, tool, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncArcade) -> None: + response = await async_client.tools.with_raw_response.get( + director_id="directorId", + tool_id="toolId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tool = await response.parse() + assert_matches_type(ToolDefinition, tool, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncArcade) -> None: + async with async_client.tools.with_streaming_response.get( + director_id="directorId", + tool_id="toolId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tool = await response.parse() + assert_matches_type(ToolDefinition, tool, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/test_client.py b/tests/test_client.py index f8ae3bf7..37b06dd4 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -10,6 +10,7 @@ import tracemalloc from typing import Any, Union, cast from unittest import mock +from typing_extensions import Literal import httpx import pytest @@ -786,7 +787,14 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retries_taken(self, client: Arcade, failures_before_success: int, respx_mock: MockRouter) -> None: + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + def test_retries_taken( + self, + client: Arcade, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: client = client.with_options(max_retries=4) nb_retries = 0 @@ -795,6 +803,8 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") return httpx.Response(500) return httpx.Response(200) @@ -1605,8 +1615,13 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) @mock.patch("arcadepy._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( - self, async_client: AsyncArcade, failures_before_success: int, respx_mock: MockRouter + self, + async_client: AsyncArcade, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, ) -> None: client = async_client.with_options(max_retries=4) @@ -1616,6 +1631,8 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") return httpx.Response(500) return httpx.Response(200) diff --git a/tests/test_models.py b/tests/test_models.py index 1db764ba..2ab1110f 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -245,7 +245,7 @@ class Model(BaseModel): assert m.foo is True m = Model.construct(foo="CARD_HOLDER") - assert m.foo is "CARD_HOLDER" + assert m.foo == "CARD_HOLDER" m = Model.construct(foo={"bar": False}) assert isinstance(m.foo, Submodel1)