diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c4bf1b6c0..e72f11310 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.13.3" + ".": "1.14.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 379a3990c..b1a5e6c48 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 124 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-11588dad716c3ded3699f14d5e1c5f847d6d099ce0595430f608386b2d1d7c46.yml -openapi_spec_hash: 60c33284c8248c346994db8b2b399758 -config_hash: a759c23a5a04ad26f8740acc7e094c01 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-f32de1991bef7651f9b0970b503e2986c7a88708c4a408d54abe6d089795e9f9.yml +openapi_spec_hash: d2005d48f75fba4a5368209cf49641dc +config_hash: 5ffcc19219880e7d9ce43c1e6860568c diff --git a/CHANGELOG.md b/CHANGELOG.md index ac31ebc9c..70c9db0e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 1.14.0 (2026-03-31) + +Full Changelog: [v1.13.3...v1.14.0](https://github.com/runloopai/api-client-python/compare/v1.13.3...v1.14.0) + +### Features + +* add archive / unarchive to benchmarks ([#8391](https://github.com/runloopai/api-client-python/issues/8391)) ([31ebb2c](https://github.com/runloopai/api-client-python/commit/31ebb2cc196e185eaef2ee8d6ddcd3e04b0a6ece)) +* Add axon list filters by id and name ([#8384](https://github.com/runloopai/api-client-python/issues/8384)) ([cd722b3](https://github.com/runloopai/api-client-python/commit/cd722b341a528558a5736894aa27bab44aa4a06e)) +* Add pagination to list active axons ([#8359](https://github.com/runloopai/api-client-python/issues/8359)) ([1937bc9](https://github.com/runloopai/api-client-python/commit/1937bc9c660895d9355d37a1f052f00fde9128e1)) +* **internal:** implement indices array format for query and form serialization ([1239661](https://github.com/runloopai/api-client-python/commit/12396614f8151d5a244c6e91815665caa6cd4c1b)) + + +### Bug Fixes + +* **sdk:** expose axon list params ([#773](https://github.com/runloopai/api-client-python/issues/773)) ([47e208d](https://github.com/runloopai/api-client-python/commit/47e208d61c247719174f5c0625803355114fa773)) +* **tests:** poll for logs in smoke tests to handle CloudWatch ingestion latency ([#774](https://github.com/runloopai/api-client-python/issues/774)) ([5f1e187](https://github.com/runloopai/api-client-python/commit/5f1e18703bd18370d1f63d7bbb2f6a22716624ff)) + + +### Chores + +* **axon:** add axon auto-pagination to stainless sdks ([#8420](https://github.com/runloopai/api-client-python/issues/8420)) ([14806e1](https://github.com/runloopai/api-client-python/commit/14806e18b4b8d2164ee8db80ebe62f853f7885bf)) + + +### Documentation + +* **api:** document vCPU, RAM, and disk for resource_size_request ([#8368](https://github.com/runloopai/api-client-python/issues/8368)) ([1edfb84](https://github.com/runloopai/api-client-python/commit/1edfb840f46ac3295ef097a7574eecb809e2f1d5)) + ## 1.13.3 (2026-03-26) Full Changelog: [v1.13.2...v1.13.3](https://github.com/runloopai/api-client-python/compare/v1.13.2...v1.13.3) diff --git a/api.md b/api.md index 239d793fb..14d010381 100644 --- a/api.md +++ b/api.md @@ -107,7 +107,7 @@ Methods: - client.axons.create(\*\*params) -> AxonView - client.axons.retrieve(id) -> AxonView -- client.axons.list() -> AxonListView +- client.axons.list(\*\*params) -> SyncAxonsCursorIDPage[AxonView] - client.axons.publish(id, \*\*params) -> PublishResultView - client.axons.subscribe_sse(id) -> AxonEventView diff --git a/pyproject.toml b/pyproject.toml index 0eff0cd53..93bd1ad77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "1.13.3" +version = "1.14.0" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" diff --git a/scripts/mock b/scripts/mock index 09eb49f65..290e21b9a 100755 --- a/scripts/mock +++ b/scripts/mock @@ -24,7 +24,7 @@ if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 10fadae13..a631738f5 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 diff --git a/src/runloop_api_client/_qs.py b/src/runloop_api_client/_qs.py index ada6fd3f7..de8c99bc6 100644 --- a/src/runloop_api_client/_qs.py +++ b/src/runloop_api_client/_qs.py @@ -101,7 +101,10 @@ def _stringify_item( items.extend(self._stringify_item(key, item, opts)) return items elif array_format == "indices": - raise NotImplementedError("The array indices format is not supported yet") + items = [] + for i, item in enumerate(value): + items.extend(self._stringify_item(f"{key}[{i}]", item, opts)) + return items elif array_format == "brackets": items = [] key = key + "[]" diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index a9339227a..51ea16bb5 100644 --- a/src/runloop_api_client/_version.py +++ b/src/runloop_api_client/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "runloop_api_client" -__version__ = "1.13.3" # x-release-please-version +__version__ = "1.14.0" # x-release-please-version diff --git a/src/runloop_api_client/pagination.py b/src/runloop_api_client/pagination.py index e084ef68e..9d1fd9491 100644 --- a/src/runloop_api_client/pagination.py +++ b/src/runloop_api_client/pagination.py @@ -18,6 +18,8 @@ "AsyncBenchmarksCursorIDPage", "SyncAgentsCursorIDPage", "AsyncAgentsCursorIDPage", + "SyncAxonsCursorIDPage", + "AsyncAxonsCursorIDPage", "SyncBenchmarkRunsCursorIDPage", "AsyncBenchmarkRunsCursorIDPage", "SyncScenariosCursorIDPage", @@ -69,6 +71,11 @@ class AgentsCursorIDPageItem(Protocol): id: str +@runtime_checkable +class AxonsCursorIDPageItem(Protocol): + id: str + + @runtime_checkable class BenchmarkRunsCursorIDPageItem(Protocol): id: str @@ -517,6 +524,74 @@ def next_page_info(self) -> Optional[PageInfo]: return PageInfo(params={"starting_after": item.id}) +class SyncAxonsCursorIDPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + axons: List[_T] + has_more: Optional[bool] = None + total_count: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + axons = self.axons + if not axons: + return [] + return axons + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + axons = self.axons + if not axons: + return None + + item = cast(Any, axons[-1]) + if not isinstance(item, AxonsCursorIDPageItem) or item.id is None: # pyright: ignore[reportUnnecessaryComparison] + # TODO emit warning log + return None + + return PageInfo(params={"starting_after": item.id}) + + +class AsyncAxonsCursorIDPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + axons: List[_T] + has_more: Optional[bool] = None + total_count: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + axons = self.axons + if not axons: + return [] + return axons + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + axons = self.axons + if not axons: + return None + + item = cast(Any, axons[-1]) + if not isinstance(item, AxonsCursorIDPageItem) or item.id is None: # pyright: ignore[reportUnnecessaryComparison] + # TODO emit warning log + return None + + return PageInfo(params={"starting_after": item.id}) + + class SyncBenchmarkRunsCursorIDPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): runs: List[_T] has_more: Optional[bool] = None diff --git a/src/runloop_api_client/resources/axons/axons.py b/src/runloop_api_client/resources/axons/axons.py index 097fe2330..54f4cb9ff 100644 --- a/src/runloop_api_client/resources/axons/axons.py +++ b/src/runloop_api_client/resources/axons/axons.py @@ -15,7 +15,7 @@ SqlResourceWithStreamingResponse, AsyncSqlResourceWithStreamingResponse, ) -from ...types import axon_create_params, axon_publish_params +from ...types import axon_list_params, axon_create_params, axon_publish_params from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property @@ -27,9 +27,9 @@ async_to_streamed_response_wrapper, ) from ..._streaming import Stream, AsyncStream -from ..._base_client import make_request_options +from ...pagination import SyncAxonsCursorIDPage, AsyncAxonsCursorIDPage +from ..._base_client import AsyncPaginator, make_request_options from ...types.axon_view import AxonView -from ...types.axon_list_view import AxonListView from ...types.axon_event_view import AxonEventView from ...types.publish_result_view import PublishResultView @@ -137,20 +137,61 @@ def retrieve( def list( self, *, + id: str | Omit = omit, + include_total_count: bool | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + starting_after: str | Omit = omit, # 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, - ) -> AxonListView: - """[Beta] List all active axons.""" - return self._get( + ) -> SyncAxonsCursorIDPage[AxonView]: + """ + [Beta] List all active axons. + + Args: + id: Filter by axon ID. + + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + + limit: The limit of items to return. Default is 20. Max is 5000. + + name: Filter by axon name (prefix match supported). + + starting_after: Load the next page of data starting after the item with the given 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_api_list( "/v1/axons", + page=SyncAxonsCursorIDPage[AxonView], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "include_total_count": include_total_count, + "limit": limit, + "name": name, + "starting_after": starting_after, + }, + axon_list_params.AxonListParams, + ), ), - cast_to=AxonListView, + model=AxonView, ) def publish( @@ -350,23 +391,64 @@ async def retrieve( cast_to=AxonView, ) - async def list( + def list( self, *, + id: str | Omit = omit, + include_total_count: bool | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + starting_after: str | Omit = omit, # 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, - ) -> AxonListView: - """[Beta] List all active axons.""" - return await self._get( + ) -> AsyncPaginator[AxonView, AsyncAxonsCursorIDPage[AxonView]]: + """ + [Beta] List all active axons. + + Args: + id: Filter by axon ID. + + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + + limit: The limit of items to return. Default is 20. Max is 5000. + + name: Filter by axon name (prefix match supported). + + starting_after: Load the next page of data starting after the item with the given 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_api_list( "/v1/axons", + page=AsyncAxonsCursorIDPage[AxonView], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "include_total_count": include_total_count, + "limit": limit, + "name": name, + "starting_after": starting_after, + }, + axon_list_params.AxonListParams, + ), ), - cast_to=AxonListView, + model=AxonView, ) async def publish( diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 1f016f2eb..3e46b17b6 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -761,6 +761,7 @@ def enable_tunnel( *, auth_mode: Optional[Literal["open", "authenticated"]] | Omit = omit, http_keep_alive: Optional[bool] | Omit = omit, + wake_on_http: Optional[bool] | Omit = omit, # 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, @@ -783,6 +784,9 @@ def enable_tunnel( http_keep_alive: When true, HTTP traffic through the tunnel counts as activity for idle lifecycle policies, resetting the idle timer. Defaults to true if not specified. + wake_on_http: When true, HTTP traffic to a suspended devbox will automatically trigger a + resume. Defaults to false if not specified. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -801,6 +805,7 @@ def enable_tunnel( { "auth_mode": auth_mode, "http_keep_alive": http_keep_alive, + "wake_on_http": wake_on_http, }, devbox_enable_tunnel_params.DevboxEnableTunnelParams, ), @@ -2397,6 +2402,7 @@ async def enable_tunnel( *, auth_mode: Optional[Literal["open", "authenticated"]] | Omit = omit, http_keep_alive: Optional[bool] | Omit = omit, + wake_on_http: Optional[bool] | Omit = omit, # 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, @@ -2419,6 +2425,9 @@ async def enable_tunnel( http_keep_alive: When true, HTTP traffic through the tunnel counts as activity for idle lifecycle policies, resetting the idle timer. Defaults to true if not specified. + wake_on_http: When true, HTTP traffic to a suspended devbox will automatically trigger a + resume. Defaults to false if not specified. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2437,6 +2446,7 @@ async def enable_tunnel( { "auth_mode": auth_mode, "http_keep_alive": http_keep_alive, + "wake_on_http": wake_on_http, }, devbox_enable_tunnel_params.DevboxEnableTunnelParams, ), diff --git a/src/runloop_api_client/sdk/_types.py b/src/runloop_api_client/sdk/_types.py index aa7e0208a..4d2accc46 100644 --- a/src/runloop_api_client/sdk/_types.py +++ b/src/runloop_api_client/sdk/_types.py @@ -4,6 +4,7 @@ from ..types import ( InputContext, ScenarioView, + AxonListParams, AgentListParams, AxonCreateParams, DevboxListParams, @@ -190,6 +191,10 @@ class SDKAgentListParams(AgentListParams, BaseRequestOptions): pass +class SDKAxonListParams(AxonListParams, BaseRequestOptions): + pass + + class SDKAxonCreateParams(AxonCreateParams, LongRequestOptions): pass diff --git a/src/runloop_api_client/sdk/async_.py b/src/runloop_api_client/sdk/async_.py index c2a237da8..d02a6a9f1 100644 --- a/src/runloop_api_client/sdk/async_.py +++ b/src/runloop_api_client/sdk/async_.py @@ -11,6 +11,7 @@ import httpx from ._types import ( + SDKAxonListParams, BaseRequestOptions, LongRequestOptions, SDKAgentListParams, @@ -544,9 +545,14 @@ def from_id(self, axon_id: str) -> AsyncAxon: """Get an AsyncAxon instance for an existing axon ID.""" return AsyncAxon(self._client, axon_id) - async def list(self, **options: Unpack[BaseRequestOptions]) -> list[AsyncAxon]: - """[Beta] List all active axons.""" - result = await self._client.axons.list(**options) + async def list(self, **params: Unpack[SDKAxonListParams]) -> list[AsyncAxon]: + """[Beta] List all active axons. + + :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKAxonListParams` for available parameters + :return: Collection of axon wrappers + :rtype: list[AsyncAxon] + """ + result = await self._client.axons.list(**params) return [AsyncAxon(self._client, axon.id) for axon in result.axons] diff --git a/src/runloop_api_client/sdk/sync.py b/src/runloop_api_client/sdk/sync.py index 0fa9f8cea..e6b672159 100644 --- a/src/runloop_api_client/sdk/sync.py +++ b/src/runloop_api_client/sdk/sync.py @@ -12,6 +12,7 @@ from .axon import Axon from .agent import Agent from ._types import ( + SDKAxonListParams, BaseRequestOptions, LongRequestOptions, SDKAgentListParams, @@ -539,9 +540,14 @@ def from_id(self, axon_id: str) -> Axon: """Get an Axon instance for an existing axon ID.""" return Axon(self._client, axon_id) - def list(self, **options: Unpack[BaseRequestOptions]) -> list[Axon]: - """[Beta] List all active axons.""" - result = self._client.axons.list(**options) + def list(self, **params: Unpack[SDKAxonListParams]) -> list[Axon]: + """[Beta] List all active axons. + + :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKAxonListParams` for available parameters + :return: Collection of axon wrappers + :rtype: list[Axon] + """ + result = self._client.axons.list(**params) return [Axon(self._client, axon.id) for axon in result.axons] diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py index 77ac06e96..bf5d8ef3f 100644 --- a/src/runloop_api_client/types/__init__.py +++ b/src/runloop_api_client/types/__init__.py @@ -27,6 +27,7 @@ from .agent_list_view import AgentListView as AgentListView from .axon_event_view import AxonEventView as AxonEventView from .mcp_config_view import McpConfigView as McpConfigView +from .axon_list_params import AxonListParams as AxonListParams from .devbox_list_view import DevboxListView as DevboxListView from .object_list_view import ObjectListView as ObjectListView from .scoring_contract import ScoringContract as ScoringContract diff --git a/src/runloop_api_client/types/axon_list_params.py b/src/runloop_api_client/types/axon_list_params.py new file mode 100644 index 000000000..dbe4cdb80 --- /dev/null +++ b/src/runloop_api_client/types/axon_list_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AxonListParams"] + + +class AxonListParams(TypedDict, total=False): + id: str + """Filter by axon ID.""" + + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + + limit: int + """The limit of items to return. Default is 20. Max is 5000.""" + + name: str + """Filter by axon name (prefix match supported).""" + + starting_after: str + """Load the next page of data starting after the item with the given ID.""" diff --git a/src/runloop_api_client/types/axon_list_view.py b/src/runloop_api_client/types/axon_list_view.py index bd6c4d31c..4bd0bef08 100644 --- a/src/runloop_api_client/types/axon_list_view.py +++ b/src/runloop_api_client/types/axon_list_view.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List +from typing import List, Optional from .._models import BaseModel from .axon_view import AxonView @@ -11,3 +11,7 @@ class AxonListView(BaseModel): axons: List[AxonView] """List of active axons.""" + + has_more: bool + + total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/benchmark_view.py b/src/runloop_api_client/types/benchmark_view.py index 4150847ac..1eb52fb6e 100644 --- a/src/runloop_api_client/types/benchmark_view.py +++ b/src/runloop_api_client/types/benchmark_view.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 pydantic import Field as FieldInfo @@ -26,6 +27,12 @@ class BenchmarkView(BaseModel): scenario_ids: List[str] = FieldInfo(alias="scenarioIds") """List of Scenario IDs that make up the benchmark.""" + status: Literal["active", "archived"] + """Whether the benchmark is active or archived. + + Archived benchmarks are excluded from listings and cannot be run. + """ + attribution: Optional[str] = None """Attribution information for the benchmark.""" diff --git a/src/runloop_api_client/types/devbox_create_params.py b/src/runloop_api_client/types/devbox_create_params.py index c72820259..1b41796b2 100644 --- a/src/runloop_api_client/types/devbox_create_params.py +++ b/src/runloop_api_client/types/devbox_create_params.py @@ -149,3 +149,9 @@ class Tunnel(TypedDict, total=False): When true, HTTP traffic through the tunnel counts as activity for idle lifecycle policies, resetting the idle timer. Defaults to true if not specified. """ + + wake_on_http: Optional[bool] + """ + When true, HTTP traffic to a suspended devbox will automatically trigger a + resume. Defaults to false if not specified. + """ diff --git a/src/runloop_api_client/types/devbox_enable_tunnel_params.py b/src/runloop_api_client/types/devbox_enable_tunnel_params.py index 4906c85ef..cd380ebb4 100644 --- a/src/runloop_api_client/types/devbox_enable_tunnel_params.py +++ b/src/runloop_api_client/types/devbox_enable_tunnel_params.py @@ -17,3 +17,9 @@ class DevboxEnableTunnelParams(TypedDict, total=False): When true, HTTP traffic through the tunnel counts as activity for idle lifecycle policies, resetting the idle timer. Defaults to true if not specified. """ + + wake_on_http: Optional[bool] + """ + When true, HTTP traffic to a suspended devbox will automatically trigger a + resume. Defaults to false if not specified. + """ diff --git a/src/runloop_api_client/types/shared/launch_parameters.py b/src/runloop_api_client/types/shared/launch_parameters.py index 7c1fdc2a7..f882691e0 100644 --- a/src/runloop_api_client/types/shared/launch_parameters.py +++ b/src/runloop_api_client/types/shared/launch_parameters.py @@ -76,7 +76,14 @@ class LaunchParameters(BaseModel): resource_size_request: Optional[ Literal["X_SMALL", "SMALL", "MEDIUM", "LARGE", "X_LARGE", "XX_LARGE", "CUSTOM_SIZE"] ] = None - """Manual resource configuration for Devbox. If not set, defaults will be used.""" + """Preset Devbox resources (vCPU, RAM in GiB, ephemeral disk in GiB). + + If not set, SMALL is used. X_SMALL: 0.5 vCPU, 1 GiB RAM, 4 GiB disk. SMALL: 1 + vCPU, 2 GiB RAM, 4 GiB disk. MEDIUM: 2 vCPU, 4 GiB RAM, 8 GiB disk. LARGE: 2 + vCPU, 8 GiB RAM, 16 GiB disk. X_LARGE: 4 vCPU, 16 GiB RAM, 16 GiB disk. + XX_LARGE: 8 vCPU, 32 GiB RAM, 16 GiB disk. CUSTOM_SIZE: set custom_cpu_cores, + custom_gb_memory, and optionally custom_disk_size. + """ user_parameters: Optional[UserParameters] = None """Specify the user for execution on Devbox. diff --git a/src/runloop_api_client/types/shared_params/launch_parameters.py b/src/runloop_api_client/types/shared_params/launch_parameters.py index c30f90d86..65babd117 100644 --- a/src/runloop_api_client/types/shared_params/launch_parameters.py +++ b/src/runloop_api_client/types/shared_params/launch_parameters.py @@ -78,7 +78,14 @@ class LaunchParameters(TypedDict, total=False): resource_size_request: Optional[ Literal["X_SMALL", "SMALL", "MEDIUM", "LARGE", "X_LARGE", "XX_LARGE", "CUSTOM_SIZE"] ] - """Manual resource configuration for Devbox. If not set, defaults will be used.""" + """Preset Devbox resources (vCPU, RAM in GiB, ephemeral disk in GiB). + + If not set, SMALL is used. X_SMALL: 0.5 vCPU, 1 GiB RAM, 4 GiB disk. SMALL: 1 + vCPU, 2 GiB RAM, 4 GiB disk. MEDIUM: 2 vCPU, 4 GiB RAM, 8 GiB disk. LARGE: 2 + vCPU, 8 GiB RAM, 16 GiB disk. X_LARGE: 4 vCPU, 16 GiB RAM, 16 GiB disk. + XX_LARGE: 8 vCPU, 32 GiB RAM, 16 GiB disk. CUSTOM_SIZE: set custom_cpu_cores, + custom_gb_memory, and optionally custom_disk_size. + """ user_parameters: Optional[UserParameters] """Specify the user for execution on Devbox. diff --git a/src/runloop_api_client/types/tunnel_view.py b/src/runloop_api_client/types/tunnel_view.py index 9287cd4d2..6188968a0 100644 --- a/src/runloop_api_client/types/tunnel_view.py +++ b/src/runloop_api_client/types/tunnel_view.py @@ -33,6 +33,12 @@ class TunnelView(BaseModel): URL format: https://{port}-{tunnel_key}.tunnel.runloop.{domain} """ + wake_on_http: bool + """ + When true, HTTP traffic to a suspended devbox will automatically trigger a + resume. + """ + auth_token: Optional[str] = None """Bearer token for tunnel authentication. diff --git a/tests/api_resources/test_axons.py b/tests/api_resources/test_axons.py index bda5f1a23..ab076037a 100644 --- a/tests/api_resources/test_axons.py +++ b/tests/api_resources/test_axons.py @@ -9,7 +9,11 @@ from tests.utils import assert_matches_type from runloop_api_client import Runloop, AsyncRunloop -from runloop_api_client.types import AxonView, AxonListView, PublishResultView +from runloop_api_client.types import ( + AxonView, + PublishResultView, +) +from runloop_api_client.pagination import SyncAxonsCursorIDPage, AsyncAxonsCursorIDPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -90,7 +94,18 @@ def test_path_params_retrieve(self, client: Runloop) -> None: @parametrize def test_method_list(self, client: Runloop) -> None: axon = client.axons.list() - assert_matches_type(AxonListView, axon, path=["response"]) + assert_matches_type(SyncAxonsCursorIDPage[AxonView], axon, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Runloop) -> None: + axon = client.axons.list( + id="id", + include_total_count=True, + limit=0, + name="name", + starting_after="starting_after", + ) + assert_matches_type(SyncAxonsCursorIDPage[AxonView], axon, path=["response"]) @parametrize def test_raw_response_list(self, client: Runloop) -> None: @@ -99,7 +114,7 @@ def test_raw_response_list(self, client: Runloop) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" axon = response.parse() - assert_matches_type(AxonListView, axon, path=["response"]) + assert_matches_type(SyncAxonsCursorIDPage[AxonView], axon, path=["response"]) @parametrize def test_streaming_response_list(self, client: Runloop) -> None: @@ -108,7 +123,7 @@ def test_streaming_response_list(self, client: Runloop) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" axon = response.parse() - assert_matches_type(AxonListView, axon, path=["response"]) + assert_matches_type(SyncAxonsCursorIDPage[AxonView], axon, path=["response"]) assert cast(Any, response.is_closed) is True @@ -282,7 +297,18 @@ async def test_path_params_retrieve(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list(self, async_client: AsyncRunloop) -> None: axon = await async_client.axons.list() - assert_matches_type(AxonListView, axon, path=["response"]) + assert_matches_type(AsyncAxonsCursorIDPage[AxonView], axon, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: + axon = await async_client.axons.list( + id="id", + include_total_count=True, + limit=0, + name="name", + starting_after="starting_after", + ) + assert_matches_type(AsyncAxonsCursorIDPage[AxonView], axon, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncRunloop) -> None: @@ -291,7 +317,7 @@ async def test_raw_response_list(self, async_client: AsyncRunloop) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" axon = await response.parse() - assert_matches_type(AxonListView, axon, path=["response"]) + assert_matches_type(AsyncAxonsCursorIDPage[AxonView], axon, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None: @@ -300,7 +326,7 @@ async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None assert response.http_request.headers.get("X-Stainless-Lang") == "python" axon = await response.parse() - assert_matches_type(AxonListView, axon, path=["response"]) + assert_matches_type(AsyncAxonsCursorIDPage[AxonView], axon, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py index ab78cd157..c04de1d06 100644 --- a/tests/api_resources/test_devboxes.py +++ b/tests/api_resources/test_devboxes.py @@ -113,6 +113,7 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: tunnel={ "auth_mode": "open", "http_keep_alive": True, + "wake_on_http": True, }, ) assert_matches_type(DevboxView, devbox, path=["response"]) @@ -404,6 +405,7 @@ def test_method_enable_tunnel_with_all_params(self, client: Runloop) -> None: id="id", auth_mode="open", http_keep_alive=True, + wake_on_http=True, ) assert_matches_type(TunnelView, devbox, path=["response"]) @@ -1727,6 +1729,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - tunnel={ "auth_mode": "open", "http_keep_alive": True, + "wake_on_http": True, }, ) assert_matches_type(DevboxView, devbox, path=["response"]) @@ -2018,6 +2021,7 @@ async def test_method_enable_tunnel_with_all_params(self, async_client: AsyncRun id="id", auth_mode="open", http_keep_alive=True, + wake_on_http=True, ) assert_matches_type(TunnelView, devbox, path=["response"])