chore: bump prime-sandboxes to 0.2.39 and pooled sandbox client - #2410
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Member
Author
|
Closing for now — there is a regression in prime-sandboxes 0.2.39, so we cannot bump yet. |
The 0.2.39 SDK coalesces concurrent status polls per client into batched requests (up to 100 ids each). Per-runtime clients defeat that batcher, so lease a loop-scoped shared client at start and close it when the last runtime on the loop tears down. Adapted from #2411. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mikasenghaas
marked this pull request as ready for review
August 21, 2026 23:19
mikasenghaas
requested review from
hallerite and
xeophon
and removed request for
xeophon
August 21, 2026 23:19
Contributor
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Dependency version bump plus a client-pooling optimization that shares one AsyncSandboxClient per event loop using standard reference counting. The author is the primary code owner of this file and the change is a well-documented performance optimization with no feature-level behavior changes. You can add or adjust custom eligibility rules. Learn more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
prime-sandboxespin from>=0.2.37to>=0.2.39and update the lockfile (adds two transitive deps:protobuf-py,protobuf-py-ext).AsyncSandboxClientper event loop across all livePrimeRuntimes (adopts the client-pool approach from Share Prime sandbox clients across concurrent runtimes #2411). 0.2.39's SDK coalesces concurrent status polls per client into batch requests (status:batchGet, up to 100 ids). Per-runtime clients defeat that batcher; the shared client is leased atstartand closed when the last runtime on the loop tears down.Benchmark
GSM8K (single-turn)
uv run eval gsm8k -n 1 -r <N> -c <N> --server(elastic env-worker pool, one prime VM sandbox per rollout, modeldeepseek/deepseek-v4-flash), main vs this branch.Wall clock, two interleaved pairs at 1024 rollouts:
Identical configs vary 1m10s–4m31s, so wall clock is dominated by platform provisioning variance at this scale and shows no reliable signal either way.
The batching effect is direct and large, though. At 2048 rollouts, counting every httpx request in the eval process and all env workers (logging hook on the
httpxlogger, no code changes):GET /api/v1/sandbox/{id}(single status polls)POST /api/v1/sandbox/status:batchGetapi.primeintellect.aitotalThe ~15.5k individual creation-status polls collapse into 3.7k batch calls; the one remaining single
GET /sandbox/{id}per sandbox is outside the batched path. Gateway traffic (background-job polling reads files through the gateway on VM sandboxes) is unaffected.Both 2048-rollout runs completed all rollouts. The pooled run hit a ~5s burst of platform-side HTTP 500s on
POST /sandbox(37 rollouts, create is not batched and untouched by this PR); the baseline run saw one HTTP 500 on a delete. After every run,prime sandbox listfiltered by the run's label showed zero active sandboxes — no leaks.Terminal-Bench 2 (multi-turn agentic)
uv run eval harbor --env.taskset.dataset terminal-bench/terminal-bench-2 --env.taskset.tasks '["fix-git"]' --env.agent.max_turns 10 -n 1 -r 1024 -c 1024 --server, at two worker topologies: a single env worker (--serve.pool.multiplex 1000 --serve.pool.max-workers 1— on this branch that is one shared client for all 1024 concurrent sandboxes) and the default elastic pool (multiplex 128 → 9 workers, ~114 sandboxes per client).GET /sandbox/{id}status:batchGetapi.primeintellect.aitotalGET /sandbox/{id}status:batchGetapi.primeintellect.aitotalLarger pools batch better: 1024 sandboxes on one client compressed all creation polling into 74 batch calls. Solve rates were equivalent across the four runs (850–899/1024). Wall clock was again not meaningful: >98% of rollouts finished within ~3–4.5 min in every run, but three of the four runs caught inference-side 504 storms whose per-call retry loops stretched a handful of tail rollouts to ~30 min — unrelated to the sandbox client (the affected traces show
ProviderError: upstream 504on chat completions).The single-worker run also answers a tuning question: 1024 concurrent creates and deletes through the one shared client's platform-API pool (httpx default, 100 connections) produced zero client-side errors or pool timeouts. Gateway traffic runs on the SDK's explicitly sized pool (1000/200). Zero leaked sandboxes in all four runs.
Verification
uv syncsucceeds andimport prime_sandboxesreports 0.2.39.🤖 Generated with Claude Code
Note
Share
AsyncSandboxClientper event loop inPrimeRuntimeand bumpprime-sandboxesto 0.2.39_SharedClientdataclass and module-global_shared_clientsdict so multiplePrimeRuntimeinstances on the same asyncio loop share oneAsyncSandboxClientwith lease-based reference countingPrimeRuntime.startacquires the shared client for the current loop and increments its lease count;teardowndecrements the lease and closes the client only when the last runtime releases itprime-sandboxesdependency floor from >=0.2.37 to >=0.2.39 in pyproject.tomlPrimeRuntime.teardownno longer always closes theAsyncSandboxClient; client lifetime now depends on correct lease decrement, so anystart/teardownpairing mismatch will leak or prematurely close the shared clientMacroscope summarized 0398f72.
Note
Medium Risk
Changes shared lifecycle of the Prime sandbox HTTP client and teardown close path. A lease/close bug could leak connections or close a client still used by other runtimes.
Overview
Bumps
prime-sandboxesto>=0.2.39so the SDK can batch concurrent sandbox status polls (status:batchGet).PrimeRuntimenow leases oneAsyncSandboxClientper event loop instead of creating a client per sandbox. That lets the SDK coalesce creation/job polls across live runtimes. The last runtime on the loop closes the client inteardown; sandbox delete still happens per runtime.Reviewed by Cursor Bugbot for commit 0398f72. Bugbot is set up for automated code reviews on this repo. Configure here.