feat(devbox): add devbox.on_evict eviction notifications (SSE)#824
feat(devbox): add devbox.on_evict eviction notifications (SSE)#824jrvb-rl wants to merge 6 commits into
Conversation
|
🔄 Reflex agent status: Working The agent is working on this pull request. This PR was created by Reflex. This comment updates in place as the agent works. |
|
Reconciled with the endpoint as landed:
Ordering: depends on runloopai/runloop#10404, which marks |
Adds an object-oriented `Devbox.on_evict(callback)` (sync and async) backed by a per-client `EvictionMonitor`. The first registration opens a single account-wide `watch_evictions` SSE stream; incoming notifications are matched against the local interest set, the devbox is removed before its callback fires (at-most-once), and the stream is closed once the interest set empties. Depends on the Stainless-generated `devboxes.watch_evictions` method and `DevboxEvictionEvent` type, which land downstream once the server endpoint merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1c62073 to
1e699b8
Compare
- Use the real generated event type DevboxEvictionEventView (the endpoint's view) in place of the assumed DevboxEvictionEvent. - Invoke the callback as callback(devbox, eviction_deadline_ms): the Devbox object and the Unix millisecond deadline, instead of passing the raw event. Requires the main-repo stainless config change that generates watch_evictions as Stream[DevboxEvictionEventView] (runloopai/runloop#10404). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1e699b8 to
ffdd21b
Compare
0ebcf9d to
92a2cfe
Compare
The mux SSE route moved from /v1/devboxes/watch_evictions to
/v1/devboxes/evictions/watch (runloopai/runloop#10409) to avoid colliding
with GET /v1/devboxes/{id}. Update the generated request path so the
watch_evictions method reaches the new endpoint. Method surface unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two bugs made devbox.on_evict never fire: 1. The generated watch_evictions() sends Accept: application/json, but the endpoint only streams for Accept: text/event-stream — otherwise it returns an empty text/plain 200. The monitor now forces the SSE Accept header. 2. The monitor ran the stream once and stopped. The server force-closes on leader change / slow consumer (and a long-lived HTTP/2 stream can drop), and expects the client to reconnect and re-read the snapshot. The monitor now reconnects with backoff until no devbox is still interested. Adds debug logging around connect / event / reconnect. Verified against dev: a forced flex drain now delivers on_evict for 20/20 devboxes before suspend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Async parity gap
That reintroduces the silent “never fires” failure for Please mirror the sync monitor’s connect/reconnect loop in |
Addresses review on #824: AsyncEvictionMonitor still had the pre-fix single-shot _run, so AsyncDevbox.on_evict would silently never fire. Port the sync monitor's fixes to sdk/async_eviction.py: - request the SSE Accept header (text/event-stream) on watch_evictions - reconnect with capped backoff until no devbox is still interested Also ruff-format sdk/eviction.py (the sync fix wrapped a call that fits on one line). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Addressed the async parity gap in 7893f59: Note: the remaining |
Description
Follow-on to the server-side flex eviction-notification work (monorepo #10325 and its successor SSE endpoint). Adds the object-oriented client surface for eviction notifications.
New API:
devbox.on_evict(callback)(anddevbox.cancel_on_evict()), on both the syncDevboxand asyncAsyncDevbox.How it works (internal state management):
EvictionMonitor(sdk/eviction.py, asyncsdk/async_eviction.py) holds the interest set:{devbox_id -> callback}. Monitors are looked up via a weakclient -> monitorregistry (monitor_for), so everyDevboxbuilt from the same generated client shares one monitor and thus one SSE connection. NoDevbox/factory constructor signatures changed.on_evictacross any devbox opens a single account-widedevboxes.watch_evictions()SSE stream on a background thread (sync) /asyncio.Task(async).RunloopSDK.close()/AsyncRunloopSDK.aclose()tear the monitor down.Dependency / status
devboxes.watch_evictionsmethod and theDevboxEvictionEventtype (devbox_id,eviction_deadline_ms), which are generated downstream once the server endpoint lands. Those symbols are imported underTYPE_CHECKINGand referenced by assumed name — they will be reconciled, and tests + an example added, once generation lands. No generated code was modified.Testing
Deferred until the generated endpoint exists (see above). New/edited files byte-compile cleanly.
🤖 Generated with Claude Code