Skip to content

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

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

KAJdev wants to merge 130 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

@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.

Delta review, since 2026-09-16 (re-checked at d5abe4b11)

I ran tests/test_sandbox.py on the new head (Python 3.12, aiohttp 3.14.3): 23 passed. The suite runs against a real aiohttp REST peer rather than mocking the SDK transport.

Earlier findings

  1. AsyncioSandbox vs AsyncSandbox: resolved. The README now gives the reason: it follows the SDK's existing AsyncioEndpoint/AsyncioJob exports, which runpod/__init__.py does export. Consistent with the rest of the SDK. One piece is still open: runpod.sandbox.__all__ still exports AsyncSandboxLogStream alongside AsyncioSandbox, so both prefixes ship in the same namespace. There are also two classes named AsyncSandboxLogStream, in runpod/api/sandboxes.py:86 and runpod/sandbox/asyncio.py:441, and a user importing the wrong one gets isinstance mismatches. Renaming the exported one to AsyncioSandboxLogStream would settle both.
  2. check=False default: addressed as asked. The choice is now deliberate and documented in the README and the exec docstring ("Like subprocess.run…"), including that HTTP errors and malformed responses raise regardless of check.
  3. Public repo while the feature is gated: addressed by documentation. The README now says sandboxes are "an access-controlled preview … installing the SDK does not grant access". Whether that's enough before GA is a product decision; it's at least clear to anyone who finds it.
  4. Hard data["output"] index: fixed. Non-JSON bodies, non-object JSON, and missing or non-string output all raise QueryError, without retrying the exec. The new tests cover {}, {"output": null}, [], and an HTML <html>proxy error</html> page returned with a 200, and all four pass.

New in this commit

Issue: SandboxCompute.vcpu_count is now typed int, but the API returns a number

The REST v2 spec (spec/openapi.yaml in runpod/rphttp2) declares SandboxCompute.vcpuCount as type: number, and the server builds it as float32. SandboxInfo assigns compute["vcpuCount"] without converting it, so at runtime callers get whatever the JSON contains. But type checkers and IDEs will now tell them it's an int. If fractional vCPU values are ever returned, the annotation will be wrong. Suggest keeping it float, as it was before this commit, or converting explicitly if an integer is really guaranteed.

Question: _retry_connection guard with no ceiling on aiohttp

if hasattr(self._session, "_retry_connection") prevents a crash if aiohttp removes this private attribute. But then the "don't replay ambiguous GET/DELETE" protection switches off without any warning. requirements.txt only sets a floor (aiohttp[speedups] >= 3.14.3). A small test asserting session._retry_connection is False would make an aiohttp change show up as a test failure rather than as silent retries.

Nits

  • The CodeQL/code-quality annotations (mostly except BaseException in cleanup paths) look like intentional cleanup-then-reraise handling. A one-line comment at those sites, or resolving the alerts, would stop the next reviewer re-raising them.

Verdict: PASS WITH NITS. All four earlier points are addressed, and the new validation is tested against a real peer. The vcpu_count type is worth reverting before merge.

🤖 Reviewed by Henrik's AI-Powered Bug Finder

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
Comment thread tests/test_sandbox.py Fixed
Comment thread tests/test_sandbox.py

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