Skip to content
Open
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
28 changes: 26 additions & 2 deletions docs/source/guides/runtime-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ is a one-line change.
| `DaytonaProvider` | Daytona cloud sandboxes | `pip install openenv[daytona]` | ✅ |
| `ACASandboxProvider` | Azure Container Apps Sandboxes | `pip install openenv[aca]` | ✅ |
| `ModalProvider` | Modal sandboxes | `pip install openenv[modal]` | ✅ |
| `FystashProvider` | Fystash warm Firecracker rooms | `pip install openenv[fystash]` | ✅ experimental |
| `KubernetesProvider` | Kubernetes cluster | core | 🚧 planned |

Cloud-provider SDKs are optional extras, imported lazily, so installing core
Expand All @@ -25,6 +26,7 @@ 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
from openenv.core.containers.runtime.fystash_provider import FystashProvider # cloud
```

See the [Core API reference](../reference/core.md#container-providers) for each
Expand All @@ -45,8 +47,9 @@ async with MyEnv(provider=provider) as env:
...
```

`ModalProvider`, `DaytonaProvider`, and `ACASandboxProvider` support this
provider-owned flow. Providers that require an explicit image at
`ModalProvider`, `DaytonaProvider`, `ACASandboxProvider`, and `FystashProvider`
support this provider-owned flow (Fystash requires a **registry image** in v1 —
no Dockerfile build). 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`:
Expand Down Expand Up @@ -148,6 +151,27 @@ 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).

### FystashProvider

Runs the server in a Fystash warm Firecracker room (`template_id=docker` by
default). Install with `pip install openenv[fystash]`. Requires
`FYSTASH_API_KEY` (signup: https://fystash.ai/signup). Optional:
`FYSTASH_API`, `FYSTASH_TEMPLATE_ID`.

v1 accepts a **registry image** only (no `image_from_dockerfile`). After
`docker pull` / `docker run` in the guest, port 8000 is exposed via Fystash
preview URL.

```python
from openenv.core.containers.runtime.fystash_provider import FystashProvider

provider = FystashProvider(image="your-org/echo-env:latest")
base_url = provider.start_container()
provider.wait_for_ready(base_url, timeout_s=300)
```

Full example: [`examples/fystash_echo_env.py`](https://github.com/huggingface/OpenEnv/blob/main/examples/fystash_echo_env.py).

### DockerSwarmProvider

Deploys the server as a service on a Docker Swarm cluster. Initializes Swarm
Expand Down
2 changes: 2 additions & 0 deletions docs/source/reference/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,5 @@ For a high-level explanation of how MCP-backed environments move through `step()
[[autodoc]] openenv.core.containers.runtime.aca_provider.ACASandboxProvider

[[autodoc]] openenv.core.containers.runtime.modal_provider.ModalProvider

[[autodoc]] openenv.core.containers.runtime.fystash_provider.FystashProvider
56 changes: 56 additions & 0 deletions examples/fystash_echo_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Hello-world example running an OpenEnv server image on Fystash.

Boots a registry image inside a Fystash docker-template room via
``FystashProvider``, waits for ``/health``, then tears down.

Usage:
export FYSTASH_API_KEY=key-…
# optional: FYSTASH_API=https://api.fystash.ai FYSTASH_TEMPLATE_ID=docker
export OPENENV_IMAGE=your-org/echo-env:latest # registry image required (v1)
PYTHONPATH=src uv run python examples/fystash_echo_env.py

Requires:
A Fystash org API key (https://fystash.ai/signup → Account).
A published OpenEnv server image (v1 does not build Dockerfiles).
"""

from __future__ import annotations

import logging
import os
import sys

from openenv.core.containers.runtime.fystash_provider import FystashProvider

logger = logging.getLogger(__name__)


def main() -> int:
logging.basicConfig(level=logging.INFO, format="%(message)s")

image = os.environ.get("OPENENV_IMAGE")
if not image:
logger.error(
"Set OPENENV_IMAGE to a registry tag "
"(FystashProvider v1 does not build Dockerfiles)."
)
return 2
if not os.environ.get("FYSTASH_API_KEY"):
logger.error("Set FYSTASH_API_KEY (https://fystash.ai/signup).")
return 2

with FystashProvider(image=image) as provider:
logger.info("Starting Fystash room + docker pull/run...")
base_url = provider.start_container()
logger.info("Preview URL: %s", base_url)
logger.info("Waiting for /health...")
provider.wait_for_ready(base_url, timeout_s=300)
logger.info("Server ready at %s", base_url)
logger.info("Stopping room...")

return 0


if __name__ == "__main__":
raise SystemExit(main())
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ modal = [
"modal>=1.4.0",
"pyyaml>=6.0",
]
fystash = [
# Uses core httpx + requests only; FYSTASH_API_KEY required at runtime.
]
inspect = [
"inspect-ai>=0.3.0",
]
Expand Down
7 changes: 4 additions & 3 deletions src/openenv/core/containers/runtime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
from .uv_provider import UVProvider

# Note: optional cloud providers that require extra SDKs (e.g.
# `ACASandboxProvider`, `DaytonaProvider`) are intentionally NOT re-exported
# here. Import them from their module, e.g.
# `from openenv.core.containers.runtime.aca_provider import ACASandboxProvider`.
# `ACASandboxProvider`, `DaytonaProvider`, `FystashProvider`) are intentionally
# NOT re-exported here. Import them from their module, e.g.
# `from openenv.core.containers.runtime.aca_provider import ACASandboxProvider`
# `from openenv.core.containers.runtime.fystash_provider import FystashProvider`.

__all__ = [
"ContainerProvider",
Expand Down
Loading