File size: 9,253 Bytes
6a8a419
 
 
 
 
 
 
b00f3e3
3e5a997
b00f3e3
3e5a997
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285e72f
3e5a997
 
 
 
 
b00f3e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3e5a997
 
 
 
b00f3e3
 
3e5a997
b00f3e3
3e5a997
6a8a419
3e5a997
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
FROM oven/bun:1.3.10 AS frontend
WORKDIR /build
COPY frontend/package.json frontend/bun.lock ./
RUN bun install --frozen-lockfile
COPY frontend/ ./
RUN bun run build

# Build Flora's dependencies on CPU without credentials or Flora model weights.
# Verified upstream PyTorch image digest, CUDA 12.8 / Torch 2.11.0.
FROM pytorch/pytorch:2.11.0-cuda12.8-cudnn9-devel@sha256:53ab3de62f6101d1e42f9be28623ab7a468a24c070d632f211ed576e30b6abd3 AS flora-build
ENV DEBIAN_FRONTEND=noninteractive TORCH_CUDA_ARCH_LIST=9.0 MAX_JOBS=4
RUN apt-get update && apt-get install -y --no-install-recommends python3-venv \
    && rm -rf /var/lib/apt/lists/* \
    && python -m venv --system-site-packages /opt/flora-venv
ENV PATH=/opt/flora-venv/bin:$PATH
WORKDIR /workspace/app
COPY worker-requirements.txt flora-requirements.txt flora-acceleration-requirements.txt ./
# Keep the reference worker/native dependency overlay order.
RUN python -m pip install --no-cache-dir -r worker-requirements.txt
RUN python -m pip install --no-cache-dir -r flora-requirements.txt
COPY build_causal_conv1d.py ./
ENV MAX_JOBS=1
RUN python build_causal_conv1d.py
RUN python -m pip install --no-cache-dir --no-build-isolation -r flora-acceleration-requirements.txt
RUN python -c "import causal_conv1d; from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule"
COPY *.py *.txt prompt-templates.json pipeline-source.tar.gz pipeline-version.json ./
RUN python bootstrap.py
COPY requirements.txt ./
RUN python -m venv --system-site-packages /opt/studio-venv
RUN /opt/studio-venv/bin/python -m pip install --no-cache-dir -r requirements.txt
RUN /opt/flora-venv/bin/python -c "from importlib.metadata import version; assert version('huggingface_hub') == '1.33.0'; assert version('transformers') == '5.8.1'"


# Camera operators arrive as verified wheels; this image needs no CUDA compiler.
FROM pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime@sha256:eee11b3b3872a8c838e35ef48f08b2d5def2080902c7f666831310ca1a0ef2be AS hand-base
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends git ffmpeg libglib2.0-0 python3-venv \
    && rm -rf /var/lib/apt/lists/*

# Isolate the controller, camera and hand dependencies to retain their CUDA ABIs.
FROM hand-base AS hand-build
ENV UV_PYTHON_INSTALL_DIR=/opt/uv-python
RUN python -m pip install --break-system-packages --no-cache-dir uv==0.12.11 \
    && uv python install 3.10.20 \
    && uv venv --python 3.10.20 /opt/droid \
    && uv venv --python 3.10.20 /opt/egohand \
    && python -m venv --system-site-packages /opt/hand-controller
WORKDIR /opt/hand-worker
COPY hand-runtime/camera-requirements.txt hand-runtime/egohand-requirements.txt hand-runtime/worker-requirements.txt ./
RUN uv pip install --python /opt/droid/bin/python torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cu128
RUN uv pip install --python /opt/droid/bin/python -r /opt/hand-worker/camera-requirements.txt \
    && uv pip install --python /opt/droid/bin/python torch-scatter==2.1.2+pt28cu128 --find-links https://data.pyg.org/whl/torch-2.8.0+cu128.html \
    && uv pip install --python /opt/droid/bin/python git+https://github.com/microsoft/MoGe.git@74fbce054ebed49800de42d0ad0e83495065719a --no-deps
RUN uv pip install --python /opt/egohand/bin/python -r /opt/hand-worker/egohand-requirements.txt \
    && /opt/hand-controller/bin/python -m pip install --no-cache-dir -r /opt/hand-worker/worker-requirements.txt
RUN git clone https://github.com/NVlabs/GEM-X.git /opt/GEM-X \
    && git -C /opt/GEM-X checkout --detach 20555befe2fbbe5b2015e1fff7ff214dc18ea9d6 \
    && uv pip install --python /opt/egohand/bin/python --no-deps -e /opt/GEM-X
COPY hand-runtime/camera-adapter-source.tar.gz hand-runtime/camera-native-source.tar.gz /tmp/
RUN mkdir -p /opt/camera-adapter /opt/hand-camera \
    && tar -xzf /tmp/camera-adapter-source.tar.gz -C /opt/camera-adapter \
    && tar -xzf /tmp/camera-native-source.tar.gz -C /opt/hand-camera
COPY hand-runtime/camera-wheels /opt/hand-worker/camera-wheels
COPY hand-runtime/verify_camera_wheels.py hand-runtime/image-version.json /opt/hand-worker/
RUN python /opt/hand-worker/verify_camera_wheels.py /opt/hand-worker/camera-wheels /opt/hand-worker/image-version.json \
    && uv pip install --python /opt/droid/bin/python --no-deps /opt/hand-worker/camera-wheels/wheels/*.whl
# The frozen camera wrapper expects these source, checkpoint and plan aliases.
RUN for source in /opt/hand-camera/release-195fps-verified/DROID-W/*; do \
      name=$(basename "$source"); \
      if [ ! -e "/opt/hand-camera/DROID-on/$name" ]; then ln -s "$source" "/opt/hand-camera/DROID-on/$name"; fi; \
    done \
    && mkdir -p /opt/hand-camera/moge-joint-vio/weights \
    && ln -s /workspace/hand-assets/camera/moge-small.pt /opt/hand-camera/moge-joint-vio/weights/small.pt \
    && ln -s /workspace/hand-assets/camera /opt/hand-camera/release-195fps-verified/DROID-W/pretrained \
    && ln -s /opt/hand-camera/release-195fps-verified/DROID-W/pretrained /opt/hand-camera/DROID-on/pretrained \
    && ln -s /workspace/hand-cache/camera-artifacts /opt/hand-camera/speed-round24/artifacts
RUN rm -rf /opt/hand-camera/release-195fps-verified/DROID-W/build \
    /opt/hand-camera/release-195fps-verified/DROID-W/thirdparty/lietorch/build /tmp/*.tar.gz

FROM hand-build AS hand-models
COPY hand-runtime/model-assets/ /model-assets/
COPY hand-runtime/model-sources.json hand-runtime/build_model_assets.py hand-runtime/egohand-checkpoint-template.zip /opt/hand-worker/
RUN /opt/egohand/bin/python /opt/hand-worker/build_model_assets.py /model-assets /opt/hand-worker/model-sources.json

FROM hand-base AS hand-runtime
COPY --from=hand-build /opt/uv-python /opt/uv-python
COPY --from=hand-build /opt/droid /opt/droid
COPY --from=hand-build /opt/egohand /opt/egohand
COPY --from=hand-build /opt/hand-controller /opt/hand-controller
COPY --from=hand-build /opt/GEM-X /opt/GEM-X
COPY --from=hand-build /opt/camera-adapter /opt/camera-adapter
COPY --from=hand-build /opt/hand-camera /opt/hand-camera
COPY hand-runtime/image-version.json hand-runtime/health.py hand-runtime/verify_runtime_imports.py /opt/hand-worker/
COPY hand-runtime/camera-startup-source.tar.gz /tmp/
COPY hand-runtime/camera-startup-sources.json hand-runtime/install_startup_sources.py /opt/hand-worker/
RUN python /opt/hand-worker/install_startup_sources.py /tmp/camera-startup-source.tar.gz /opt/hand-worker/camera-startup-sources.json /opt/hand-worker/image-version.json /opt/hand-camera \
    && rm /tmp/camera-startup-source.tar.gz
ENV PATH=/opt/hand-controller/bin:$PATH PYTHONUNBUFFERED=1 HF_HUB_DISABLE_TELEMETRY=1 PYTHONDONTWRITEBYTECODE=1
ENV HOME=/home/user XDG_CACHE_HOME=/home/user/.cache
ENV PYTHONPATH=/opt/camera-adapter/src GDP_TRT_UPDATE=1
ENV DROID_SITE=/opt/droid/lib/python3.10/site-packages EGO_SITE=/opt/egohand/lib/python3.10/site-packages
ENV DROID_LIBRARY_PATH=${DROID_SITE}/tensorrt_libs:${DROID_SITE}/nvidia/cu12/lib:${DROID_SITE}/nvidia/cudnn/lib:${DROID_SITE}/nvidia/cuda_runtime/lib:${DROID_SITE}/nvidia/cublas/lib:${DROID_SITE}/nvidia/cufft/lib:${DROID_SITE}/nvidia/curand/lib:${DROID_SITE}/nvidia/cuda_nvrtc/lib
ENV EGO_LIBRARY_PATH=${EGO_SITE}/tensorrt_libs:${EGO_SITE}/nvidia/cudnn/lib:${EGO_SITE}/nvidia/cu13/lib:${DROID_SITE}/nvidia/cuda_runtime/lib:${DROID_SITE}/nvidia/cublas/lib:${DROID_SITE}/nvidia/cufft/lib:${DROID_SITE}/nvidia/curand/lib:${DROID_SITE}/nvidia/cuda_nvrtc/lib
RUN if ! getent passwd 1000 >/dev/null; then useradd -m -u 1000 user; fi \
    && mkdir -p /workspace /home/user/.cache && chown -R 1000:1000 /workspace /home/user
USER 1000
WORKDIR /workspace
COPY --from=hand-models --chown=1000:1000 /model-assets /workspace/hand-assets
COPY hand-runtime/model-sources.json hand-runtime/build_model_assets.py /opt/hand-worker/
RUN python /opt/hand-worker/build_model_assets.py /workspace/hand-assets /opt/hand-worker/model-sources.json --verify-only
RUN python /opt/hand-worker/verify_runtime_imports.py all

# One public image supplies the Studio UI, Flora Jobs and native hand Jobs.
FROM hand-runtime AS studio
COPY --from=flora-build /opt/flora-venv /opt/flora-venv
COPY --from=flora-build /opt/studio-venv /opt/studio-venv
COPY --from=flora-build --chown=1000:1000 /workspace/source /workspace/source
WORKDIR /workspace/app
ENV PATH=/opt/flora-venv/bin:$PATH
ENV PYTHONPATH=/workspace/source/src:/workspace/app PYTHONUNBUFFERED=1 HF_HUB_DISABLE_TELEMETRY=1
ENV FLORA_EXECUTION=accelerated FLORA_BATCH_SIZE=8 FLORA_ENABLE_INFERENCE=1
COPY --chown=1000:1000 *.py *.txt *.json pipeline-source.tar.gz Dockerfile README.md THIRD_PARTY.md VISUALIZER-LICENSE TRANSFORMERS-LICENSE /workspace/app/
COPY --from=frontend --chown=1000:1000 /build/dist /workspace/app/frontend/dist
RUN python -c "import worker, torch, causal_conv1d; from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule; assert torch.__version__.split('+')[0] == '2.11.0'"
RUN PYTHONPATH=/opt/camera-adapter/src /opt/hand-controller/bin/python /opt/hand-worker/verify_runtime_imports.py all
USER 1000
EXPOSE 7860
CMD ["/opt/studio-venv/bin/python", "-m", "uvicorn", "server:app", "--host", "0.0.0.0", "--port", "7860", "--no-access-log"]