Buckets:
| # Runtime Providers | |
| A runtime provider starts an environment server and returns a `base_url` that an | |
| `EnvClient` connects to. Container providers implement the same | |
| `ContainerProvider` contract, so switching from local Docker to a cloud sandbox | |
| is a one-line change. | |
| ## Available providers | |
| | Provider | Backend | Install | Status | | |
| |----------|---------|---------|--------| | |
| | `LocalDockerProvider` | Local Docker daemon | core | ✅ | | |
| | `DockerSwarmProvider` | Docker Swarm cluster | core | ✅ | | |
| | `UVProvider` | Local process via `uv` (no container) | core | ✅ | | |
| | `DaytonaProvider` | Daytona cloud sandboxes | `pip install openenv[daytona]` | ✅ | | |
| | `ACASandboxProvider` | Azure Container Apps Sandboxes | `pip install openenv[aca]` | ✅ | | |
| | `ModalProvider` | Modal sandboxes | `pip install openenv[modal]` | ✅ | | |
| | `KubernetesProvider` | Kubernetes cluster | core | 🚧 planned | | |
| Cloud-provider SDKs are optional extras, imported lazily, so installing core | |
| OpenEnv pulls in no cloud SDK. The core providers (`LocalDockerProvider`, | |
| `DockerSwarmProvider`, `UVProvider`) are re-exported from the runtime package; | |
| cloud providers are imported from their module: | |
| ```python | |
| from openenv.core.containers.runtime import LocalDockerProvider # core | |
| from openenv.core.containers.runtime.daytona_provider import DaytonaProvider # cloud | |
| ``` | |
| See the [Core API reference](../reference/core#container-providers) for each | |
| provider's full API. | |
| ## Lifecycle | |
| Container providers that store their source image on the provider can be owned | |
| by the client. In this form, the client starts the provider on first connect, | |
| waits for readiness, and stops the provider when the client closes: | |
| ```python | |
| image = DaytonaProvider.image_from_dockerfile("envs/echo_env/server/Dockerfile") | |
| provider = DaytonaProvider(image=image) | |
| async with MyEnv(provider=provider) as env: | |
| result = await env.reset() | |
| ... | |
| ``` | |
| `ModalProvider`, `DaytonaProvider`, and `ACASandboxProvider` support this | |
| provider-owned flow. Providers that require an explicit image at | |
| `start_container()` time, such as `LocalDockerProvider` and | |
| `DockerSwarmProvider`, should still be started manually and passed in with the | |
| returned `base_url`: | |
| ```python | |
| base_url = provider.start_container(image) | |
| provider.wait_for_ready(base_url, timeout_s=180) | |
| try: | |
| async with MyEnv(base_url=base_url, provider=provider) as env: | |
| result = await env.reset() | |
| ... | |
| finally: | |
| provider.stop_container() | |
| ``` | |
| `UVProvider` is not a container provider: it runs the server as a local process | |
| and exposes `.start()` / `.wait_for_ready()` / `.stop()` instead. | |
| ## Reusing one server for multiple sessions | |
| After a client has connected, call `new_session()` to open another independent | |
| environment session against the same running server: | |
| ```python | |
| async with MyEnv(provider=provider) as env: | |
| first = await env.reset() | |
| child = await env.new_session() | |
| second = await child.reset() | |
| ``` | |
| Child sessions are owned by the parent client: closing the parent also closes | |
| any children it created. You can still close a child earlier when you no longer | |
| need it. Server capacity limits still apply, so `new_session()` can fail while | |
| opening the child WebSocket when the server has reached `MAX_CONCURRENT_ENVS`. | |
| ## Running many environments in parallel | |
| Scaling out (for example, many concurrent RL rollouts) is a main reason cloud | |
| providers exist. The model is one provider and one client per environment: each | |
| provider starts its own isolated sandbox, so they run independently. Launch them | |
| concurrently with `asyncio.gather`, wrapping the blocking provider calls in | |
| `asyncio.to_thread` since most cloud SDKs are synchronous: | |
| ```python | |
| async def run_one(env_id: int, image) -> str: | |
| provider = DaytonaProvider() | |
| base_url = await asyncio.to_thread(provider.start_container, image) | |
| try: | |
| await asyncio.to_thread(provider.wait_for_ready, base_url, 300) | |
| async with MyEnv(base_url=base_url, provider=provider) as env: | |
| result = await env.reset() | |
| return result.observation.text | |
| finally: | |
| await asyncio.to_thread(provider.stop_container) | |
| image = DaytonaProvider.image_from_dockerfile("envs/echo_env/server/Dockerfile") | |
| results = await asyncio.gather(*(run_one(i, image) for i in range(20))) | |
| ``` | |
| Full example: [`examples/daytona_tbench2_concurrent.py`](https://github.com/huggingface/OpenEnv/blob/main/examples/daytona_tbench2_concurrent.py) | |
| spins up N sandboxes concurrently and reports per-stage timing. | |
| ## Per-provider setup | |
| ### ACASandboxProvider | |
| Runs the server in an Azure Container Apps Sandbox. Install with | |
| `pip install openenv[aca]`. Requires Azure credentials (`credential=None` | |
| falls back to `DefaultAzureCredential`). | |
| ```python | |
| from openenv.core.containers.runtime.aca_provider import ACASandboxProvider | |
| provider = ACASandboxProvider( | |
| image="disk:my-env", | |
| subscription_id="<subscription-id>", | |
| resource_group="<resource-group>", | |
| sandbox_group="<sandbox-group>", | |
| region="eastus", # used to derive the endpoint when endpoint=None | |
| endpoint=None, | |
| credential=None, # defaults to DefaultAzureCredential() | |
| sdk_kwargs={}, | |
| ) | |
| ``` | |
| ### DaytonaProvider | |
| Runs the server in a Daytona cloud sandbox. Install with | |
| `pip install openenv[daytona]`. Requires the `DAYTONA_API_KEY` environment | |
| variable. | |
| ```python | |
| from openenv.core.containers.runtime.daytona_provider import DaytonaProvider | |
| image = DaytonaProvider.image_from_dockerfile("envs/echo_env/server/Dockerfile") | |
| provider = DaytonaProvider(image=image) | |
| ``` | |
| Full examples: [`examples/daytona_tbench2_simple.py`](https://github.com/huggingface/OpenEnv/blob/main/examples/daytona_tbench2_simple.py) | |
| and [`examples/daytona_tbench2_concurrent.py`](https://github.com/huggingface/OpenEnv/blob/main/examples/daytona_tbench2_concurrent.py). | |
| ### DockerSwarmProvider | |
| Deploys the server as a service on a Docker Swarm cluster. Initializes Swarm | |
| automatically when it is not already active. | |
| ```python | |
| from openenv.core.containers.runtime import DockerSwarmProvider | |
| provider = DockerSwarmProvider() | |
| ``` | |
| ### KubernetesProvider | |
| 🚧 Not yet implemented. The class exists as a placeholder for the planned | |
| Kubernetes backend. | |
| ### LocalDockerProvider | |
| Runs the server on the local Docker daemon. This is the default for | |
| `from_docker_image`, so you rarely construct it explicitly. | |
| ```python | |
| from openenv.core.containers.runtime import LocalDockerProvider | |
| provider = LocalDockerProvider() | |
| ``` | |
| ### ModalProvider | |
| Runs the server in a Modal sandbox over an encrypted tunnel. Install with | |
| `pip install openenv[modal]`. Requires a configured Modal account | |
| (`modal setup`). | |
| ```python | |
| from openenv.core.containers.runtime.modal_provider import ModalProvider | |
| image = ModalProvider.image_from_dockerfile("envs/echo_env/server/Dockerfile") | |
| provider = ModalProvider(app_name="openenv", image=image) | |
| ``` | |
| Full example: [`examples/modal_echo_env.py`](https://github.com/huggingface/OpenEnv/blob/main/examples/modal_echo_env.py). | |
| ### UVProvider | |
| Runs the server as a local process via `uv`, without a container. Useful for | |
| developing an environment from a checkout. | |
| ```python | |
| from openenv.core.containers.runtime import UVProvider | |
| provider = UVProvider(project_path="path/to/env") | |
| base_url = provider.start() | |
| provider.wait_for_ready() | |
| ``` | |
Xet Storage Details
- Size:
- 7.33 kB
- Xet hash:
- c57ce469c9962bda73584fc4950924644897dc55939f32f36cc895def9508480
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.