From b2dbacf4c25cc22b9540e98a5923e1689f3a2f1f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:32:18 +0000 Subject: [PATCH] feat(api): OpenAPI spec update via Stainless API (#135) --- api.md | 2 +- src/cloudflare/resources/workers/ai.py | 62 +++++---- src/cloudflare/types/workers/__init__.py | 1 + src/cloudflare/types/workers/ai_run_params.py | 128 +++++++++++++++++- .../types/workers/ai_run_response.py | 104 ++++++++++++++ tests/api_resources/workers/test_ai.py | 55 +++++--- 6 files changed, 306 insertions(+), 46 deletions(-) create mode 100644 src/cloudflare/types/workers/ai_run_response.py diff --git a/api.md b/api.md index e4212148d..e898ed3f6 100644 --- a/api.md +++ b/api.md @@ -2652,7 +2652,7 @@ from cloudflare.types.workers import AIRunResponse Methods: -- client.workers.ai.run(model_name, \*, account_id, \*\*params) -> object +- client.workers.ai.run(model_name, \*, account_id, \*\*params) -> Optional ## Scripts diff --git a/src/cloudflare/resources/workers/ai.py b/src/cloudflare/resources/workers/ai.py index a5b9aa4fe..a32772bd5 100644 --- a/src/cloudflare/resources/workers/ai.py +++ b/src/cloudflare/resources/workers/ai.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Type, cast +from typing import Any, Optional, cast import httpx @@ -23,7 +23,7 @@ from ..._wrappers import ResultWrapper from ..._base_client import ( make_request_options, ) -from ...types.workers import ai_run_params +from ...types.workers import AIRunResponse, ai_run_params __all__ = ["AI", "AsyncAI"] @@ -42,14 +42,14 @@ class AI(SyncAPIResource): model_name: str, *, account_id: str, - body: object, + body: ai_run_params.Body, # 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: + ) -> Optional[AIRunResponse]: """ This endpoint provides users with the capability to run specific AI models on-demand. @@ -74,17 +74,22 @@ class AI(SyncAPIResource): raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") if not model_name: raise ValueError(f"Expected a non-empty value for `model_name` but received {model_name!r}") - return self._post( - f"/accounts/{account_id}/ai/run/{model_name}", - body=maybe_transform(body, ai_run_params.AIRunParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=ResultWrapper._unwrapper, + return cast( + Optional[AIRunResponse], + self._post( + f"/accounts/{account_id}/ai/run/{model_name}", + body=maybe_transform(body, ai_run_params.AIRunParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper._unwrapper, + ), + cast_to=cast( + Any, ResultWrapper[AIRunResponse] + ), # Union types cannot be passed in as arguments in the type system ), - cast_to=cast(Type[object], ResultWrapper[object]), ) @@ -102,14 +107,14 @@ class AsyncAI(AsyncAPIResource): model_name: str, *, account_id: str, - body: object, + body: ai_run_params.Body, # 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: + ) -> Optional[AIRunResponse]: """ This endpoint provides users with the capability to run specific AI models on-demand. @@ -134,17 +139,22 @@ class AsyncAI(AsyncAPIResource): raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") if not model_name: raise ValueError(f"Expected a non-empty value for `model_name` but received {model_name!r}") - return await self._post( - f"/accounts/{account_id}/ai/run/{model_name}", - body=await async_maybe_transform(body, ai_run_params.AIRunParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=ResultWrapper._unwrapper, + return cast( + Optional[AIRunResponse], + await self._post( + f"/accounts/{account_id}/ai/run/{model_name}", + body=await async_maybe_transform(body, ai_run_params.AIRunParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper._unwrapper, + ), + cast_to=cast( + Any, ResultWrapper[AIRunResponse] + ), # Union types cannot be passed in as arguments in the type system ), - cast_to=cast(Type[object], ResultWrapper[object]), ) diff --git a/src/cloudflare/types/workers/__init__.py b/src/cloudflare/types/workers/__init__.py index f89c0072c..b4cb2c728 100644 --- a/src/cloudflare/types/workers/__init__.py +++ b/src/cloudflare/types/workers/__init__.py @@ -6,6 +6,7 @@ from .ai_run_params import AIRunParams as AIRunParams from .workers_domain import WorkersDomain as WorkersDomain from .workers_routes import WorkersRoutes as WorkersRoutes from .workers_script import WorkersScript as WorkersScript +from .ai_run_response import AIRunResponse as AIRunResponse from .workers_filters import WorkersFilters as WorkersFilters from .domain_list_params import DomainListParams as DomainListParams from .route_create_params import RouteCreateParams as RouteCreateParams diff --git a/src/cloudflare/types/workers/ai_run_params.py b/src/cloudflare/types/workers/ai_run_params.py index a4380f15f..108403ed4 100644 --- a/src/cloudflare/types/workers/ai_run_params.py +++ b/src/cloudflare/types/workers/ai_run_params.py @@ -2,12 +2,136 @@ from __future__ import annotations +from typing import List, Union, Iterable from typing_extensions import Required, TypedDict -__all__ = ["AIRunParams"] +from ..._types import FileTypes + +__all__ = [ + "AIRunParams", + "Body", + "BodyTextClassification", + "BodyTextToImage", + "BodySentenceSimilarity", + "BodyTextEmbeddings", + "BodyAudio", + "BodyImage", + "BodyUnionMember10", + "BodyUnionMember11", + "BodyUnionMember11Message", + "BodyTranslation", + "BodySummarization", + "BodyUnionMember15", +] class AIRunParams(TypedDict, total=False): account_id: Required[str] - body: Required[object] + body: Required[Body] + + +class BodyTextClassification(TypedDict, total=False): + text: Required[str] + + +class BodyTextToImage(TypedDict, total=False): + prompt: Required[str] + + guidance: float + + image: Iterable[float] + + mask: Iterable[float] + + num_steps: int + + strength: float + + +class BodySentenceSimilarity(TypedDict, total=False): + sentences: Required[List[str]] + + source: Required[str] + + +class BodyTextEmbeddings(TypedDict, total=False): + text: Required[Union[str, List[str]]] + + +class BodyAudio(TypedDict, total=False): + audio: Iterable[float] + + +class BodyImage(TypedDict, total=False): + image: Iterable[float] + + +class BodyImage(TypedDict, total=False): + image: Iterable[float] + + +class BodyUnionMember10(TypedDict, total=False): + prompt: Required[str] + + max_tokens: int + + raw: bool + + stream: bool + + +class BodyUnionMember11Message(TypedDict, total=False): + content: Required[str] + + role: Required[str] + + +class BodyUnionMember11(TypedDict, total=False): + messages: Required[Iterable[BodyUnionMember11Message]] + + max_tokens: int + + stream: bool + + +class BodyTranslation(TypedDict, total=False): + target_lang: Required[str] + + text: Required[str] + + source_lang: str + + +class BodySummarization(TypedDict, total=False): + input_text: Required[str] + + max_length: int + + +class BodyUnionMember15(TypedDict, total=False): + image: Iterable[float] + + max_tokens: int + + prompt: str + + +Body = Union[ + BodyTextClassification, + BodyTextToImage, + BodySentenceSimilarity, + BodyTextEmbeddings, + FileTypes, + BodyAudio, + FileTypes, + BodyImage, + FileTypes, + BodyImage, + BodyUnionMember10, + BodyUnionMember11, + BodyTranslation, + BodySummarization, + FileTypes, + BodyUnionMember15, +] diff --git a/src/cloudflare/types/workers/ai_run_response.py b/src/cloudflare/types/workers/ai_run_response.py new file mode 100644 index 000000000..0838dcdb0 --- /dev/null +++ b/src/cloudflare/types/workers/ai_run_response.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Union, Optional + +from ..._models import BaseModel + +__all__ = [ + "AIRunResponse", + "TextClassification", + "TextEmbeddings", + "SpeechRecognition", + "SpeechRecognitionWord", + "ImageClassification", + "ObjectDetection", + "ObjectDetectionBox", + "Response", + "Translation", + "Summarization", + "ImageToText", +] + + +class TextClassification(BaseModel): + label: Optional[str] = None + + score: Optional[float] = None + + +class TextEmbeddings(BaseModel): + data: Optional[List[List[float]]] = None + + shape: Optional[List[float]] = None + + +class SpeechRecognitionWord(BaseModel): + end: Optional[float] = None + + start: Optional[float] = None + + word: Optional[str] = None + + +class SpeechRecognition(BaseModel): + text: str + + word_count: Optional[float] = None + + words: Optional[List[SpeechRecognitionWord]] = None + + +class ImageClassification(BaseModel): + label: Optional[str] = None + + score: Optional[float] = None + + +class ObjectDetectionBox(BaseModel): + xmax: Optional[float] = None + + xmin: Optional[float] = None + + ymax: Optional[float] = None + + ymin: Optional[float] = None + + +class ObjectDetection(BaseModel): + box: Optional[ObjectDetectionBox] = None + + label: Optional[str] = None + + score: Optional[float] = None + + +class Response(BaseModel): + response: Optional[str] = None + + +class Translation(BaseModel): + translated_text: Optional[str] = None + + +class Summarization(BaseModel): + summary: Optional[str] = None + + +class ImageToText(BaseModel): + description: Optional[str] = None + + +AIRunResponse = Union[ + List[TextClassification], + object, + List[float], + TextEmbeddings, + SpeechRecognition, + List[ImageClassification], + List[ObjectDetection], + Response, + object, + Translation, + Summarization, + ImageToText, +] diff --git a/tests/api_resources/workers/test_ai.py b/tests/api_resources/workers/test_ai.py index 2f30eac63..d03db0504 100644 --- a/tests/api_resources/workers/test_ai.py +++ b/tests/api_resources/workers/test_ai.py @@ -3,12 +3,13 @@ from __future__ import annotations import os -from typing import Any, cast +from typing import Any, Optional, cast import pytest from cloudflare import Cloudflare, AsyncCloudflare from tests.utils import assert_matches_type +from cloudflare.types.workers import AIRunResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -22,9 +23,19 @@ class TestAI: ai = client.workers.ai.run( "string", account_id="023e105f4ecef8ad9ca31a8372d0c353", - body={}, + body={"text": "string"}, ) - assert_matches_type(object, ai, path=["response"]) + assert_matches_type(Optional[AIRunResponse], ai, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_run_with_all_params(self, client: Cloudflare) -> None: + ai = client.workers.ai.run( + "string", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + body={"text": "string"}, + ) + assert_matches_type(Optional[AIRunResponse], ai, path=["response"]) @pytest.mark.skip() @parametrize @@ -32,13 +43,13 @@ class TestAI: response = client.workers.ai.with_raw_response.run( "string", account_id="023e105f4ecef8ad9ca31a8372d0c353", - body={}, + body={"text": "string"}, ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" ai = response.parse() - assert_matches_type(object, ai, path=["response"]) + assert_matches_type(Optional[AIRunResponse], ai, path=["response"]) @pytest.mark.skip() @parametrize @@ -46,13 +57,13 @@ class TestAI: with client.workers.ai.with_streaming_response.run( "string", account_id="023e105f4ecef8ad9ca31a8372d0c353", - body={}, + body={"text": "string"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" ai = response.parse() - assert_matches_type(object, ai, path=["response"]) + assert_matches_type(Optional[AIRunResponse], ai, path=["response"]) assert cast(Any, response.is_closed) is True @@ -63,14 +74,14 @@ class TestAI: client.workers.ai.with_raw_response.run( "string", account_id="", - body={}, + body={"text": "string"}, ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `model_name` but received ''"): client.workers.ai.with_raw_response.run( "", account_id="023e105f4ecef8ad9ca31a8372d0c353", - body={}, + body={"text": "string"}, ) @@ -83,9 +94,19 @@ class TestAsyncAI: ai = await async_client.workers.ai.run( "string", account_id="023e105f4ecef8ad9ca31a8372d0c353", - body={}, + body={"text": "string"}, ) - assert_matches_type(object, ai, path=["response"]) + assert_matches_type(Optional[AIRunResponse], ai, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_run_with_all_params(self, async_client: AsyncCloudflare) -> None: + ai = await async_client.workers.ai.run( + "string", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + body={"text": "string"}, + ) + assert_matches_type(Optional[AIRunResponse], ai, path=["response"]) @pytest.mark.skip() @parametrize @@ -93,13 +114,13 @@ class TestAsyncAI: response = await async_client.workers.ai.with_raw_response.run( "string", account_id="023e105f4ecef8ad9ca31a8372d0c353", - body={}, + body={"text": "string"}, ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" ai = await response.parse() - assert_matches_type(object, ai, path=["response"]) + assert_matches_type(Optional[AIRunResponse], ai, path=["response"]) @pytest.mark.skip() @parametrize @@ -107,13 +128,13 @@ class TestAsyncAI: async with async_client.workers.ai.with_streaming_response.run( "string", account_id="023e105f4ecef8ad9ca31a8372d0c353", - body={}, + body={"text": "string"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" ai = await response.parse() - assert_matches_type(object, ai, path=["response"]) + assert_matches_type(Optional[AIRunResponse], ai, path=["response"]) assert cast(Any, response.is_closed) is True @@ -124,12 +145,12 @@ class TestAsyncAI: await async_client.workers.ai.with_raw_response.run( "string", account_id="", - body={}, + body={"text": "string"}, ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `model_name` but received ''"): await async_client.workers.ai.with_raw_response.run( "", account_id="023e105f4ecef8ad9ca31a8372d0c353", - body={}, + body={"text": "string"}, )