Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sandboxes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from .base import (
ExecutionResult,
ProviderCapabilities,
SandboxConfig,
SandboxProvider,
SandboxState,
Expand Down Expand Up @@ -40,6 +41,7 @@
"SandboxConfig",
"ExecutionResult",
"SandboxState",
"ProviderCapabilities",
# Manager
"SandboxManager",
"Manager", # Alias
Expand Down
15 changes: 14 additions & 1 deletion sandboxes/providers/cloudflare.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@

import httpx

from ..base import ExecutionResult, Sandbox, SandboxConfig, SandboxProvider, SandboxState
from ..base import (
ExecutionResult,
ProviderCapabilities,
Sandbox,
SandboxConfig,
SandboxProvider,
SandboxState,
)
from ..exceptions import ProviderError, SandboxError, SandboxNotFoundError
from ..security import validate_download_path, validate_upload_path

Expand All @@ -26,6 +33,12 @@
class CloudflareProvider(SandboxProvider):
"""Interact with a Cloudflare Sandbox Worker deployment via HTTP API."""

CAPABILITIES = ProviderCapabilities(
persistent=True,
streaming=True,
file_upload=True,
)

def __init__(
self,
*,
Expand Down
15 changes: 14 additions & 1 deletion sandboxes/providers/daytona.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
import os
from typing import Any

from ..base import ExecutionResult, Sandbox, SandboxConfig, SandboxProvider, SandboxState
from ..base import (
ExecutionResult,
ProviderCapabilities,
Sandbox,
SandboxConfig,
SandboxProvider,
SandboxState,
)
from ..exceptions import ProviderError, SandboxError, SandboxNotFoundError
from ..security import validate_download_path, validate_upload_path

Expand Down Expand Up @@ -34,6 +41,12 @@
class DaytonaProvider(SandboxProvider):
"""Daytona sandbox provider implementation."""

CAPABILITIES = ProviderCapabilities(
persistent=True,
snapshot=True,
file_upload=True,
)

def __init__(self, api_key: str | None = None, **config):
"""Initialize Daytona provider."""
super().__init__(**config)
Expand Down
15 changes: 14 additions & 1 deletion sandboxes/providers/e2b.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@
from datetime import datetime
from typing import Any

from ..base import ExecutionResult, Sandbox, SandboxConfig, SandboxProvider, SandboxState
from ..base import (
ExecutionResult,
ProviderCapabilities,
Sandbox,
SandboxConfig,
SandboxProvider,
SandboxState,
)
from ..exceptions import ProviderError, SandboxError, SandboxNotFoundError
from ..security import validate_download_path, validate_upload_path

Expand All @@ -27,6 +34,12 @@
class E2BProvider(SandboxProvider):
"""E2B sandbox provider using the official SDK."""

CAPABILITIES = ProviderCapabilities(
persistent=True,
streaming=True,
file_upload=True,
)

def __init__(self, api_key: str | None = None, **config):
"""
Initialize E2B provider.
Expand Down
15 changes: 14 additions & 1 deletion sandboxes/providers/hopx.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@
from datetime import datetime
from typing import Any

from ..base import ExecutionResult, Sandbox, SandboxConfig, SandboxProvider, SandboxState
from ..base import (
ExecutionResult,
ProviderCapabilities,
Sandbox,
SandboxConfig,
SandboxProvider,
SandboxState,
)
from ..exceptions import ProviderError, SandboxError, SandboxNotFoundError
from ..security import validate_download_path, validate_upload_path

Expand All @@ -27,6 +34,12 @@
class HopxProvider(SandboxProvider):
"""Hopx sandbox provider using the official hopx-ai SDK."""

CAPABILITIES = ProviderCapabilities(
persistent=True,
streaming=True,
file_upload=True,
)

def __init__(self, api_key: str | None = None, **config):
"""
Initialize Hopx provider.
Expand Down
14 changes: 13 additions & 1 deletion sandboxes/providers/modal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@
from datetime import datetime
from typing import Any

from ..base import ExecutionResult, Sandbox, SandboxConfig, SandboxProvider, SandboxState
from ..base import (
ExecutionResult,
ProviderCapabilities,
Sandbox,
SandboxConfig,
SandboxProvider,
SandboxState,
)
from ..exceptions import ProviderError, SandboxError, SandboxNotFoundError

logger = logging.getLogger(__name__)
Expand All @@ -27,6 +34,11 @@
class ModalProvider(SandboxProvider):
"""Modal sandbox provider implementation."""

CAPABILITIES = ProviderCapabilities(
persistent=True,
streaming=True,
)

def __init__(self, **config):
"""Initialize Modal provider.

Expand Down
15 changes: 14 additions & 1 deletion sandboxes/providers/sprites.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@
from datetime import datetime
from typing import Any

from ..base import ExecutionResult, Sandbox, SandboxConfig, SandboxProvider, SandboxState
from ..base import (
ExecutionResult,
ProviderCapabilities,
Sandbox,
SandboxConfig,
SandboxProvider,
SandboxState,
)
from ..exceptions import ProviderError, SandboxError, SandboxNotFoundError

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -51,6 +58,12 @@ class SpritesProvider(SandboxProvider):
- CLI mode: Uses sprite CLI with existing login (sprite login)
"""

CAPABILITIES = ProviderCapabilities(
persistent=True,
streaming=True,
interactive_shell=True,
)

def __init__(self, token: str | None = None, use_cli: bool = False, **config):
"""Initialize Sprites provider.

Expand Down
83 changes: 83 additions & 0 deletions tests/test_provider_capabilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Tests for provider capability declarations."""

from sandboxes.providers.cloudflare import CloudflareProvider
from sandboxes.providers.daytona import DaytonaProvider
from sandboxes.providers.e2b import E2BProvider
from sandboxes.providers.hopx import HopxProvider
from sandboxes.providers.modal import ModalProvider
from sandboxes.providers.sprites import SpritesProvider
from sandboxes.providers.vercel import VercelProvider


def test_provider_capability_matrix_contract():
"""Providers should declare capabilities that match implemented features."""
expected = {
"e2b": {
"persistent": True,
"snapshot": False,
"streaming": True,
"file_upload": True,
"interactive_shell": False,
"gpu": False,
},
"modal": {
"persistent": True,
"snapshot": False,
"streaming": True,
"file_upload": False,
"interactive_shell": False,
"gpu": False,
},
"daytona": {
"persistent": True,
"snapshot": True,
"streaming": False,
"file_upload": True,
"interactive_shell": False,
"gpu": False,
},
"hopx": {
"persistent": True,
"snapshot": False,
"streaming": True,
"file_upload": True,
"interactive_shell": False,
"gpu": False,
},
"sprites": {
"persistent": True,
"snapshot": False,
"streaming": True,
"file_upload": False,
"interactive_shell": True,
"gpu": False,
},
"cloudflare": {
"persistent": True,
"snapshot": False,
"streaming": True,
"file_upload": True,
"interactive_shell": False,
"gpu": False,
},
"vercel": {
"persistent": True,
"snapshot": True,
"streaming": True,
"file_upload": True,
"interactive_shell": True,
"gpu": False,
},
}

observed = {
"e2b": E2BProvider.get_capabilities().as_dict(),
"modal": ModalProvider.get_capabilities().as_dict(),
"daytona": DaytonaProvider.get_capabilities().as_dict(),
"hopx": HopxProvider.get_capabilities().as_dict(),
"sprites": SpritesProvider.get_capabilities().as_dict(),
"cloudflare": CloudflareProvider.get_capabilities().as_dict(),
"vercel": VercelProvider.get_capabilities().as_dict(),
}

assert observed == expected