Skip to content

feat: add sync and async sandbox SDK CON-1523, CON-1524, CON-1525, CON-1526 - #595

Open
KAJdev wants to merge 132 commits into
mainfrom
zeke/con-1525-create-sandbox-domain
Open

KAJdev wants to merge 132 commits into
mainfrom
zeke/con-1525-create-sandbox-domain

Conversation

@KAJdev

@KAJdev KAJdev commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Sandbox and AsyncioSandbox share lifecycle and execution behavior. Owned contexts terminate remote compute on exit; handles returned by get() or list() close only their local connections. Interrupted creation and failed provisioning preserve cleanup ownership and recoverable sandbox IDs.

Configure RUNPOD_API_BASE_URL for an authorized sandbox-enabled environment and supply RUNPOD_API_KEY. Creation and execution options require the API contracts in rphttp2#326 and rphttp2#315.

from runpod import Sandbox

with Sandbox(image="python:3.12-slim", cmd=["sleep", "infinity"]) as sandbox:
    result = sandbox.exec(["python", "-c", "print('hello')"], check=True)
    print(result.output)
import asyncio
from runpod import AsyncioSandbox

async def main():
    async with AsyncioSandbox(
        image="python:3.12-slim", cmd=["sleep", "infinity"]
    ) as sandbox:
        result = await sandbox.exec(["python", "-c", "print('hello')"], check=True)
        print(result.output)

asyncio.run(main())

exec() retains partial output on failure, accepts timeout_seconds, and supports detached commands with background=True. A detached acknowledgement confirms startup, not completion. Foreground execution remains subject to the backend's current four-second cap.

logs() streams container or system logs with resumable event IDs. Quiet container log delivery depends on CON-1557 / runpod/host#2820.

Linear: CON-1523, CON-1524, CON-1525, CON-1526

General README onboarding is tracked in #610.

KAJdev added 30 commits July 1, 2026 15:09
deanq and others added 6 commits September 29, 2026 13:31
Resolve conflicts in two files:

- runpod/__init__.py: keep the branch's lazy PEP-562 import surface and
  add main's serverless startup gate (run_import_checks). main's eager
  import block is dropped as redundant -- every name it exported is
  already resolvable through _LAZY_ATTRS.
- README.md: keep the branch's apps-SDK rewrite (Installation, Example,
  Contributing) and append main's new sections (Serverless Worker,
  Worker Fitness Checks, Network-Volume Warm Cache, REST API v2, Pods,
  Directory). Dropped main's duplicate "Community and Contributing" and
  redundant Python-3.10 note already covered by the new intro.
Comment thread runpod/rp_cli/main.py Fixed
Comment thread runpod/rp_cli/main.py Fixed
Comment thread tests/e2e/test_sandbox.py Fixed
Comment thread tests/e2e/test_sandbox.py Fixed
Comment thread tests/test_sandbox.py Fixed
Comment thread tests/test_sandbox.py Fixed
Comment thread tests/e2e/test_sandbox.py Fixed
Comment thread tests/e2e/test_sandbox.py Fixed
Comment thread tests/test_sandbox.py Fixed
Comment thread tests/test_sandbox.py Fixed
@jiayi-runpod jiayi-runpod changed the title feat: add sync and async sandbox SDK feat: add sync and async sandbox SDK CON-1523, Oct 14, 2026
@jiayi-runpod jiayi-runpod changed the title feat: add sync and async sandbox SDK CON-1523, feat: add sync and async sandbox SDK CON-1523, CON-1524, Oct 14, 2026
@jiayi-runpod jiayi-runpod changed the title feat: add sync and async sandbox SDK CON-1523, CON-1524, feat: add sync and async sandbox SDK CON-1523, CON-1524, CON-1525, Oct 14, 2026
@jiayi-runpod jiayi-runpod changed the title feat: add sync and async sandbox SDK CON-1523, CON-1524, CON-1525, feat: add sync and async sandbox SDK CON-1523, CON-1524, CON-1525, CON-1526 Oct 14, 2026
Comment thread tests/test_sandbox.py Fixed
Comment thread tests/test_sandbox.py Resolved
Comment thread tests/test_sandbox.py Fixed
Comment thread tests/test_sandbox.py Resolved

@runpod-Henrik runpod-Henrik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed from d5abe4b1 to abb210e6: still approve. Most of the 148-commit compare is main (the apps SDK and others); this PR's own change since my review is the shared-storage and lifecycle work, the failure-ownership hardening, and the naming fixes. The ownership work is the part I'd have worried about most, and it's covered: a failed create whose problem body carries sandboxId now becomes SandboxCreationError with the id kept and the sandbox terminated, and only a sandbox_starting 409 is retried, so container_exited and sandbox_terminated fail straight away.

Checked:

  • Tests. The sandbox suite went from 23 to 63 tests and all pass locally, as does the whole unit suite (1,599). Putting back status-only 409 retries fails three of the new tests, and dropping the id capture on a failed create fails four, so both behaviours are pinned. CI is green on 3.10 to 3.14, and the branch merges cleanly with current main.
  • Dev, end to end (22 Oct, 11:52 PDT, v2-rest.runpod.dev). Sync and async: create, exec (exit_code 0, duration_ms and truncated filled in), check=True on exit 3 raising SandboxExecutionError with exit_code 3, extend, update, and context exit leaving both sandboxes TERMINATED / USER_REQUESTED. The first command came back 3 to 4 s after the create call.
  • My earlier points. AsyncioSandboxLogStream now matches AsyncioSandbox, vcpu_count is a float as the spec says, and the aiohttp _retry_connection override is asserted in the tests. Thanks.

Two things against the current REST contract, neither blocking:

  • The wire still uses the names rphttp2 has deprecated. Create sends imageName, cpuFlavorId, vcpuCount and memoryInGb, and SandboxInfo.image is read from the response's imageName. The REST API now marks all of those deprecated in favour of image and cpu: {id, vcpuCount} (memory is derived from the flavor). They work today, but the plan for the deprecated dataCenterId was "move the probe and the SDK, then remove", and the same will apply here: once the aliases go, create becomes a 4xx (rphttp2 rejects unknown fields) and info.image quietly turns None. Since the public parameter is already image, switching the wire to image and reading image (falling back to imageName) is a small change now. cpu needs both id and vcpuCount, so sending it when only one of cpu_flavor_id / vcpu_count is given needs a decision; keeping the aliases for that case until the server fills in the missing half is fine, as long as it's deliberate.
  • Creating with neither image nor template_id is refused client-side ("Supply exactly one of image or template_id"), but the API now accepts it and runs the default sandbox image. Allowing "at most one" would match the server.

await asyncio.wait_for(queued.wait(), 2)
task.cancel()
with pytest.raises(asyncio.CancelledError):
await task

This branch has not been deployed

No deployments
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.

5 participants