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 \
MUJOCO_GL=egl \
PATH=/lerobot/.venv/bin:$PATH \
CMAKE_POLICY_VERSION_MINIMUM=3.5 \
CUDA_VISIBLE_DEVICES=0 \
DEVICE=cuda \
BENCHMARK=${BENCHMARK}
@@ -82,7 +83,7 @@ ENV HOME=/home/user_lerobot \
TORCH_HOME=/home/user_lerobot/.cache/torch \
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
# independently of source-code changes.
@@ -96,8 +97,29 @@ RUN if [ "$UNBOUND_DEPS" = "true" ]; then \
fi
# Install lerobot core + the selected benchmark extra.
# Git-based deps (libero_plus, robomme) require network access at build time.
RUN uv pip install --no-cache ".[${BENCHMARK}]"
# LIBERO-plus needs a dedicated install path because the upstream package is
# 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).
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 pathlib import Path
import draccus
import numpy as np
from lerobot import envs # noqa: F401 — registers all env subclasses
from lerobot.configs import parser
from lerobot.envs.configs import EnvConfig
from lerobot.envs.factory import make_env
from lerobot.envs.utils import add_envs_task, preprocess_observation
@@ -184,7 +184,7 @@ def worker_main(cfg: EvalWorkerConfig) -> None:
def main() -> None:
init_logging()
cfg = parser.parse(EvalWorkerConfig)
cfg = draccus.parse(config_class=EvalWorkerConfig)
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}'