Skip to content

chore: bump prime-sandboxes to 0.2.39 and pooled sandbox client - #2410

Merged
mikasenghaas merged 3 commits into
mainfrom
chore/bump-prime-sandboxes-0239
Aug 21, 2026
Merged

chore: bump prime-sandboxes to 0.2.39 and pooled sandbox client#2410
mikasenghaas merged 3 commits into
mainfrom
chore/bump-prime-sandboxes-0239

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Aug 20, 2026

Copy link
Copy Markdown
Member

Summary

  • Bump the prime-sandboxes pin from >=0.2.37 to >=0.2.39 and update the lockfile (adds two transitive deps: protobuf-py, protobuf-py-ext).
  • Share one AsyncSandboxClient per event loop across all live PrimeRuntimes (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 at start and 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, model deepseek/deepseek-v4-flash), main vs this branch.

Wall clock, two interleaved pairs at 1024 rollouts:

run main (0.2.37) this PR (0.2.39 + shared client)
1024 rollouts, pair 1 2m17.8s 2m06.1s
1024 rollouts, pair 2 1m10.4s 4m30.7s

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 httpx logger, no code changes):

HTTP requests per run main (0.2.37) this PR (0.2.39 + shared client)
GET /api/v1/sandbox/{id} (single status polls) 17,543 2,011
POST /api/v1/sandbox/status:batchGet 3,709
api.primeintellect.ai total 23,851 11,937 (−50%)
sandbox gateway (job polling, up/downloads) 48,514 47,927
wall time 1m42.5s 1m37.6s

The ~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 list filtered 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).

HTTP requests per run main this PR
1 worker: single GET /sandbox/{id} 6,393 1,039
1 worker: status:batchGet 74
1 worker: api.primeintellect.ai total 9,509 4,269 (−55%)
9 workers: single GET /sandbox/{id} 7,701 1,033
9 workers: status:batchGet 1,781
9 workers: api.primeintellect.ai total 10,889 5,981 (−45%)

Larger 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 504 on 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 sync succeeds and import prime_sandboxes reports 0.2.39.
  • Eight e2e eval runs (gsm8k 2×1024 + 2×2048, terminal-bench-2 4×1024) on prime sandboxes through the env-server pool, reward parity with main and no leaked sandboxes.

🤖 Generated with Claude Code

Note

Share AsyncSandboxClient per event loop in PrimeRuntime and bump prime-sandboxes to 0.2.39

  • Adds _SharedClient dataclass and module-global _shared_clients dict so multiple PrimeRuntime instances on the same asyncio loop share one AsyncSandboxClient with lease-based reference counting
  • PrimeRuntime.start acquires the shared client for the current loop and increments its lease count; teardown decrements the lease and closes the client only when the last runtime releases it
  • Bumps prime-sandboxes dependency floor from >=0.2.37 to >=0.2.39 in pyproject.toml
  • Risk: PrimeRuntime.teardown no longer always closes the AsyncSandboxClient; client lifetime now depends on correct lease decrement, so any start/teardown pairing mismatch will leak or prematurely close the shared client

Macroscope 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-sandboxes to >=0.2.39 so the SDK can batch concurrent sandbox status polls (status:batchGet).

PrimeRuntime now leases one AsyncSandboxClient per 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 in teardown; 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.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mikasenghaas

Copy link
Copy Markdown
Member Author

Closing for now — there is a regression in prime-sandboxes 0.2.39, so we cannot bump yet.

mikasenghaas and others added 2 commits August 21, 2026 21:00
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 mikasenghaas changed the title chore: bump prime-sandboxes to 0.2.39 chore: bump prime-sandboxes to 0.2.39 and share one sandbox client per event loop Aug 21, 2026
@mikasenghaas mikasenghaas changed the title chore: bump prime-sandboxes to 0.2.39 and share one sandbox client per event loop chore: bump prime-sandboxes to 0.2.39 and pooled sandbox client Aug 21, 2026
@mikasenghaas
mikasenghaas marked this pull request as ready for review August 21, 2026 23:19
@mikasenghaas
mikasenghaas requested review from hallerite and xeophon and removed request for xeophon August 21, 2026 23:19
@macroscopeapp

macroscopeapp Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 0398f72

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.

@mikasenghaas
mikasenghaas merged commit b878d00 into main Aug 21, 2026
13 checks passed
@mikasenghaas
mikasenghaas deleted the chore/bump-prime-sandboxes-0239 branch August 21, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant