# 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. # Isolated benchmark image for LIBERO-plus integration tests. # Installs only lerobot[libero_plus] (LIBERO-plus from GitHub, dm-control, mujoco). # # Build: docker build -f docker/Dockerfile.benchmark.libero_plus -t lerobot-benchmark-libero-plus . # Run: docker run --gpus all --rm lerobot-benchmark-libero-plus lerobot-eval ... ARG CUDA_VERSION=12.4.1 ARG OS_VERSION=22.04 FROM nvidia/cuda:${CUDA_VERSION}-base-ubuntu${OS_VERSION} ARG PYTHON_VERSION=3.12 ENV DEBIAN_FRONTEND=noninteractive \ MUJOCO_GL=egl \ PATH=/lerobot/.venv/bin:$PATH \ CUDA_VISIBLE_DEVICES=0 \ DEVICE=cuda # System deps — same set as Dockerfile.internal plus LIBERO-plus extras RUN apt-get update && apt-get install -y --no-install-recommends \ software-properties-common build-essential git curl unzip \ libglib2.0-0 libgl1-mesa-glx libegl1-mesa ffmpeg \ libusb-1.0-0-dev speech-dispatcher libgeos-dev portaudio19-dev \ cmake pkg-config ninja-build \ libexpat1 libfontconfig1-dev libmagickwand-dev \ && add-apt-repository -y ppa:deadsnakes/ppa \ && apt-get update \ && apt-get install -y --no-install-recommends \ python${PYTHON_VERSION} \ python${PYTHON_VERSION}-venv \ python${PYTHON_VERSION}-dev \ && curl -LsSf https://astral.sh/uv/0.8.0/install.sh | sh \ && mv /root/.local/bin/uv /usr/local/bin/uv \ && useradd --create-home --shell /bin/bash user_lerobot \ && usermod -aG sudo user_lerobot \ && apt-get clean && rm -rf /var/lib/apt/lists/* WORKDIR /lerobot RUN chown -R user_lerobot:user_lerobot /lerobot USER user_lerobot ENV HOME=/home/user_lerobot \ HF_HOME=/home/user_lerobot/.cache/huggingface \ HF_LEROBOT_HOME=/home/user_lerobot/.cache/huggingface/lerobot \ TORCH_HOME=/home/user_lerobot/.cache/torch \ TRITON_CACHE_DIR=/home/user_lerobot/.cache/triton RUN uv venv --python python${PYTHON_VERSION} # Install only lerobot[libero_plus] — isolated from hf-libero and metaworld dep trees COPY --chown=user_lerobot:user_lerobot setup.py pyproject.toml uv.lock README.md MANIFEST.in ./ COPY --chown=user_lerobot:user_lerobot src/ src/ # Use uv pip install (not uv sync) — uv sync resolves all extras across all # Python versions, hitting cross-extra numpy conflicts. pip-style install # only resolves the requested extras for the current platform. # Install lerobot with libero_plus and smolvla extras. # The libero git dep sometimes gets silently skipped by uv pip install # when embedded in self-referential extras, so install it explicitly too. # Step 1: install lerobot with extras (libero git dep may be silently skipped) RUN uv pip install --no-cache -e ".[libero_plus,smolvla]" # Step 2: LIBERO-plus has a nested libero/libero/ structure that neither uv pip # install from git nor editable installs handle correctly (metadata-only install). # Clone and add to PYTHONPATH as the only reliable way to make it importable. RUN git clone --depth=1 https://github.com/sylvestf/LIBERO-plus.git /home/user_lerobot/libero-plus \ && mkdir -p /home/user_lerobot/.libero \ && echo 'assets: /tmp' > /home/user_lerobot/.libero/config.yaml # Install LIBERO-plus deps (robosuite, bddl, easydict, mujoco, etc.) from setup.py. # Pin robosuite==1.4.1 (fork uses single_arm_env removed in v1.5+), then install # the remaining deps from the clone. --no-build-isolation avoids rebuilding lerobot. RUN uv pip install --no-cache "robosuite==1.4.1" bddl easydict mujoco matplotlib wand scikit-image gym \ && cd /home/user_lerobot/libero-plus && uv pip install --no-cache --no-deps -e "." ENV PYTHONPATH="/home/user_lerobot/libero-plus:${PYTHONPATH}" # Pre-download libero assets so nothing is fetched at runtime (CI timeout risk). # libero/libero/__init__.py prompts with input() when ~/.libero/config.yaml is # missing; write the config first so any import is non-interactive. # LIBERO-plus installs with a nested package structure, so find_spec('libero') # may return a namespace package without .origin. Use glob to locate bddl_files. ENV LIBERO_PLUS_ROOT=/home/user_lerobot/libero-plus/libero/libero # LIBERO-plus's clone already ships both bddl_files/ and init_files/ with base # `.pruned_init` files for all 5 suites. Its Benchmark.get_task_init_states() # strips perturbation suffixes (_table_N, _tb_N, _view_, _language_, _light_) # back to the base filename, so no extra init files are needed. # # Assets are different: LIBERO-plus's bddl_base_domain.py resolves scene XMLs # via `os.path.join(DIR_PATH, "../assets")` (package-relative, ignoring # config.yaml), and its 2402 perturbation tasks reference table/view/light # textures that ship only in Sylvest/LIBERO-plus's `assets.zip` (6.4 GB). # Without this extract, every perturbation task fails on a FileNotFoundError # for scenes/tabletop_table_.xml. RUN python${PYTHON_VERSION} -c "\ from huggingface_hub import hf_hub_download; \ hf_hub_download(repo_id='Sylvest/LIBERO-plus', repo_type='dataset', \ filename='assets.zip', local_dir='/tmp/libero-plus-dl')" && \ unzip -q /tmp/libero-plus-dl/assets.zip -d ${LIBERO_PLUS_ROOT}/ && \ rm -rf /tmp/libero-plus-dl RUN mkdir -p /home/user_lerobot/.libero && \ printf "assets: ${LIBERO_PLUS_ROOT}/assets\nbddl_files: ${LIBERO_PLUS_ROOT}/bddl_files\ndatasets: ${LIBERO_PLUS_ROOT}/../datasets\ninit_states: ${LIBERO_PLUS_ROOT}/init_files\n" \ > /home/user_lerobot/.libero/config.yaml RUN chmod +x /lerobot/.venv/lib/python${PYTHON_VERSION}/site-packages/triton/backends/nvidia/bin/ptxas COPY --chown=user_lerobot:user_lerobot . . CMD ["/bin/bash"]