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.25.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.25.0 (2026-07-24)

Full Changelog: [v1.24.0...v1.25.0](https://github.com/runloopai/api-client-python/compare/v1.24.0...v1.25.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/evictions/watch">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.25.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.25.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/evictions/watch",
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/evictions/watch",
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
2 changes: 2 additions & 0 deletions src/runloop_api_client/sdk/async_.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from .async_devbox import AsyncDevbox
from .async_scorer import AsyncScorer
from .async_secret import AsyncSecret
from .async_eviction import shutdown_monitor_for as shutdown_eviction_monitor
from .async_scenario import AsyncScenario
from .async_snapshot import AsyncSnapshot
from .async_benchmark import AsyncBenchmark
Expand Down Expand Up @@ -1373,6 +1374,7 @@ def __init__(

async def aclose(self) -> None:
"""Close the underlying HTTP client and release resources."""
await shutdown_eviction_monitor(self.api)
await self.api.close()

async def __aenter__(self) -> "AsyncRunloopSDK":
Expand Down
20 changes: 20 additions & 0 deletions src/runloop_api_client/sdk/async_devbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from ._helpers import filter_params
from .._streaming import AsyncStream
from ..lib.polling import PollingConfig
from .async_eviction import AsyncEvictionCallback, monitor_for as _eviction_monitor_for
from ..types.devboxes import ExecutionUpdateChunk
from .async_execution import AsyncExecution, _AsyncStreamingGroup
from .async_execution_result import AsyncExecutionResult
Expand Down Expand Up @@ -216,6 +217,25 @@ async def await_suspended(self, *, polling_config: PollingConfig | None = None)
"""
return await self._client.devboxes.await_suspended(self._id, polling_config=polling_config)

async def on_evict(self, callback: AsyncEvictionCallback) -> None:
"""Register a callback fired once if this devbox gets a pending infrastructure eviction.

The first ``on_evict`` across any devbox on this client opens a single
account-wide notification stream; it closes automatically once every
registered devbox has been notified. The callback runs at most once and is
invoked as ``callback(devbox, eviction_deadline_ms)`` — the devbox itself and
the Unix millisecond deadline by which it will be suspended. Use it to run
cleanup before the devbox is suspended. The callback may be sync or async.

:param callback: Callable invoked with this devbox and its eviction deadline (ms).
:type callback: AsyncEvictionCallback
"""
await _eviction_monitor_for(self._client).register(self, callback)

async def cancel_on_evict(self) -> None:
"""Withdraw this devbox's eviction interest registered via :meth:`on_evict`."""
await _eviction_monitor_for(self._client).unregister(self._id)

async def shutdown(
self,
**options: Unpack[LongRequestOptions],
Expand Down
Loading
Loading