Skip to content
Open
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.24.0"
".": "1.27.0"
}
8 changes: 4 additions & 4 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 119
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-0b7cd0c2fc193b18189cd7f44cf45ece7726b5d485fb72577f7d235266432ea0.yml
openapi_spec_hash: 78c340dbfb9d3d58b24ef318fc2a657b
config_hash: 218b8d25038e627faab98532392ee9a0
configured_endpoints: 120
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-6fb96a2de54d260cad2b76366cada4eda15335275dbab7f6c108c1bb29acf309.yml
openapi_spec_hash: 6044f4a4da13881c5f82fcc394c1f43b
config_hash: 92c04f0b12249cb2d2bcf9e503a9cbf9
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## 1.27.0 (2026-07-24)

Full Changelog: [v1.24.0...v1.27.0](https://github.com/runloopai/api-client-python/compare/v1.24.0...v1.27.0)

### Features

* **mux:** wait_for_eviction endpoint for flex eviction notifications ([#10325](https://github.com/runloopai/api-client-python/issues/10325)) ([c06794d](https://github.com/runloopai/api-client-python/commit/c06794d413f37be16ee5955fe31b07b92b8adbc8))


### Bug Fixes

* **mux:** field-level merge on network policy egress updates ([#10338](https://github.com/runloopai/api-client-python/issues/10338)) ([cfc97ef](https://github.com/runloopai/api-client-python/commit/cfc97ef55a65e88abb0d76f733cf51dc33823fb0))


### Chores

* **stainless:** generate watch_evictions as an SSE stream ([#10404](https://github.com/runloopai/api-client-python/issues/10404)) ([0413e7b](https://github.com/runloopai/api-client-python/commit/0413e7ba4cd10b919a3fc50e67e0238dc95f4b18))

## 1.24.0 (2026-07-22)

Full Changelog: [v1.23.3...v1.24.0](https://github.com/runloopai/api-client-python/compare/v1.23.3...v1.24.0)
Expand Down
2 changes: 2 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ Types:
```python
from runloop_api_client.types import (
DevboxAsyncExecutionDetailView,
DevboxEvictionEventView,
DevboxExecutionDetailView,
DevboxKillExecutionRequest,
DevboxListView,
Expand Down Expand Up @@ -244,6 +245,7 @@ Methods:
- <code title="post /v1/devboxes/{id}/suspend">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">suspend</a>(id) -> <a href="./src/runloop_api_client/types/devbox_view.py">DevboxView</a></code>
- <code title="post /v1/devboxes/{id}/upload_file">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">upload_file</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_upload_file_params.py">params</a>) -> object</code>
- <code title="post /v1/devboxes/{devbox_id}/executions/{execution_id}/wait_for_status">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">wait_for_command</a>(execution_id, \*, devbox_id, \*\*<a href="src/runloop_api_client/types/devbox_wait_for_command_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_async_execution_detail_view.py">DevboxAsyncExecutionDetailView</a></code>
- <code title="get /v1/devboxes/watch_evictions">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">watch_evictions</a>() -> <a href="./src/runloop_api_client/types/devbox_eviction_event_view.py">DevboxEvictionEventView</a></code>
- <code title="post /v1/devboxes/{id}/write_file_contents">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">write_file_contents</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_write_file_contents_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_execution_detail_view.py">DevboxExecutionDetailView</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.24.0"
version = "1.27.0"
description = "The official Python library for the runloop API"
dynamic = ["readme"]
license = "MIT"
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.24.0" # x-release-please-version
__version__ = "1.27.0" # x-release-please-version
68 changes: 68 additions & 0 deletions src/runloop_api_client/resources/devboxes/devboxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
LONG_POLL_CLIENT_BUFFER_SECONDS,
EXEC_LONG_POLL_SERVER_MAX_SECONDS,
)
from ..._streaming import Stream, AsyncStream
from ...pagination import (
SyncDevboxesCursorIDPage,
AsyncDevboxesCursorIDPage,
Expand All @@ -94,6 +95,7 @@
from ...types.shared_params.mount import Mount
from ...types.devbox_snapshot_view import DevboxSnapshotView
from ...types.shared.launch_parameters import LaunchParameters as SharedLaunchParameters
from ...types.devbox_eviction_event_view import DevboxEvictionEventView
from ...types.devbox_resource_usage_view import DevboxResourceUsageView
from ...types.devbox_execution_detail_view import DevboxExecutionDetailView
from ...types.devbox_create_ssh_key_response import DevboxCreateSSHKeyResponse
Expand Down Expand Up @@ -1742,6 +1744,33 @@ def wait_for_command(
cast_to=DevboxAsyncExecutionDetailView,
)

def watch_evictions(
self,
*,
# 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,
) -> Stream[DevboxEvictionEventView]:
"""
Subscribe, via server-sent events, to pending infrastructure evictions for every
Devbox in the account. On connect the stream emits one event per Devbox that
currently has a pending eviction, then one event as each further eviction is
scheduled. Best-effort and advisory: a Devbox stays running until its deadline,
and delivery is not guaranteed.
"""
return self._get(
"/v1/devboxes/watch_evictions",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=DevboxEvictionEventView,
stream=True,
stream_cls=Stream[DevboxEvictionEventView],
)

def write_file_contents(
self,
id: str,
Expand Down Expand Up @@ -3417,6 +3446,33 @@ async def wait_for_command(
cast_to=DevboxAsyncExecutionDetailView,
)

async def watch_evictions(
self,
*,
# 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,
) -> AsyncStream[DevboxEvictionEventView]:
"""
Subscribe, via server-sent events, to pending infrastructure evictions for every
Devbox in the account. On connect the stream emits one event per Devbox that
currently has a pending eviction, then one event as each further eviction is
scheduled. Best-effort and advisory: a Devbox stays running until its deadline,
and delivery is not guaranteed.
"""
return await self._get(
"/v1/devboxes/watch_evictions",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=DevboxEvictionEventView,
stream=True,
stream_cls=AsyncStream[DevboxEvictionEventView],
)

async def write_file_contents(
self,
id: str,
Expand Down Expand Up @@ -3555,6 +3611,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
self.wait_for_command = to_raw_response_wrapper(
devboxes.wait_for_command,
)
self.watch_evictions = to_raw_response_wrapper(
devboxes.watch_evictions,
)
self.write_file_contents = to_raw_response_wrapper(
devboxes.write_file_contents,
)
Expand Down Expand Up @@ -3651,6 +3710,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
self.wait_for_command = async_to_raw_response_wrapper(
devboxes.wait_for_command,
)
self.watch_evictions = async_to_raw_response_wrapper(
devboxes.watch_evictions,
)
self.write_file_contents = async_to_raw_response_wrapper(
devboxes.write_file_contents,
)
Expand Down Expand Up @@ -3747,6 +3809,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
self.wait_for_command = to_streamed_response_wrapper(
devboxes.wait_for_command,
)
self.watch_evictions = to_streamed_response_wrapper(
devboxes.watch_evictions,
)
self.write_file_contents = to_streamed_response_wrapper(
devboxes.write_file_contents,
)
Expand Down Expand Up @@ -3843,6 +3908,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
self.wait_for_command = async_to_streamed_response_wrapper(
devboxes.wait_for_command,
)
self.watch_evictions = async_to_streamed_response_wrapper(
devboxes.watch_evictions,
)
self.write_file_contents = async_to_streamed_response_wrapper(
devboxes.write_file_contents,
)
Expand Down
6 changes: 4 additions & 2 deletions src/runloop_api_client/resources/network_policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ def update(
) -> NetworkPolicyView:
"""Update an existing NetworkPolicy.

All fields are optional.
All fields are optional - null fields preserve
existing values, provided fields replace entirely.

Args:
allow_agent_gateway: If true, allows devbox egress to the agent gateway.
Expand Down Expand Up @@ -525,7 +526,8 @@ async def update(
) -> NetworkPolicyView:
"""Update an existing NetworkPolicy.

All fields are optional.
All fields are optional - null fields preserve
existing values, provided fields replace entirely.

Args:
allow_agent_gateway: If true, allows devbox egress to the agent gateway.
Expand Down
1 change: 1 addition & 0 deletions src/runloop_api_client/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
from .scenario_start_run_params import ScenarioStartRunParams as ScenarioStartRunParams
from .benchmark_start_run_params import BenchmarkStartRunParams as BenchmarkStartRunParams
from .blueprint_build_parameters import BlueprintBuildParameters as BlueprintBuildParameters
from .devbox_eviction_event_view import DevboxEvictionEventView as DevboxEvictionEventView
from .devbox_execute_sync_params import DevboxExecuteSyncParams as DevboxExecuteSyncParams
from .devbox_resource_usage_view import DevboxResourceUsageView as DevboxResourceUsageView
from .gateway_config_list_params import GatewayConfigListParams as GatewayConfigListParams
Expand Down
16 changes: 16 additions & 0 deletions src/runloop_api_client/types/devbox_eviction_event_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from .._models import BaseModel

__all__ = ["DevboxEvictionEventView"]


class DevboxEvictionEventView(BaseModel):
devbox_id: str
"""The ID of the Devbox with a pending eviction."""

eviction_deadline_ms: int
"""Unix timestamp (milliseconds) after which the Devbox will be suspended.
Advisory and best-effort.
"""
48 changes: 48 additions & 0 deletions tests/api_resources/test_devboxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,30 @@ def test_path_params_wait_for_command(self, client: Runloop) -> None:
statuses=["queued"],
)

@parametrize
def test_method_watch_evictions(self, client: Runloop) -> None:
devbox_stream = client.devboxes.watch_evictions()
devbox_stream.response.close()

@parametrize
def test_raw_response_watch_evictions(self, client: Runloop) -> None:
response = client.devboxes.with_raw_response.watch_evictions()

assert response.http_request.headers.get("X-Stainless-Lang") == "python"
stream = response.parse()
stream.close()

@parametrize
def test_streaming_response_watch_evictions(self, client: Runloop) -> None:
with client.devboxes.with_streaming_response.watch_evictions() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"

stream = response.parse()
stream.close()

assert cast(Any, response.is_closed) is True

@parametrize
def test_method_write_file_contents(self, client: Runloop) -> None:
devbox = client.devboxes.write_file_contents(
Expand Down Expand Up @@ -2859,6 +2883,30 @@ async def test_path_params_wait_for_command(self, async_client: AsyncRunloop) ->
statuses=["queued"],
)

@parametrize
async def test_method_watch_evictions(self, async_client: AsyncRunloop) -> None:
devbox_stream = await async_client.devboxes.watch_evictions()
await devbox_stream.response.aclose()

@parametrize
async def test_raw_response_watch_evictions(self, async_client: AsyncRunloop) -> None:
response = await async_client.devboxes.with_raw_response.watch_evictions()

assert response.http_request.headers.get("X-Stainless-Lang") == "python"
stream = await response.parse()
await stream.close()

@parametrize
async def test_streaming_response_watch_evictions(self, async_client: AsyncRunloop) -> None:
async with async_client.devboxes.with_streaming_response.watch_evictions() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"

stream = await response.parse()
await stream.close()

assert cast(Any, response.is_closed) is True

@parametrize
async def test_method_write_file_contents(self, async_client: AsyncRunloop) -> None:
devbox = await async_client.devboxes.write_file_contents(
Expand Down
Loading