fix(docker): add libero_plus install workaround to generic Dockerfile.benchmark

The generic Dockerfile.benchmark was using a plain `uv pip install ".[libero_plus]"`
which silently fails to make `libero` importable due to an upstream LIBERO-plus
packaging bug. Port the dedicated clone + .pth workaround from
Dockerfile.eval-libero-plus so `docker build --build-arg BENCHMARK=libero_plus`
produces working containers.

Also fix eval worker using nonexistent `parser.parse()` — use `draccus.parse()`.

Made-with: Cursor
This commit is contained in:
Pepijn Kooijmans
2026-03-23 18:31:57 +01:00
parent 8633608d26
commit e52e7e644a
3 changed files with 61 additions and 5 deletions
+25 -3
View File
@@ -39,6 +39,7 @@ ARG BENCHMARK=libero
ENV DEBIAN_FRONTEND=noninteractive \ ENV DEBIAN_FRONTEND=noninteractive \
MUJOCO_GL=egl \ MUJOCO_GL=egl \
PATH=/lerobot/.venv/bin:$PATH \ PATH=/lerobot/.venv/bin:$PATH \
CMAKE_POLICY_VERSION_MINIMUM=3.5 \
CUDA_VISIBLE_DEVICES=0 \ CUDA_VISIBLE_DEVICES=0 \
DEVICE=cuda \ DEVICE=cuda \
BENCHMARK=${BENCHMARK} BENCHMARK=${BENCHMARK}
@@ -82,7 +83,7 @@ ENV HOME=/home/user_lerobot \
TORCH_HOME=/home/user_lerobot/.cache/torch \ TORCH_HOME=/home/user_lerobot/.cache/torch \
TRITON_CACHE_DIR=/home/user_lerobot/.cache/triton TRITON_CACHE_DIR=/home/user_lerobot/.cache/triton
RUN uv venv --python python${PYTHON_VERSION} RUN uv venv --seed --python python${PYTHON_VERSION}
# Copy only the dependency manifests first so Docker can cache this layer # Copy only the dependency manifests first so Docker can cache this layer
# independently of source-code changes. # independently of source-code changes.
@@ -96,8 +97,29 @@ RUN if [ "$UNBOUND_DEPS" = "true" ]; then \
fi fi
# Install lerobot core + the selected benchmark extra. # Install lerobot core + the selected benchmark extra.
# Git-based deps (libero_plus, robomme) require network access at build time. # LIBERO-plus needs a dedicated install path because the upstream package is
RUN uv pip install --no-cache ".[${BENCHMARK}]" # import-broken when installed via the extras chain alone.
RUN case "${BENCHMARK}" in \
libero_plus) \
PATH=/usr/bin:/bin:/lerobot/.venv/bin:$PATH /lerobot/.venv/bin/python -m pip install --no-cache-dir \
"hf-libero>=0.1.3,<0.2.0" \
"hf-egl-probe>=1.0.1" \
"transformers>=5.3.0,<6.0.0" \
"scipy>=1.14.0,<2.0.0" \
"bddl>=1.0.1,<2.0.0" \
"future" \
"easydict>=1.9" \
"wand" \
"scikit-image>=0.20.0" \
"gym>=0.25.0,<0.27.0" \
&& git clone --depth 1 https://github.com/sylvestf/LIBERO-plus.git /tmp/LIBERO-plus \
&& PATH=/usr/bin:/bin:/lerobot/.venv/bin:$PATH /lerobot/.venv/bin/python -m pip install --no-cache-dir --no-deps /tmp/LIBERO-plus \
&& /lerobot/.venv/bin/python -c "import pathlib, site; pathlib.Path(site.getsitepackages()[0], 'libero_plus_repo.pth').write_text('/tmp/LIBERO-plus\n')" \
&& /lerobot/.venv/bin/python -m pip install --no-cache-dir . \
&& /lerobot/.venv/bin/python -c "import libero, robosuite, bddl" ;; \
*) \
uv pip install --no-cache ".[${BENCHMARK}]" ;; \
esac
# Triton requires its ptxas binary to be executable (NVIDIA-specific). # Triton requires its ptxas binary to be executable (NVIDIA-specific).
RUN if [ -f /lerobot/.venv/lib/python${PYTHON_VERSION}/site-packages/triton/backends/nvidia/bin/ptxas ]; then \ RUN if [ -f /lerobot/.venv/lib/python${PYTHON_VERSION}/site-packages/triton/backends/nvidia/bin/ptxas ]; then \
+2 -2
View File
@@ -39,10 +39,10 @@ import urllib.request
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
import draccus
import numpy as np import numpy as np
from lerobot import envs # noqa: F401 — registers all env subclasses from lerobot import envs # noqa: F401 — registers all env subclasses
from lerobot.configs import parser
from lerobot.envs.configs import EnvConfig from lerobot.envs.configs import EnvConfig
from lerobot.envs.factory import make_env from lerobot.envs.factory import make_env
from lerobot.envs.utils import add_envs_task, preprocess_observation from lerobot.envs.utils import add_envs_task, preprocess_observation
@@ -184,7 +184,7 @@ def worker_main(cfg: EvalWorkerConfig) -> None:
def main() -> None: def main() -> None:
init_logging() init_logging()
cfg = parser.parse(EvalWorkerConfig) cfg = draccus.parse(config_class=EvalWorkerConfig)
worker_main(cfg) worker_main(cfg)
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env python
# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pathlib import Path
from lerobot.scripts import lerobot_eval_worker
def test_worker_main_writes_results(monkeypatch, tmp_path: Path):
cfg = lerobot_eval_worker.EvalWorkerConfig(
env="pusht", # type: ignore[arg-type]
instance_id=3,
output_path=tmp_path / "worker.json",
)
monkeypatch.setattr(lerobot_eval_worker, "run_worker", lambda _cfg: {"per_task": []})
lerobot_eval_worker.worker_main(cfg)
assert cfg.output_path.exists()
assert cfg.output_path.read_text().strip() == '{\n "per_task": []\n}'