Agent Sandbox

NVIDIA NeMo Gym Sandbox Provider

Run NVIDIA NeMo Gym RL training environments on Agent Sandbox warm pools. Installing the nemo-gym-k8s-agent-sandbox package registers the agent_sandbox sandbox provider through NeMo Gym’s entry-point registry — no NeMo Gym fork required.

This package makes Agent Sandbox a sandbox backend for NVIDIA NeMo Gym RL training environments, alongside NeMo Gym’s built-in providers (Docker, Daytona, ECS Fargate, Enroot, OpenShell, OpenSandbox, Apptainer).

NeMo Gym environments get their isolated execution sandboxes as SandboxClaims against SandboxWarmPools, so per-rollout sandbox acquisition is claim-bind latency (typically sub-second on a warm pool hit) instead of pod scheduling + image pull. Claim TTLs (ttl_s) map to the claim lifecycle’s shutdownTime, so the controller garbage-collects expired sandboxes even if the training process dies.

How it plugs in

NeMo Gym discovers providers through the nemo_gym.sandbox_providers entry-point group (see NeMo Gym’s nemo_gym/sandbox/providers/registry.py). This package publishes agent_sandbox in that group, so:

pip install nemo-gym-k8s-agent-sandbox   # alongside nemo-gym

is all it takes for agent_sandbox to become a valid provider name — no registration code, no NeMo Gym fork.

Python version: this package requires Python >= 3.13. nemo-gym itself currently publishes requires-python >=3.13.14 — a floor no released 3.13.x satisfies (3.14+ does) — so on a 3.13.x interpreter pip rejects the nemo-gym resolution. Either use Python 3.14+, or pre-install nemo-gym past its floor first:

pip install --ignore-requires-python 'nemo-gym>=0.5.0'

Once nemo-gym relaxes its floor, the workaround (and the matching one in dev/tools/test-unit) goes away — this package’s own metadata already works.

Cluster prerequisites

  • The agent-sandbox controller and extensions (SandboxClaim / SandboxTemplate / SandboxWarmPool CRDs and controllers) installed.
  • At least one SandboxWarmPool whose SandboxTemplate runs an image serving the sandbox runtime REST API (/execute, /upload, /download, …) on connection.server_port (default 8888). See examples/python-runtime-sandbox for the reference runtime image.
  • For clients running outside the cluster: the sandbox router plus either a Gateway (connection.mode: gateway) or a directly reachable router URL (connection.mode: direct). Inside the cluster, in_cluster mode talks straight to sandbox pod IPs and needs neither.

Usage

Add the provider config to a NeMo Gym run (the shipped config binds the standard instance name sandbox, so it drops in wherever an openshell/opensandbox config would):

AGENT=responses_api_agents/mini_swe_agent_2/configs/mini_swe_agent_2.yaml
MODEL=responses_api_models/vllm_model/configs/vllm_model.yaml
ng_run "+config_paths=[$AGENT, /path/to/configs/agent_sandbox.yaml, $MODEL]"

See configs/agent_sandbox.yaml for the annotated config. Kubernetes API access uses standard kubeconfig / in-cluster service account resolution.

Image -> warm pool routing

Warm pools pre-bake their image in the pool’s SandboxTemplate, so sandbox_spec.image cannot be pulled per-create. Instead, images route to pools:

create:
  warmpool: nemo-gym-pool          # default pool
  image_warmpools:                 # exact image string -> pool name
    my-registry/swebench-runtime:latest: swebench-pool

Resolution order per sandbox: provider_options.warmpool > image_warmpools[spec.image] > create.warmpool. An image that only resolves via the default pool logs a warning (the pool’s template decides the real image).

Per-sandbox provider_options

Key Meaning
warmpool Claim from this pool (overrides all routing)
namespace Claim namespace (overrides create.namespace)
pod_labels / pod_annotations Stamped on the sandbox Pod via additionalPodMetadata
volume_claim_templates Volume claim templates merged into the sandbox

Semantics and limitations

NeMo Gym spec field Mapping
image Routed to a warm pool via image_warmpools (not pulled per-create)
ttl_s Claim lifecycle shutdownTime + Delete policy (enforced server-side)
env Exported inside every wrapped exec script
workdir Default cd for every exec (falls back to the runtime’s own cwd)
files Uploaded by NeMo Gym’s sandbox API via upload_file after create returns
metadata SandboxClaim labels (must satisfy Kubernetes label syntax)
resources Ignored with a warning — resources come from the SandboxTemplate
entrypoint Unsupported (create error) — the template owns the pod command
exec(user=...) Ignored with a warning — the runtime API runs as the pod user

Implementation notes:

  • The runtime’s /execute tokenizes with shlex.split and runs without a shell, so every command is sent as <exec_shell> -c '<script>' with cwd/env folded in.
  • The file API only accepts ..-free relative paths rooted at the runtime server’s working directory. Absolute upload_file/download_file paths are honored by staging through a relative temp name and copying via exec — this assumes the file-API root and the exec working directory are the same directory, which holds for the reference runtime image (both come from its SANDBOX_BASE_DIR, default /app).
  • An exec that exceeds its timeout abandons the HTTP request, but the process may keep running inside the pod (the runtime API has no server-side kill); the result carries error_type="timeout" and return code 125.

Development

# Only needed on 3.13.x interpreters (see the Python version note above):
pip install --ignore-requires-python 'nemo-gym>=0.5.0'
pip install -e '../../python/agentic-sandbox-client[async]'
pip install -e '.[test]'
pytest tests/unit

Tests are pure unit tests against fakes — no cluster, no NeMo Gym run required (only the nemo-gym package for its provider base types).