diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 97bd1871..3d2ac0bd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.11" + ".": "0.1.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 099fd9aa..ce5dcbb1 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 +configured_endpoints: 10 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-990d8e4966391be90cdcbac42a5b5e5cadeab4122fb2a6e121cc89ec8135e079.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 287f831f..ba6179ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.1.0 (2024-10-22) + +Full Changelog: [v0.0.11...v0.1.0](https://github.com/ArcadeAI/arcade-py/compare/v0.0.11...v0.1.0) + +### Features + +* **api:** api update ([#25](https://github.com/ArcadeAI/arcade-py/issues/25)) ([c3ac0fa](https://github.com/ArcadeAI/arcade-py/commit/c3ac0fad62cf13e01f483448132d196f45f218af)) +* **api:** api update ([#27](https://github.com/ArcadeAI/arcade-py/issues/27)) ([f62efbf](https://github.com/ArcadeAI/arcade-py/commit/f62efbf57628d3d9b6e2734aec1c8028e21e54b4)) +* **api:** api update ([#28](https://github.com/ArcadeAI/arcade-py/issues/28)) ([5614650](https://github.com/ArcadeAI/arcade-py/commit/561465067e08077515f5e5cb361d8d09b0f7ead9)) + ## 0.0.11 (2024-10-15) Full Changelog: [v0.1.0-alpha.4...v0.0.11](https://github.com/ArcadeAI/arcade-py/compare/v0.1.0-alpha.4...v0.0.11) 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..2af35933 100644 --- a/api.md +++ b/api.md @@ -59,18 +59,25 @@ from arcadepy.types import ( ResponseOutput, ToolkitDefinition, ValueSchema, - ToolListResponse, ) ``` Methods: -- client.tools.list(\*\*params) -> ToolListResponse +- client.tools.list(\*\*params) -> SyncOffsetPage[ToolDefinition] - client.tools.authorize(\*\*params) -> AuthorizationResponse - client.tools.execute(\*\*params) -> Response +- client.tools.get(\*\*params) -> ToolDefinition -## Definition +## Formatted + +Types: + +```python +from arcadepy.types.tools import FormattedListResponse, FormattedGetResponse +``` Methods: -- client.tools.definition.get(\*\*params) -> ToolDefinition +- client.tools.formatted.list(\*\*params) -> SyncOffsetPage[object] +- client.tools.formatted.get(\*\*params) -> object diff --git a/pyproject.toml b/pyproject.toml index b71b9a8c..1eb3a214 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "arcadepy" -version = "0.0.11" +version = "0.1.0" description = "The official Python library for the Arcade API" dynamic = ["readme"] license = "Apache-2.0" @@ -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/_version.py b/src/arcadepy/_version.py index f719587a..b6a298ef 100644 --- a/src/arcadepy/_version.py +++ b/src/arcadepy/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "arcadepy" -__version__ = "0.0.11" # x-release-please-version +__version__ = "0.1.0" # x-release-please-version 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..f6ab0b4a 100644 --- a/src/arcadepy/resources/chat/completions.py +++ b/src/arcadepy/resources/chat/completions.py @@ -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, @@ -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/resources/tools/__init__.py b/src/arcadepy/resources/tools/__init__.py index f71184cc..92317d1b 100644 --- a/src/arcadepy/resources/tools/__init__.py +++ b/src/arcadepy/resources/tools/__init__.py @@ -8,22 +8,22 @@ ToolsResourceWithStreamingResponse, AsyncToolsResourceWithStreamingResponse, ) -from .definition import ( - DefinitionResource, - AsyncDefinitionResource, - DefinitionResourceWithRawResponse, - AsyncDefinitionResourceWithRawResponse, - DefinitionResourceWithStreamingResponse, - AsyncDefinitionResourceWithStreamingResponse, +from .formatted import ( + FormattedResource, + AsyncFormattedResource, + FormattedResourceWithRawResponse, + AsyncFormattedResourceWithRawResponse, + FormattedResourceWithStreamingResponse, + AsyncFormattedResourceWithStreamingResponse, ) __all__ = [ - "DefinitionResource", - "AsyncDefinitionResource", - "DefinitionResourceWithRawResponse", - "AsyncDefinitionResourceWithRawResponse", - "DefinitionResourceWithStreamingResponse", - "AsyncDefinitionResourceWithStreamingResponse", + "FormattedResource", + "AsyncFormattedResource", + "FormattedResourceWithRawResponse", + "AsyncFormattedResourceWithRawResponse", + "FormattedResourceWithStreamingResponse", + "AsyncFormattedResourceWithStreamingResponse", "ToolsResource", "AsyncToolsResource", "ToolsResourceWithRawResponse", diff --git a/src/arcadepy/resources/tools/formatted.py b/src/arcadepy/resources/tools/formatted.py new file mode 100644 index 00000000..4a82f122 --- /dev/null +++ b/src/arcadepy/resources/tools/formatted.py @@ -0,0 +1,318 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPage, AsyncOffsetPage +from ...types.tools import formatted_get_params, formatted_list_params +from ..._base_client import AsyncPaginator, make_request_options + +__all__ = ["FormattedResource", "AsyncFormattedResource"] + + +class FormattedResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FormattedResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/ArcadeAI/arcade-py#accessing-raw-response-data-eg-headers + """ + return FormattedResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FormattedResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/ArcadeAI/arcade-py#with_streaming_response + """ + return FormattedResourceWithStreamingResponse(self) + + def list( + self, + *, + format: str | NotGiven = NOT_GIVEN, + 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[object]: + """ + Returns a page of tools, optionally filtered by toolkit, formatted for a + specific provider + + Args: + format: Provider format + + 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/formatted/list", + page=SyncOffsetPage[object], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "format": format, + "limit": limit, + "offset": offset, + "toolkit": toolkit, + }, + formatted_list_params.FormattedListParams, + ), + ), + model=object, + ) + + def get( + self, + *, + tool_id: str, + format: 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, + ) -> object: + """ + Returns the formatted tool specification for a specific tool, given a provider + + Args: + tool_id: Tool ID + + format: Provider format + + 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( + "/v1/tools/formatted/definition", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "tool_id": tool_id, + "format": format, + }, + formatted_get_params.FormattedGetParams, + ), + ), + cast_to=object, + ) + + +class AsyncFormattedResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFormattedResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/ArcadeAI/arcade-py#accessing-raw-response-data-eg-headers + """ + return AsyncFormattedResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFormattedResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/ArcadeAI/arcade-py#with_streaming_response + """ + return AsyncFormattedResourceWithStreamingResponse(self) + + def list( + self, + *, + format: str | NotGiven = NOT_GIVEN, + 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[object, AsyncOffsetPage[object]]: + """ + Returns a page of tools, optionally filtered by toolkit, formatted for a + specific provider + + Args: + format: Provider format + + 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/formatted/list", + page=AsyncOffsetPage[object], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "format": format, + "limit": limit, + "offset": offset, + "toolkit": toolkit, + }, + formatted_list_params.FormattedListParams, + ), + ), + model=object, + ) + + async def get( + self, + *, + tool_id: str, + format: 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, + ) -> object: + """ + Returns the formatted tool specification for a specific tool, given a provider + + Args: + tool_id: Tool ID + + format: Provider format + + 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 await self._get( + "/v1/tools/formatted/definition", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "tool_id": tool_id, + "format": format, + }, + formatted_get_params.FormattedGetParams, + ), + ), + cast_to=object, + ) + + +class FormattedResourceWithRawResponse: + def __init__(self, formatted: FormattedResource) -> None: + self._formatted = formatted + + self.list = to_raw_response_wrapper( + formatted.list, + ) + self.get = to_raw_response_wrapper( + formatted.get, + ) + + +class AsyncFormattedResourceWithRawResponse: + def __init__(self, formatted: AsyncFormattedResource) -> None: + self._formatted = formatted + + self.list = async_to_raw_response_wrapper( + formatted.list, + ) + self.get = async_to_raw_response_wrapper( + formatted.get, + ) + + +class FormattedResourceWithStreamingResponse: + def __init__(self, formatted: FormattedResource) -> None: + self._formatted = formatted + + self.list = to_streamed_response_wrapper( + formatted.list, + ) + self.get = to_streamed_response_wrapper( + formatted.get, + ) + + +class AsyncFormattedResourceWithStreamingResponse: + def __init__(self, formatted: AsyncFormattedResource) -> None: + self._formatted = formatted + + self.list = async_to_streamed_response_wrapper( + formatted.list, + ) + self.get = async_to_streamed_response_wrapper( + formatted.get, + ) diff --git a/src/arcadepy/resources/tools/tools.py b/src/arcadepy/resources/tools/tools.py index ebfdb3da..aae87c61 100644 --- a/src/arcadepy/resources/tools/tools.py +++ b/src/arcadepy/resources/tools/tools.py @@ -4,20 +4,20 @@ import httpx -from ...types import tool_list_params, tool_execute_params, tool_authorize_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, async_maybe_transform, ) from ..._compat import cached_property -from .definition import ( - DefinitionResource, - AsyncDefinitionResource, - DefinitionResourceWithRawResponse, - AsyncDefinitionResourceWithRawResponse, - DefinitionResourceWithStreamingResponse, - AsyncDefinitionResourceWithStreamingResponse, +from .formatted import ( + FormattedResource, + AsyncFormattedResource, + FormattedResourceWithRawResponse, + AsyncFormattedResourceWithRawResponse, + FormattedResourceWithStreamingResponse, + AsyncFormattedResourceWithStreamingResponse, ) from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -26,9 +26,10 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options +from ...pagination import SyncOffsetPage, AsyncOffsetPage +from ..._base_client import AsyncPaginator, make_request_options from ...types.response import Response -from ...types.tool_list_response import ToolListResponse +from ...types.shared.tool_definition import ToolDefinition from ...types.shared.authorization_response import AuthorizationResponse __all__ = ["ToolsResource", "AsyncToolsResource"] @@ -36,8 +37,8 @@ class ToolsResource(SyncAPIResource): @cached_property - def definition(self) -> DefinitionResource: - return DefinitionResource(self._client) + def formatted(self) -> FormattedResource: + return FormattedResource(self._client) @cached_property def with_raw_response(self) -> ToolsResourceWithRawResponse: @@ -61,6 +62,8 @@ def with_streaming_response(self) -> ToolsResourceWithStreamingResponse: 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. @@ -68,12 +71,16 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ToolListResponse: + ) -> SyncOffsetPage[ToolDefinition]: """ - Returns a list of tools, optionally filtered by toolkit or auth provider + Returns a page of tools, optionally filtered by toolkit Args: - toolkit: Toolkit Name + 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 @@ -83,16 +90,24 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ - return self._get( + 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({"toolkit": toolkit}, tool_list_params.ToolListParams), + query=maybe_transform( + { + "limit": limit, + "offset": offset, + "toolkit": toolkit, + }, + tool_list_params.ToolListParams, + ), ), - cast_to=ToolListResponse, + model=ToolDefinition, ) def authorize( @@ -199,11 +214,48 @@ def execute( cast_to=Response, ) + def get( + self, + *, + tool_id: str, + # 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, + ) -> ToolDefinition: + """ + Returns the arcade tool specification for a specific tool + + Args: + tool_id: Tool ID + + 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( + "/v1/tools/definition", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"tool_id": tool_id}, tool_get_params.ToolGetParams), + ), + cast_to=ToolDefinition, + ) + class AsyncToolsResource(AsyncAPIResource): @cached_property - def definition(self) -> AsyncDefinitionResource: - return AsyncDefinitionResource(self._client) + def formatted(self) -> AsyncFormattedResource: + return AsyncFormattedResource(self._client) @cached_property def with_raw_response(self) -> AsyncToolsResourceWithRawResponse: @@ -224,9 +276,11 @@ def with_streaming_response(self) -> AsyncToolsResourceWithStreamingResponse: """ return AsyncToolsResourceWithStreamingResponse(self) - async def list( + 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. @@ -234,12 +288,16 @@ async def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ToolListResponse: + ) -> AsyncPaginator[ToolDefinition, AsyncOffsetPage[ToolDefinition]]: """ - Returns a list of tools, optionally filtered by toolkit or auth provider + Returns a page of tools, optionally filtered by toolkit Args: - toolkit: Toolkit Name + 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 @@ -249,16 +307,24 @@ async def list( timeout: Override the client-level default timeout for this request, in seconds """ - return await self._get( + 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=await async_maybe_transform({"toolkit": toolkit}, tool_list_params.ToolListParams), + query=maybe_transform( + { + "limit": limit, + "offset": offset, + "toolkit": toolkit, + }, + tool_list_params.ToolListParams, + ), ), - cast_to=ToolListResponse, + model=ToolDefinition, ) async def authorize( @@ -365,6 +431,43 @@ async def execute( cast_to=Response, ) + async def get( + self, + *, + tool_id: str, + # 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, + ) -> ToolDefinition: + """ + Returns the arcade tool specification for a specific tool + + Args: + tool_id: Tool ID + + 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 await self._get( + "/v1/tools/definition", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"tool_id": tool_id}, tool_get_params.ToolGetParams), + ), + cast_to=ToolDefinition, + ) + class ToolsResourceWithRawResponse: def __init__(self, tools: ToolsResource) -> None: @@ -379,10 +482,13 @@ def __init__(self, tools: ToolsResource) -> None: self.execute = to_raw_response_wrapper( tools.execute, ) + self.get = to_raw_response_wrapper( + tools.get, + ) @cached_property - def definition(self) -> DefinitionResourceWithRawResponse: - return DefinitionResourceWithRawResponse(self._tools.definition) + def formatted(self) -> FormattedResourceWithRawResponse: + return FormattedResourceWithRawResponse(self._tools.formatted) class AsyncToolsResourceWithRawResponse: @@ -398,10 +504,13 @@ def __init__(self, tools: AsyncToolsResource) -> None: self.execute = async_to_raw_response_wrapper( tools.execute, ) + self.get = async_to_raw_response_wrapper( + tools.get, + ) @cached_property - def definition(self) -> AsyncDefinitionResourceWithRawResponse: - return AsyncDefinitionResourceWithRawResponse(self._tools.definition) + def formatted(self) -> AsyncFormattedResourceWithRawResponse: + return AsyncFormattedResourceWithRawResponse(self._tools.formatted) class ToolsResourceWithStreamingResponse: @@ -417,10 +526,13 @@ def __init__(self, tools: ToolsResource) -> None: self.execute = to_streamed_response_wrapper( tools.execute, ) + self.get = to_streamed_response_wrapper( + tools.get, + ) @cached_property - def definition(self) -> DefinitionResourceWithStreamingResponse: - return DefinitionResourceWithStreamingResponse(self._tools.definition) + def formatted(self) -> FormattedResourceWithStreamingResponse: + return FormattedResourceWithStreamingResponse(self._tools.formatted) class AsyncToolsResourceWithStreamingResponse: @@ -436,7 +548,10 @@ def __init__(self, tools: AsyncToolsResource) -> None: self.execute = async_to_streamed_response_wrapper( tools.execute, ) + self.get = async_to_streamed_response_wrapper( + tools.get, + ) @cached_property - def definition(self) -> AsyncDefinitionResourceWithStreamingResponse: - return AsyncDefinitionResourceWithStreamingResponse(self._tools.definition) + def formatted(self) -> AsyncFormattedResourceWithStreamingResponse: + return AsyncFormattedResourceWithStreamingResponse(self._tools.formatted) 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..4081ba17 100644 --- a/src/arcadepy/types/chat/completion_create_params.py +++ b/src/arcadepy/types/chat/completion_create_params.py @@ -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..ecb15a02 --- /dev/null +++ b/src/arcadepy/types/tool_get_params.py @@ -0,0 +1,14 @@ +# 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): + 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/src/arcadepy/types/tools/__init__.py b/src/arcadepy/types/tools/__init__.py index 774cfc1a..c5f9e37a 100644 --- a/src/arcadepy/types/tools/__init__.py +++ b/src/arcadepy/types/tools/__init__.py @@ -2,4 +2,5 @@ from __future__ import annotations -from .definition_get_params import DefinitionGetParams as DefinitionGetParams +from .formatted_get_params import FormattedGetParams as FormattedGetParams +from .formatted_list_params import FormattedListParams as FormattedListParams diff --git a/src/arcadepy/types/tools/formatted_get_params.py b/src/arcadepy/types/tools/formatted_get_params.py new file mode 100644 index 00000000..f417ed88 --- /dev/null +++ b/src/arcadepy/types/tools/formatted_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__ = ["FormattedGetParams"] + + +class FormattedGetParams(TypedDict, total=False): + tool_id: Required[Annotated[str, PropertyInfo(alias="toolId")]] + """Tool ID""" + + format: str + """Provider format""" diff --git a/src/arcadepy/types/tools/formatted_list_params.py b/src/arcadepy/types/tools/formatted_list_params.py new file mode 100644 index 00000000..a52f2cdb --- /dev/null +++ b/src/arcadepy/types/tools/formatted_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["FormattedListParams"] + + +class FormattedListParams(TypedDict, total=False): + format: str + """Provider format""" + + 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""" diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index edd17fd0..dc8a03e8 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -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", @@ -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..c0457617 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,37 @@ 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( + 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( + 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( + 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 +175,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 +193,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 +202,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 +289,34 @@ 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( + 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( + 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( + 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/api_resources/tools/test_definition.py b/tests/api_resources/tools/test_definition.py deleted file mode 100644 index 1e525c30..00000000 --- a/tests/api_resources/tools/test_definition.py +++ /dev/null @@ -1,90 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from arcadepy import Arcade, AsyncArcade -from tests.utils import assert_matches_type -from arcadepy.types.shared import ToolDefinition - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestDefinition: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_get(self, client: Arcade) -> None: - definition = client.tools.definition.get( - director_id="directorId", - tool_id="toolId", - ) - assert_matches_type(ToolDefinition, definition, path=["response"]) - - @parametrize - def test_raw_response_get(self, client: Arcade) -> None: - response = client.tools.definition.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" - definition = response.parse() - assert_matches_type(ToolDefinition, definition, path=["response"]) - - @parametrize - def test_streaming_response_get(self, client: Arcade) -> None: - with client.tools.definition.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" - - definition = response.parse() - assert_matches_type(ToolDefinition, definition, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncDefinition: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - async def test_method_get(self, async_client: AsyncArcade) -> None: - definition = await async_client.tools.definition.get( - director_id="directorId", - tool_id="toolId", - ) - assert_matches_type(ToolDefinition, definition, path=["response"]) - - @parametrize - async def test_raw_response_get(self, async_client: AsyncArcade) -> None: - response = await async_client.tools.definition.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" - definition = await response.parse() - assert_matches_type(ToolDefinition, definition, path=["response"]) - - @parametrize - async def test_streaming_response_get(self, async_client: AsyncArcade) -> None: - async with async_client.tools.definition.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" - - definition = await response.parse() - assert_matches_type(ToolDefinition, definition, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/tools/test_formatted.py b/tests/api_resources/tools/test_formatted.py new file mode 100644 index 00000000..fae95262 --- /dev/null +++ b/tests/api_resources/tools/test_formatted.py @@ -0,0 +1,170 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from arcadepy import Arcade, AsyncArcade +from tests.utils import assert_matches_type +from arcadepy.pagination import SyncOffsetPage, AsyncOffsetPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestFormatted: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: Arcade) -> None: + formatted = client.tools.formatted.list() + assert_matches_type(SyncOffsetPage[object], formatted, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Arcade) -> None: + formatted = client.tools.formatted.list( + format="format", + limit=0, + offset=0, + toolkit="toolkit", + ) + assert_matches_type(SyncOffsetPage[object], formatted, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Arcade) -> None: + response = client.tools.formatted.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + formatted = response.parse() + assert_matches_type(SyncOffsetPage[object], formatted, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Arcade) -> None: + with client.tools.formatted.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + formatted = response.parse() + assert_matches_type(SyncOffsetPage[object], formatted, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_get(self, client: Arcade) -> None: + formatted = client.tools.formatted.get( + tool_id="toolId", + ) + assert_matches_type(object, formatted, path=["response"]) + + @parametrize + def test_method_get_with_all_params(self, client: Arcade) -> None: + formatted = client.tools.formatted.get( + tool_id="toolId", + format="format", + ) + assert_matches_type(object, formatted, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Arcade) -> None: + response = client.tools.formatted.with_raw_response.get( + tool_id="toolId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + formatted = response.parse() + assert_matches_type(object, formatted, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Arcade) -> None: + with client.tools.formatted.with_streaming_response.get( + tool_id="toolId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + formatted = response.parse() + assert_matches_type(object, formatted, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncFormatted: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_list(self, async_client: AsyncArcade) -> None: + formatted = await async_client.tools.formatted.list() + assert_matches_type(AsyncOffsetPage[object], formatted, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncArcade) -> None: + formatted = await async_client.tools.formatted.list( + format="format", + limit=0, + offset=0, + toolkit="toolkit", + ) + assert_matches_type(AsyncOffsetPage[object], formatted, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncArcade) -> None: + response = await async_client.tools.formatted.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + formatted = await response.parse() + assert_matches_type(AsyncOffsetPage[object], formatted, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncArcade) -> None: + async with async_client.tools.formatted.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + formatted = await response.parse() + assert_matches_type(AsyncOffsetPage[object], formatted, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_get(self, async_client: AsyncArcade) -> None: + formatted = await async_client.tools.formatted.get( + tool_id="toolId", + ) + assert_matches_type(object, formatted, path=["response"]) + + @parametrize + async def test_method_get_with_all_params(self, async_client: AsyncArcade) -> None: + formatted = await async_client.tools.formatted.get( + tool_id="toolId", + format="format", + ) + assert_matches_type(object, formatted, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncArcade) -> None: + response = await async_client.tools.formatted.with_raw_response.get( + tool_id="toolId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + formatted = await response.parse() + assert_matches_type(object, formatted, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncArcade) -> None: + async with async_client.tools.formatted.with_streaming_response.get( + tool_id="toolId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + formatted = await response.parse() + assert_matches_type(object, formatted, 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)