Feat: add enroot backend - #1952
Conversation
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
|
Did you run this with any environments? |
|
@cmunley1 Yes, I ran it using the following commands. And then, If you have any environment suggestion that I should test with, please let me know. |
Signed-off-by: Onur Yilmaz <35306097+oyilmaz-nvidia@users.noreply.github.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
…eMo/Gym into onur/add-enroot-backend
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
|
/claude review |
Signed-off-by: Onur Yilmaz <35306097+oyilmaz-nvidia@users.noreply.github.com>
- Narrow ENROOT_RUNTIME_ERROR_MARKERS: remove "failed to" and "does not exist" (too broad; appear in normal user command stderr and corrupted the exit-code signal to the agent) - Harden cache directory creation with mode=0o700 to prevent predictable /tmp path from being pre-seeded; validate cached .sqsh is a regular file owned by the current uid before reusing - Guard upload_file/download_file fast paths against symlinks in the bind-mounted staging dir (container-controlled symlinks could leak host files via host-side read_bytes) - Add PID reuse protection: verify /proc/<pid>/cmdline still carries the container's unique name marker before each enroot exec call, using a mockable _pid_has_container_marker method - Add bypass_entrypoint=True to EnrootCreateConfig: passes -e ENROOT_ENTRYPOINT= to enroot start so Docker images with a non-shell entrypoint do not wrap and break the init command - Fix heredoc delimiter collision in mini_swe_agent_2: use a UUID- suffixed delimiter so eval_script lines containing bare "EVEOF" do not silently truncate the script - Update test_create_builds_argv_and_runs_probe to match the init command marker and bypass_entrypoint flag; add targeted tests for all new safeguards (stale PID, broad stderr, symlink guard) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The previous attempt passed -e ENROOT_ENTRYPOINT= which does nothing — enroot bakes the Docker ENTRYPOINT directly into /etc/rc inside the rootfs during `enroot create` (docker.sh line 239). The only way to suppress it at start time is `enroot start --rc SCRIPT`, which replaces /etc/rc entirely. Passing /dev/null gives an empty rc so the init command runs directly. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Join split string in download_file error message (line-length) - Add missing blank lines after _load_enroot_provider() in registry.py Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…into onur/add-enroot-backend
…elimiter
uuid4().hex fails when uuid4 is monkeypatched to return a plain string in
tests. str(uuid4()).replace('-', '') works for both real UUID objects and
string stubs.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
|
@hemildesai Addressed the review and added instruction to run the mini swe bench with 5 environments. Got 0.6 average reward. |
|
/claude review |
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
|
/claude review |
|
SHIP — LGTM, no reliability concerns. Reviewed all 12 files against the base. This is a clean addition of an enroot sandbox provider plus a shared-helper refactor and two small defensive fixes. Nothing blocks merge. What I verified:
Config follows convention (YAML is source of truth; dataclass field defaults mirror the exemplar YAML, and the README documents them). Imports are all stdlib or existing internal — no new dependencies. |
## Add enroot sandbox backend Adds `enroot` as a built-in sandbox provider, giving NeMo Gym rootless, single-node container isolation via the local [enroot](https://github.com/NVIDIA/enroot) CLI. This is well-suited to NVIDIA/Slurm HPC nodes where enroot (paired with pyxis) is the supported container runtime. ### What's included - **`EnrootProvider`** (`nemo_gym/sandbox/providers/enroot/provider.py`) — runs each sandbox as a long-lived enroot container by shelling out to the `enroot` binary. Owns the full container lifecycle (create/start/exec/cleanup) from a single process, supports `.sqsh` images, enroot import URIs, and Docker image references (e.g. `ubuntu:22.04`, `nvcr.io/nvidia/pytorch:24.01`), and implements the provider-neutral sandbox API (`exec`, `upload`, `download`, env/file injection, sync + async). - **Registry wiring** — registers the `enroot` provider name in `nemo_gym/sandbox/providers/registry.py` via a lazy loader. - **Config** — default `nemo_gym/sandbox/providers/enroot/configs/enroot.yaml`. - **Docs** — `nemo_gym/sandbox/providers/enroot/README.md` covering requirements, quick start, and constraints (requires `enroot` on `PATH`, unprivileged user namespaces; does not auto-install). - **Tests** — `tests/unit_tests/test_enroot_provider.py` (~900 lines). ### Quick Test In Gym folder ``` In the Gym folder, run; 1. source .venv/bin/activate Then, to generate sqsh files, run; 2. python scripts/enroot_prefetch_sqsh.py \ --sqsh-dir /path/to/enroot_sqshs \ 'docker.io/swebench/sweb.eval.x86_64.django_1776_django-10973:latest' \ 'docker.io/swebench/sweb.eval.x86_64.sphinx-doc_1776_sphinx-8595:latest' \ 'docker.io/swebench/sweb.eval.x86_64.scikit-learn_1776_scikit-learn-14141:latest' \ 'docker.io/swebench/sweb.eval.x86_64.sympy_1776_sympy-20916:latest' \ 'docker.io/swebench/sweb.eval.x86_64.pylint-dev_1776_pylint-4551:latest' 3. HF_LOGIN=... gym env start \ --config responses_api_agents/mini_swe_agent_2/configs/mini_swe_agent_2.yaml \ --config nemo_gym/sandbox/providers/enroot/configs/enroot.yaml \ --model-type local_vllm_model \ --model Qwen/Qwen3.6-27B-FP8 \ '++policy_model.responses_api_models.local_vllm_model.vllm_serve_kwargs.tensor_parallel_size=2' \ '++policy_model.responses_api_models.local_vllm_model.vllm_serve_env_vars.VLLM_RAY_DP_PACK_STRATEGY=strict' \ '++policy_model.responses_api_models.local_vllm_model.vllm_serve_kwargs.enable_auto_tool_choice=true' \ '++policy_model.responses_api_models.local_vllm_model.vllm_serve_kwargs.tool_call_parser=qwen3_coder' \ '++policy_model.responses_api_models.local_vllm_model.vllm_serve_kwargs.reasoning_parser=qwen3' \ '++policy_model.responses_api_models.local_vllm_model.uses_reasoning_parser=true' \ '++policy_model.responses_api_models.local_vllm_model.vllm_serve_kwargs.quantization=fp8' \ '++sandbox.enroot.create.sqsh_cache_dir=/path/to/enroot_sqshs' \ '++sandbox.enroot.create.bypass_entrypoint=false' & 4. gym eval run --no-serve \ --agent mini_swe_agent_2 \ --input responses_api_agents/mini_swe_agent_2/data/example.jsonl \ --output results/mini_swe_agent_2_v2.jsonl \ --limit 5 \ --num-repeats 1 \ --temperature 0.5 \ --max-output-tokens 2048 ``` After running the above, the result is as follows; Computing aggregate metrics INFO: 127.0.0.1:36258 - "GET /global_config_dict_yaml HTTP/1.1" 200 OK Key metrics for mini_swe_agent_2: { "pass@1/accuracy": 60.0, "pass@1[avg-of-1]/accuracy": 60.0, "mean/reward": 0.6, "resolved_task_count": 3, "task_count": 5, "resolved_task_rate": 60.0, "eval_error_rate": 0.0, "tests_status_rate": 100.0 } Finished rollout collection! View results at: Fully materialized inputs: results/mini_swe_agent_2_v2_materialized_inputs.jsonl Rollouts: results/mini_swe_agent_2_v2.jsonl Aggregate metrics: results/mini_swe_agent_2_v2_aggregate_metrics.json ### Notes - Requires the `enroot` binary already installed; constructing the provider raises `RuntimeError` if it's missing. - All enroot calls run as the same OS user (`enroot exec` re-enters namespaces owned by the launching user). --------- Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com> Signed-off-by: Onur Yilmaz <35306097+oyilmaz-nvidia@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Add enroot sandbox backend
Adds
enrootas a built-in sandbox provider, giving NeMo Gym rootless, single-node container isolation via the local enroot CLI. This is well-suited to NVIDIA/Slurm HPC nodes where enroot (paired with pyxis) is the supported container runtime.What's included
EnrootProvider(nemo_gym/sandbox/providers/enroot/provider.py) — runs each sandbox as a long-lived enroot container by shelling out to theenrootbinary. Owns the full container lifecycle (create/start/exec/cleanup) from a single process, supports.sqshimages, enroot import URIs, and Docker image references (e.g.ubuntu:22.04,nvcr.io/nvidia/pytorch:24.01), and implements the provider-neutral sandbox API (exec,upload,download, env/file injection, sync + async).enrootprovider name innemo_gym/sandbox/providers/registry.pyvia a lazy loader.nemo_gym/sandbox/providers/enroot/configs/enroot.yaml.nemo_gym/sandbox/providers/enroot/README.mdcovering requirements, quick start, and constraints (requiresenrootonPATH, unprivileged user namespaces; does not auto-install).tests/unit_tests/test_enroot_provider.py(~900 lines).Quick Test
In Gym folder
After running the above, the result is as follows;
Computing aggregate metrics
INFO: 127.0.0.1:36258 - "GET /global_config_dict_yaml HTTP/1.1" 200 OK
Key metrics for mini_swe_agent_2:
{
"pass@1/accuracy": 60.0,
"pass@1[avg-of-1]/accuracy": 60.0,
"mean/reward": 0.6,
"resolved_task_count": 3,
"task_count": 5,
"resolved_task_rate": 60.0,
"eval_error_rate": 0.0,
"tests_status_rate": 100.0
}
Finished rollout collection! View results at:
Fully materialized inputs: results/mini_swe_agent_2_v2_materialized_inputs.jsonl
Rollouts: results/mini_swe_agent_2_v2.jsonl
Aggregate metrics: results/mini_swe_agent_2_v2_aggregate_metrics.json
Notes
enrootbinary already installed; constructing the provider raisesRuntimeErrorif it's missing.enroot execre-enters namespaces owned by the launching user).