nemo_gym.sandbox.providers.e2b.provider

View as Markdown

Sandbox provider backed by the E2B Python SDK.

Works against e2b.dev itself and against any e2b-compatible gateway (point connection.api_url/connection.sandbox_url at it).

Two E2B concepts differ from the provider-neutral :class:SandboxSpec and are handled explicitly rather than silently:

Templates, not images. E2B starts sandboxes from a pre-built template name or ID, not from an arbitrary registry reference. Because tagged template names and OCI image references can both contain :, the direct SandboxSpec.image shortcut is deliberately limited to unambiguous names in [A-Za-z0-9_-]. Use provider_options.template or create.template_map for tagged names, IDs, and image references.

Resources are fixed at template build time. cpu_count/memory_mb are arguments to the template build, so a per-sandbox SandboxSpec.resources cannot be honoured at create time. Requests are reported once per provider instance (or raise, with create.strict_resources) instead of being dropped quietly.

Module Contents

Classes

NameDescription
E2BConnectionConfigConnection settings forwarded to the SDK.
E2BCreateConfigSandbox creation settings.
E2BCreateErrorRaised when a sandbox cannot be created.
E2BExecConfigCommand execution settings.
E2BOperationConfigRetry policy for transient SDK/transport failures.
E2BProviderProvider backed by the E2B Python SDK.

Functions

NameDescription
_config_from_mappingBuild a config dataclass from a mapping, rejecting unknown keys.
_is_finite_number-
_require_e2b_sdkLoad the optional SDK through the shared helper.
_validate_nonnegative_int-
_validate_nonnegative_number-
_validate_optional_number-

Data

LOGGER

T

_API_PARAM_KEYS

_DIRECT_TEMPLATE_RE

API

class nemo_gym.sandbox.providers.e2b.provider.E2BConnectionConfig(
api_key: str | None = None,
api_url: str | None = None,
sandbox_url: str | None = None,
domain: str | None = None,
debug: bool | None = None,
validate_api_key: bool | None = None,
headers: dict[str, str] | None = None,
api_headers: dict[str, str] | None = None,
proxy: str | None = None,
request_timeout_s: float | None = None
)
Dataclass

Connection settings forwarded to the SDK.

Any field left None falls back to the SDK’s own environment variables (E2B_API_KEY, E2B_API_URL, E2B_SANDBOX_URL, E2B_DOMAIN, …).

api_headers
dict[str, str] | None = None
api_key
str | None = None
api_url
str | None = None
debug
bool | None = None
domain
str | None = None
headers
dict[str, str] | None = None
proxy
str | None = None
request_timeout_s
float | None = None
sandbox_url
str | None = None
validate_api_key
bool | None = None
nemo_gym.sandbox.providers.e2b.provider.E2BConnectionConfig.__post_init__() -> None
class nemo_gym.sandbox.providers.e2b.provider.E2BCreateConfig(
template: str | None = None,
template_map: dict[str, str] = dict(),
timeout_s: float | None = 3600.0,
allow_internet_access: bool = True,
secure: bool = True,
strict_resources: bool = False
)
Dataclass

Sandbox creation settings.

allow_internet_access
bool = True
secure
bool = True
strict_resources
bool = False
template
str | None = None
template_map
dict[str, str] = field(default_factory=dict)
timeout_s
float | None = 3600.0
nemo_gym.sandbox.providers.e2b.provider.E2BCreateConfig.__post_init__() -> None
class nemo_gym.sandbox.providers.e2b.provider.E2BCreateError()

Bases: SandboxCreateError

Raised when a sandbox cannot be created.

class nemo_gym.sandbox.providers.e2b.provider.E2BExecConfig(
default_timeout_s: float | None = 180.0,
user: str | None = None,
request_timeout_s: float | None = None,
background: bool = True,
reconnect_attempts: int = 2
)
Dataclass

Command execution settings.

background
bool = True
default_timeout_s
float | None = 180.0
reconnect_attempts
int = 2
request_timeout_s
float | None = None
user
str | None = None
nemo_gym.sandbox.providers.e2b.provider.E2BExecConfig.__post_init__() -> None
class nemo_gym.sandbox.providers.e2b.provider.E2BOperationConfig(
retries: int = 2,
retry_delay_s: float = 0.5,
retry_max_delay_s: float = 8.0
)
Dataclass

Retry policy for transient SDK/transport failures.

retries
int = 2
retry_delay_s
float = 0.5
retry_max_delay_s
float = 8.0
nemo_gym.sandbox.providers.e2b.provider.E2BOperationConfig.__post_init__() -> None
class nemo_gym.sandbox.providers.e2b.provider.E2BProvider(
connection: nemo_gym.sandbox.providers.e2b.provider.E2BConnectionConfig | collections.abc.Mapping[str, typing.Any] | None = None,
create: nemo_gym.sandbox.providers.e2b.provider.E2BCreateConfig | collections.abc.Mapping[str, typing.Any] | None = None,
exec: nemo_gym.sandbox.providers.e2b.provider.E2BExecConfig | collections.abc.Mapping[str, typing.Any] | None = None,
operations: nemo_gym.sandbox.providers.e2b.provider.E2BOperationConfig | collections.abc.Mapping[str, typing.Any] | None = None
)

Provider backed by the E2B Python SDK.

_connection
_create
= _config_from_mapping(E2BCreateConfig, create)
_exec
= _config_from_mapping(E2BExecConfig, exec)
_operations
_warned_resource_specs
set[str] = set()
name
= 'e2b'
nemo_gym.sandbox.providers.e2b.provider.E2BProvider._api_params() -> dict[str, typing.Any]

SDK ApiParams for connection-scoped calls; omitted keys fall back to env.

Only create/connect/kill open a connection and accept these. Everything else runs against an already-connected sandbox — see :meth:_request_params.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider._check_resources(
spec: nemo_gym.sandbox.providers.base.SandboxSpec,
template: str
) -> None

Surface resource requests E2B cannot honour per sandbox.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider._exec_request_timeout() -> float | None

Return the E2B 2.36 stream-open timeout for command requests.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider._request_params() -> dict[str, typing.Any]

Per-request options for calls on an existing sandbox object.

commands.run, files.* and is_running take request_timeout only — the sandbox already carries the connection config, and handing them the full ApiParams raises TypeError: unexpected keyword argument 'api_key'.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider._resolve_template(
spec: nemo_gym.sandbox.providers.base.SandboxSpec
) -> str

Map a spec onto an E2B template.

Precedence: provider_options.template -> create.template_map -> an unambiguous direct spec.image. create.template is used only when spec.image is omitted; an unmapped image must not silently select an unrelated fallback template.

Building a template from an image is provisioning, not part of starting a sandbox, so it lives in :mod:nemo_gym.sandbox.providers.e2b.build and never runs on this path.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider._run_background(
sandbox: typing.Any,
kwargs: dict[str, typing.Any]
) -> typing.Any
async

Run a command detached, reattaching by pid if the stream drops.

commands.run(background=True) returns as soon as the process has started, handing back its pid. The command then keeps running inside the sandbox independently of the stream carrying its output, so losing that stream — a gateway rollout, a proxy restart, a network blip — no longer destroys the command: reattach with commands.connect(pid) and the real exit code still arrives.

Reattaching has two inherent limits:

  • Output emitted while disconnected is lost. Output already received by the previous handle is retained and combined with the reattached stream, but the stream is live rather than replayed.
  • The process must still be running. connect raises not-found once it has exited, so a command that finishes during the gap cannot be recovered.
nemo_gym.sandbox.providers.e2b.provider.E2BProvider._sandbox(
handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> typing.Any
staticmethod
nemo_gym.sandbox.providers.e2b.provider.E2BProvider._with_retries(
factory: typing.Callable[[], typing.Awaitable[nemo_gym.sandbox.providers.e2b.provider.T]],
operation: str,
retry_timeouts: bool = False
) -> nemo_gym.sandbox.providers.e2b.provider.T
async

Retry transient failures with exponential backoff.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider.aclose() -> None
async

No provider-scoped client to close; sandboxes own their connections.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider.close(
handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
async
nemo_gym.sandbox.providers.e2b.provider.E2BProvider.connect(
descriptor: collections.abc.Mapping[str, typing.Any]
) -> nemo_gym.sandbox.providers.base.SandboxHandle
async

Attach to the sandbox described by descriptor.

E2B’s public connect API applies its default sandbox timeout when none is supplied, so attaching may renew a sandbox that is close to expiry.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider.create(
spec: nemo_gym.sandbox.providers.base.SandboxSpec
) -> nemo_gym.sandbox.providers.base.SandboxHandle
async
nemo_gym.sandbox.providers.e2b.provider.E2BProvider.download_file(
handle: nemo_gym.sandbox.providers.base.SandboxHandle,
source_path: str,
target_path: pathlib.Path
) -> None
async
nemo_gym.sandbox.providers.e2b.provider.E2BProvider.exec(
handle: nemo_gym.sandbox.providers.base.SandboxHandle,
command: str,
cwd: str | None = None,
env: dict[str, str] | None = None,
timeout_s: int | float | None = None,
user: str | int | None = None
) -> nemo_gym.sandbox.providers.base.SandboxExecResult
async
nemo_gym.sandbox.providers.e2b.provider.E2BProvider.read_file(
handle: nemo_gym.sandbox.providers.base.SandboxHandle,
source_path: str
) -> bytes
async
nemo_gym.sandbox.providers.e2b.provider.E2BProvider.serialize_handle(
handle: nemo_gym.sandbox.providers.base.SandboxHandle,
scope: str | None = None
) -> dict[str, typing.Any]
async

Return a descriptor for attaching to this sandbox from another process.

nemo_gym.sandbox.providers.e2b.provider.E2BProvider.status(
handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> nemo_gym.sandbox.providers.base.SandboxStatus
async
nemo_gym.sandbox.providers.e2b.provider.E2BProvider.upload_file(
handle: nemo_gym.sandbox.providers.base.SandboxHandle,
source_path: pathlib.Path,
target_path: str
) -> None
async
nemo_gym.sandbox.providers.e2b.provider.E2BProvider.write_file(
handle: nemo_gym.sandbox.providers.base.SandboxHandle,
target_path: str,
data: str | bytes
) -> None
async
nemo_gym.sandbox.providers.e2b.provider._config_from_mapping(
cls: type[nemo_gym.sandbox.providers.e2b.provider.T],
value: typing.Any
) -> nemo_gym.sandbox.providers.e2b.provider.T

Build a config dataclass from a mapping, rejecting unknown keys.

nemo_gym.sandbox.providers.e2b.provider._is_finite_number(
value: typing.Any
) -> bool
nemo_gym.sandbox.providers.e2b.provider._require_e2b_sdk() -> typing.Any

Load the optional SDK through the shared helper.

Keep this small wrapper local so provider unit tests can replace the SDK without importing or contacting E2B.

nemo_gym.sandbox.providers.e2b.provider._validate_nonnegative_int(
name: str,
value: typing.Any
) -> None
nemo_gym.sandbox.providers.e2b.provider._validate_nonnegative_number(
name: str,
value: typing.Any
) -> None
nemo_gym.sandbox.providers.e2b.provider._validate_optional_number(
name: str,
value: typing.Any,
positive: bool
) -> None
nemo_gym.sandbox.providers.e2b.provider.LOGGER = logging.getLogger(__name__)
nemo_gym.sandbox.providers.e2b.provider.T = TypeVar('T')
nemo_gym.sandbox.providers.e2b.provider._API_PARAM_KEYS = ('api_key', 'api_url', 'sandbox_url', 'domain', 'debug', 'validate_api_key', 'he...
nemo_gym.sandbox.providers.e2b.provider._DIRECT_TEMPLATE_RE = re.compile('^[A-Za-z0-9_-]+$')