Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.13.3"
".": "1.14.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
2 changes: 1 addition & 1 deletion api.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Methods:

- <code title="post /v1/axons">client.axons.<a href="./src/runloop_api_client/resources/axons/axons.py">create</a>(\*\*<a href="src/runloop_api_client/types/axon_create_params.py">params</a>) -> <a href="./src/runloop_api_client/types/axon_view.py">AxonView</a></code>
- <code title="get /v1/axons/{id}">client.axons.<a href="./src/runloop_api_client/resources/axons/axons.py">retrieve</a>(id) -> <a href="./src/runloop_api_client/types/axon_view.py">AxonView</a></code>
- <code title="get /v1/axons">client.axons.<a href="./src/runloop_api_client/resources/axons/axons.py">list</a>() -> <a href="./src/runloop_api_client/types/axon_list_view.py">AxonListView</a></code>
- <code title="get /v1/axons">client.axons.<a href="./src/runloop_api_client/resources/axons/axons.py">list</a>(\*\*<a href="src/runloop_api_client/types/axon_list_params.py">params</a>) -> <a href="./src/runloop_api_client/types/axon_view.py">SyncAxonsCursorIDPage[AxonView]</a></code>
- <code title="post /v1/axons/{id}/publish">client.axons.<a href="./src/runloop_api_client/resources/axons/axons.py">publish</a>(id, \*\*<a href="src/runloop_api_client/types/axon_publish_params.py">params</a>) -> <a href="./src/runloop_api_client/types/publish_result_view.py">PublishResultView</a></code>
- <code title="get /v1/axons/{id}/subscribe/sse">client.axons.<a href="./src/runloop_api_client/resources/axons/axons.py">subscribe_sse</a>(id) -> <a href="./src/runloop_api_client/types/axon_event_view.py">AxonEventView</a></code>

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 2 additions & 2 deletions scripts/mock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
2 changes: 1 addition & 1 deletion scripts/test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/runloop_api_client/_qs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 + "[]"
Expand Down
2 changes: 1 addition & 1 deletion src/runloop_api_client/_version.py
Original file line number Diff line number Diff line change
@@ -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
75 changes: 75 additions & 0 deletions src/runloop_api_client/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"AsyncBenchmarksCursorIDPage",
"SyncAgentsCursorIDPage",
"AsyncAgentsCursorIDPage",
"SyncAxonsCursorIDPage",
"AsyncAxonsCursorIDPage",
"SyncBenchmarkRunsCursorIDPage",
"AsyncBenchmarkRunsCursorIDPage",
"SyncScenariosCursorIDPage",
Expand Down Expand Up @@ -69,6 +71,11 @@ class AgentsCursorIDPageItem(Protocol):
id: str


@runtime_checkable
class AxonsCursorIDPageItem(Protocol):
id: str


@runtime_checkable
class BenchmarkRunsCursorIDPageItem(Protocol):
id: str
Expand Down Expand Up @@ -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
Expand Down
110 changes: 96 additions & 14 deletions src/runloop_api_client/resources/axons/axons.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
Loading