diff --git a/.cursor/environment.json b/.cursor/environment.json new file mode 100644 index 00000000..d6830f5b --- /dev/null +++ b/.cursor/environment.json @@ -0,0 +1,5 @@ +{ + "snapshot": "snapshot-20250522-bd1c3d88-262b-44dc-bd01-cb8e8b262337", + "install": "export GOPRIVATE=github.com/onkernel/kernel-go-sdk;\n\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n", + "terminals": [] +} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6b7b74c5..da59f99e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.3.0" + ".": "0.4.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index d4463c35..be606c6d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-3edc7a0eef4a0d4495782efbdb0d9b777a55aee058dab119f90de56019441326.yml -openapi_spec_hash: dff0b1efa1c1614cf770ed8327cefab2 -config_hash: cb04a4d88ee9f530b303ca57ff7090b3 +configured_endpoints: 11 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-64ccdff4ca5d73d79d89e817fe83ccfd3d529696df3e6818c3c75e586ae00801.yml +openapi_spec_hash: 21c7b8757fc0cc9415cda1bc06251de6 +config_hash: b3fcacd707da56b21d31ce0baf4fb87d diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d36628e..db6db724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.4.0 (2025-05-28) + +Full Changelog: [v0.3.0...v0.4.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.3.0...v0.4.0) + +### Features + +* **api:** update via SDK Studio ([eda6c2c](https://github.com/onkernel/kernel-python-sdk/commit/eda6c2c9ec1f585b8546c629bb661f0f9a9e9c04)) + ## 0.3.0 (2025-05-22) Full Changelog: [v0.2.0...v0.3.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.2.0...v0.3.0) diff --git a/api.md b/api.md index e94025bc..cbacba5d 100644 --- a/api.md +++ b/api.md @@ -28,13 +28,18 @@ Methods: Types: ```python -from kernel.types.apps import InvocationCreateResponse, InvocationRetrieveResponse +from kernel.types.apps import ( + InvocationCreateResponse, + InvocationRetrieveResponse, + InvocationUpdateResponse, +) ``` Methods: - client.apps.invocations.create(\*\*params) -> InvocationCreateResponse - client.apps.invocations.retrieve(id) -> InvocationRetrieveResponse +- client.apps.invocations.update(id, \*\*params) -> InvocationUpdateResponse # Browsers diff --git a/pyproject.toml b/pyproject.toml index d4bf1e30..ed0ec2d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.3.0" +version = "0.4.0" description = "The official Python library for the kernel API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/kernel/_version.py b/src/kernel/_version.py index bd4f5195..e2017455 100644 --- a/src/kernel/_version.py +++ b/src/kernel/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "kernel" -__version__ = "0.3.0" # x-release-please-version +__version__ = "0.4.0" # x-release-please-version diff --git a/src/kernel/resources/apps/invocations.py b/src/kernel/resources/apps/invocations.py index 44015013..3f1f495a 100644 --- a/src/kernel/resources/apps/invocations.py +++ b/src/kernel/resources/apps/invocations.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing_extensions import Literal + import httpx from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven @@ -14,9 +16,10 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ...types.apps import invocation_create_params +from ...types.apps import invocation_create_params, invocation_update_params from ..._base_client import make_request_options from ...types.apps.invocation_create_response import InvocationCreateResponse +from ...types.apps.invocation_update_response import InvocationUpdateResponse from ...types.apps.invocation_retrieve_response import InvocationRetrieveResponse __all__ = ["InvocationsResource", "AsyncInvocationsResource"] @@ -48,6 +51,7 @@ def create( action_name: str, app_name: str, version: str, + async_: bool | NotGiven = NOT_GIVEN, payload: 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. @@ -66,6 +70,9 @@ def create( version: Version of the application + async_: If true, invoke asynchronously. When set, the API responds 202 Accepted with + status "queued". + payload: Input data for the action, sent as a JSON string. extra_headers: Send extra headers @@ -83,6 +90,7 @@ def create( "action_name": action_name, "app_name": app_name, "version": version, + "async_": async_, "payload": payload, }, invocation_create_params.InvocationCreateParams, @@ -126,6 +134,52 @@ def retrieve( cast_to=InvocationRetrieveResponse, ) + def update( + self, + id: str, + *, + status: Literal["succeeded", "failed"], + output: 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, + ) -> InvocationUpdateResponse: + """ + Update invocation status or output + + Args: + status: New status for the invocation. + + output: Updated output of the invocation rendered as JSON string. + + 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 + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._patch( + f"/invocations/{id}", + body=maybe_transform( + { + "status": status, + "output": output, + }, + invocation_update_params.InvocationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InvocationUpdateResponse, + ) + class AsyncInvocationsResource(AsyncAPIResource): @cached_property @@ -153,6 +207,7 @@ async def create( action_name: str, app_name: str, version: str, + async_: bool | NotGiven = NOT_GIVEN, payload: 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. @@ -171,6 +226,9 @@ async def create( version: Version of the application + async_: If true, invoke asynchronously. When set, the API responds 202 Accepted with + status "queued". + payload: Input data for the action, sent as a JSON string. extra_headers: Send extra headers @@ -188,6 +246,7 @@ async def create( "action_name": action_name, "app_name": app_name, "version": version, + "async_": async_, "payload": payload, }, invocation_create_params.InvocationCreateParams, @@ -231,6 +290,52 @@ async def retrieve( cast_to=InvocationRetrieveResponse, ) + async def update( + self, + id: str, + *, + status: Literal["succeeded", "failed"], + output: 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, + ) -> InvocationUpdateResponse: + """ + Update invocation status or output + + Args: + status: New status for the invocation. + + output: Updated output of the invocation rendered as JSON string. + + 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 + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._patch( + f"/invocations/{id}", + body=await async_maybe_transform( + { + "status": status, + "output": output, + }, + invocation_update_params.InvocationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InvocationUpdateResponse, + ) + class InvocationsResourceWithRawResponse: def __init__(self, invocations: InvocationsResource) -> None: @@ -242,6 +347,9 @@ def __init__(self, invocations: InvocationsResource) -> None: self.retrieve = to_raw_response_wrapper( invocations.retrieve, ) + self.update = to_raw_response_wrapper( + invocations.update, + ) class AsyncInvocationsResourceWithRawResponse: @@ -254,6 +362,9 @@ def __init__(self, invocations: AsyncInvocationsResource) -> None: self.retrieve = async_to_raw_response_wrapper( invocations.retrieve, ) + self.update = async_to_raw_response_wrapper( + invocations.update, + ) class InvocationsResourceWithStreamingResponse: @@ -266,6 +377,9 @@ def __init__(self, invocations: InvocationsResource) -> None: self.retrieve = to_streamed_response_wrapper( invocations.retrieve, ) + self.update = to_streamed_response_wrapper( + invocations.update, + ) class AsyncInvocationsResourceWithStreamingResponse: @@ -278,3 +392,6 @@ def __init__(self, invocations: AsyncInvocationsResource) -> None: self.retrieve = async_to_streamed_response_wrapper( invocations.retrieve, ) + self.update = async_to_streamed_response_wrapper( + invocations.update, + ) diff --git a/src/kernel/types/apps/__init__.py b/src/kernel/types/apps/__init__.py index 425fffd4..f4bf7a25 100644 --- a/src/kernel/types/apps/__init__.py +++ b/src/kernel/types/apps/__init__.py @@ -4,7 +4,9 @@ from .deployment_create_params import DeploymentCreateParams as DeploymentCreateParams from .invocation_create_params import InvocationCreateParams as InvocationCreateParams +from .invocation_update_params import InvocationUpdateParams as InvocationUpdateParams from .deployment_create_response import DeploymentCreateResponse as DeploymentCreateResponse from .deployment_follow_response import DeploymentFollowResponse as DeploymentFollowResponse from .invocation_create_response import InvocationCreateResponse as InvocationCreateResponse +from .invocation_update_response import InvocationUpdateResponse as InvocationUpdateResponse from .invocation_retrieve_response import InvocationRetrieveResponse as InvocationRetrieveResponse diff --git a/src/kernel/types/apps/invocation_create_params.py b/src/kernel/types/apps/invocation_create_params.py index a97a2c5a..01035ae5 100644 --- a/src/kernel/types/apps/invocation_create_params.py +++ b/src/kernel/types/apps/invocation_create_params.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo __all__ = ["InvocationCreateParams"] @@ -17,5 +19,11 @@ class InvocationCreateParams(TypedDict, total=False): version: Required[str] """Version of the application""" + async_: Annotated[bool, PropertyInfo(alias="async")] + """If true, invoke asynchronously. + + When set, the API responds 202 Accepted with status "queued". + """ + payload: str """Input data for the action, sent as a JSON string.""" diff --git a/src/kernel/types/apps/invocation_update_params.py b/src/kernel/types/apps/invocation_update_params.py new file mode 100644 index 00000000..72ccf5d9 --- /dev/null +++ b/src/kernel/types/apps/invocation_update_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InvocationUpdateParams"] + + +class InvocationUpdateParams(TypedDict, total=False): + status: Required[Literal["succeeded", "failed"]] + """New status for the invocation.""" + + output: str + """Updated output of the invocation rendered as JSON string.""" diff --git a/src/kernel/types/apps/invocation_update_response.py b/src/kernel/types/apps/invocation_update_response.py new file mode 100644 index 00000000..c30fc160 --- /dev/null +++ b/src/kernel/types/apps/invocation_update_response.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InvocationUpdateResponse"] + + +class InvocationUpdateResponse(BaseModel): + id: str + """ID of the invocation""" + + action_name: str + """Name of the action invoked""" + + app_name: str + """Name of the application""" + + started_at: datetime + """RFC 3339 Nanoseconds timestamp when the invocation started""" + + status: Literal["queued", "running", "succeeded", "failed"] + """Status of the invocation""" + + finished_at: Optional[datetime] = None + """ + RFC 3339 Nanoseconds timestamp when the invocation finished (null if still + running) + """ + + output: Optional[str] = None + """Output produced by the action, rendered as a JSON string. + + This could be: string, number, boolean, array, object, or null. + """ + + payload: Optional[str] = None + """Payload provided to the invocation. + + This is a string that can be parsed as JSON. + """ + + status_reason: Optional[str] = None + """Status reason""" diff --git a/tests/api_resources/apps/test_invocations.py b/tests/api_resources/apps/test_invocations.py index 61af0316..87dc31fc 100644 --- a/tests/api_resources/apps/test_invocations.py +++ b/tests/api_resources/apps/test_invocations.py @@ -9,7 +9,11 @@ from kernel import Kernel, AsyncKernel from tests.utils import assert_matches_type -from kernel.types.apps import InvocationCreateResponse, InvocationRetrieveResponse +from kernel.types.apps import ( + InvocationCreateResponse, + InvocationUpdateResponse, + InvocationRetrieveResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -34,6 +38,7 @@ def test_method_create_with_all_params(self, client: Kernel) -> None: action_name="analyze", app_name="my-app", version="1.0.0", + async_=True, payload='{"data":"example input"}', ) assert_matches_type(InvocationCreateResponse, invocation, path=["response"]) @@ -110,6 +115,62 @@ def test_path_params_retrieve(self, client: Kernel) -> None: "", ) + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: Kernel) -> None: + invocation = client.apps.invocations.update( + id="id", + status="succeeded", + ) + assert_matches_type(InvocationUpdateResponse, invocation, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: Kernel) -> None: + invocation = client.apps.invocations.update( + id="id", + status="succeeded", + output="output", + ) + assert_matches_type(InvocationUpdateResponse, invocation, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: Kernel) -> None: + response = client.apps.invocations.with_raw_response.update( + id="id", + status="succeeded", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invocation = response.parse() + assert_matches_type(InvocationUpdateResponse, invocation, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: Kernel) -> None: + with client.apps.invocations.with_streaming_response.update( + id="id", + status="succeeded", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invocation = response.parse() + assert_matches_type(InvocationUpdateResponse, invocation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: Kernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.apps.invocations.with_raw_response.update( + id="", + status="succeeded", + ) + class TestAsyncInvocations: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -131,6 +192,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> action_name="analyze", app_name="my-app", version="1.0.0", + async_=True, payload='{"data":"example input"}', ) assert_matches_type(InvocationCreateResponse, invocation, path=["response"]) @@ -206,3 +268,59 @@ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None: await async_client.apps.invocations.with_raw_response.retrieve( "", ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncKernel) -> None: + invocation = await async_client.apps.invocations.update( + id="id", + status="succeeded", + ) + assert_matches_type(InvocationUpdateResponse, invocation, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> None: + invocation = await async_client.apps.invocations.update( + id="id", + status="succeeded", + output="output", + ) + assert_matches_type(InvocationUpdateResponse, invocation, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncKernel) -> None: + response = await async_client.apps.invocations.with_raw_response.update( + id="id", + status="succeeded", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invocation = await response.parse() + assert_matches_type(InvocationUpdateResponse, invocation, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncKernel) -> None: + async with async_client.apps.invocations.with_streaming_response.update( + id="id", + status="succeeded", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invocation = await response.parse() + assert_matches_type(InvocationUpdateResponse, invocation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncKernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.apps.invocations.with_raw_response.update( + id="", + status="succeeded", + )