Skip to content

Move the shared sandbox layer to openenv.core.sandbox#1010

Draft
sergiopaniego wants to merge 21 commits into
huggingface:mainfrom
sergiopaniego:sandbox-to-core
Draft

Move the shared sandbox layer to openenv.core.sandbox#1010
sergiopaniego wants to merge 21 commits into
huggingface:mainfrom
sergiopaniego:sandbox-to-core

Conversation

@sergiopaniego

Copy link
Copy Markdown
Member

Warning

Do not merge until #999 lands. This branch is stacked on #999 (pi_env). Opening it against main is the only option (GitHub cannot base an upstream PR on a fork branch), so the diff currently includes all of #999; it auto-cleans to just the sandbox-move once #999 merges. Review the sandbox-move commit; the pi_env files belong to #999.

Moves the shared sandbox layer out of envs/opencode_env/sandbox/ into src/openenv/core/sandbox/, so opencode_env and pi_env both import it from core instead of pi_env peer-depending on opencode_env. This closes the peer-dependency flagged in #999 (a plain pip install openenv-pi-env no longer needs opencode_env).

What moves

SandboxBackend / SandboxHandle / BgJob / ExecResult (the protocol), the E2B and Hugging Face backends, interception.py (the proxy), and build_template.pyopenenv.core.sandbox.

Notable changes

  • opencode_env and pi_env import from openenv.core.sandbox. pi_env drops its opencode_env peer dep: the server Dockerfile no longer installs openenv-opencode-env, and _PROXY_SOURCE_PATH reads interception.py from core.
  • interception.py is no longer COPYed into the opencode pre-baked image; the harness uploads it at rollout start (matching the existing pi-sandbox image). The opencode hf_image/ dir moves out of sandbox/ to opencode_env/hf_image/ to match pi, and docker-build.yml is updated.
  • e2b stays a lazy import in core/sandbox/__init__.py, so core keeps no hard e2b dependency (unchanged behavior).
  • READMEs + env docs updated (sync_env_docs --check green).

No backward-compat shim (deliberate)

Both opencode_env and the TRL async_grpo harness path are experimental, so a clean move is fine. Known downstream that must update one import opencode_env.sandboxopenenv.core.sandbox:

  • The TRL OpenCode example (examples/scripts/openenv/opencode.py, Async grpo OpenEnv harness rollout trl#6420) imports from opencode_env.sandbox.base import ExecResult, SandboxHandle — one-line change, for its author to apply.
  • Our self-contained HF-sandbox gists — we update them when this lands.

AI-assisted.

…roxy

PiSessionFactory/PiSession/PiConfig mirror the OpenCode primitive: install the
pi CLI (Node bootstrap + npm), write models.json pointing at an OpenAI-compatible
endpoint, run pi --print --mode json headless, and (transparent_proxy mode)
capture per-token logprobs via the shared interception proxy for GRPO.

The sandbox backend (HFSandboxBackend) and interception proxy are imported from
opencode_env for now; the plan is to consolidate both into openenv.core in a
follow-up. Includes builder + factory-lifecycle unit tests.
Mirrors opencode_env's second layer so pi_env is both an in-process harness
primitive and a deployable OpenEnv environment: PiEnv MCP client + FastAPI
server (PiEnvironment with the run_rollout tool, task catalog, Gradio UI) +
Dockerfile + openenv.yaml. HF sandbox (image, HF_TOKEN) replaces E2B
(template, E2B_API_KEY); in-sandbox paths are /root; the sandbox backend and
interception proxy are imported from opencode_env. Adds uv.lock and the
pi-env server-image entry to docker-build.yml.
sync_env_docs.py --fix generates the docs/source/environments/pi.md stub
from the README; the HTML catalog card in environments.md and the
_toctree.yml nav entry are added manually (the script does not manage
those). check-env-docs now passes.
…, pipefail

- README: correct the HF sandbox docs URL, drop redundant/circular links
- .dockerignore: HF_TOKEN, not E2B_API_KEY
- pi_runtime: gate Node >=22.19 (not just major 22); use the full model id and
  select by --provider so a slashed id isn't split; set -o pipefail so the
  piped tee doesn't mask Pi's exit code
…docs

Import the primitive from its defining modules instead of the top-level
pi_env package (which re-exports the client), per the server/client-separation
invariant. Reword the setup docs: it runs at rollout start, not strictly
before the agent.
…dead field

- harness/server: size the sandbox lifetime and MCP-tool timeout to the full
  Node+Pi cold install, not just the agent
- README: note the opencode_env install prerequisite for the primitive path;
  correct the server Dockerfile comment
- config: drop unused request_timeout_ms
- client: document the endpoint shorthand
Mirror opencode_env's pre-baked image: bake Node + Pi + proxy deps so rollouts
skip the cold install (the proxy source is uploaded at runtime since it lives
in opencode_env). Also drop the stale '(default)' on transparent_proxy in the
harness docstring (the factory default is black_box).
…ps; guard UI stderr

- server: only run verify + compute reward when the run is clean (a failed
  setup/agent leaves a half-prepared sandbox); dedupe the endpoint error paren
- harness: set -o pipefail on the proxy-deps install so a failed pip isn't
  masked by tail
- gradio: don't index an empty stderr split (IndexError when exit!=0, no stderr)
…e separately

- token check via huggingface_hub.get_token() (HF_TOKEN, legacy var, or a
  cached login) instead of guessing env var names
- _start_proxy: skip the pip install when the deps import, independent of the
  proxy source upload, so the pre-baked image (deps baked, source not) skips it
Mirror of the OpenCodeSessionFactory retry: session creation (sandbox + Pi
install + proxy + agent) is the flakiest step, and a single transient failure
dropped the rollout as unscorable. create() now retries create_attempts times
with exponential backoff; the per-attempt logic moved to _create_once, which
already tears its own sandbox down on failure, so a retry never leaks.
The sandbox protocol (SandboxBackend/SandboxHandle/BgJob/ExecResult), the E2B and
Hugging Face backends, the interception proxy, and build_template move from
envs/opencode_env/sandbox/ to src/openenv/core/sandbox/, so both opencode_env and
pi_env import them from core instead of pi_env peer-depending on opencode_env.

- opencode_env and pi_env now import from openenv.core.sandbox; pi_env drops its
  opencode_env peer dependency (the server Dockerfile no longer installs it, and
  `pip install openenv-pi-env` no longer needs opencode_env).
- interception.py is no longer baked into the opencode pre-baked image; the harness
  uploads it at rollout start from core (matching the pi-sandbox image). The opencode
  hf_image dir moves out of sandbox/ to opencode_env/hf_image/ to match pi.
- e2b stays a lazy import so core keeps no hard e2b dependency.
- No backward-compat shim: both opencode_env and the TRL async_grpo path are
  experimental, so external importers (our gists, the TRL opencode example) update to
  openenv.core.sandbox directly.

AI-assisted.
Copilot AI review requested due to automatic review settings July 24, 2026 07:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

They now live under src/ (lint-checked) rather than envs/ (not linted), so usort +
ruff format run over them.
Copilot AI review requested due to automatic review settings July 24, 2026 09:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Under src/ these are ruff-lint-checked (they were not under envs/): remove the
unused typing.Any in base.py and .base.SandboxBackend in e2b.py.
Copilot AI review requested due to automatic review settings July 24, 2026 09:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants