# ============================================================================= # Fragmenta — Hugging Face Spaces Dockerfile (CPU + GPU) # ============================================================================= # Deploy as a Docker Space on Hugging Face. # Works on both free CPU Spaces and paid GPU Spaces. # # CPU Spaces: torch.cuda.is_available() → False → runs on CPU # GPU Spaces: NVIDIA runtime injected by HF → torch.cuda.is_available() → True # # Port: 7860 (HF Spaces requirement) # ============================================================================= # --------------------------------------------------------------------------- # Stage 1: Build the React frontend # --------------------------------------------------------------------------- FROM node:20-slim AS frontend-builder WORKDIR /build/frontend COPY app/frontend/package.json app/frontend/package-lock.json* ./ RUN npm ci --prefer-offline --no-audit 2>/dev/null || npm install COPY app/frontend/ ./ RUN npm run build # --------------------------------------------------------------------------- # Stage 2: Python backend (CPU + GPU via bundled CUDA in PyTorch wheels) # --------------------------------------------------------------------------- FROM python:3.11-slim-bookworm ENV DEBIAN_FRONTEND=noninteractive # System deps (audio processing, networking) RUN apt-get update && apt-get install -y --no-install-recommends \ libsndfile1 \ ffmpeg \ git \ curl \ && apt-get clean && rm -rf /var/lib/apt/lists/* RUN pip install --no-cache-dir --root-user-action=ignore --upgrade pip setuptools wheel WORKDIR /app # --------------------------------------------------------------------------- # Python dependencies # --------------------------------------------------------------------------- COPY requirements.txt . # Install all requirements in one pass. Strip: # - PyQt6 / pyqt6-webengine: native desktop UI, not used in the web container. # - flash-attn: optional CUDA-only kernel, no wheel for this base image. # - extra-index-url: not needed, default PyPI ships the right wheels. # - pycairo / pygobject: require libcairo + C compiler absent from slim image. RUN grep -ivE 'flash-attn|extra-index-url|pycairo|pygobject|pywebview' requirements.txt > requirements_docker.txt \ && pip install --no-cache-dir --root-user-action=ignore -r requirements_docker.txt \ && rm requirements_docker.txt # --------------------------------------------------------------------------- # Application code # --------------------------------------------------------------------------- COPY . . COPY --from=frontend-builder /build/frontend/build ./app/frontend/build # Install stable-audio-tools in-tree RUN pip install --no-cache-dir --root-user-action=ignore -e ./vendor/stable-audio-tools/ # Create writable directories RUN mkdir -p /app/models/pretrained \ /app/models/fine_tuned \ /app/models/config \ /app/data \ /app/output \ /app/logs \ /app/config # Fetch pretrained weights from MazCodes/fragmenta-models. # Uses huggingface_hub (already a dep) instead of git clone so we don't need # git-lfs and so failures are loud rather than silent LFS-pointer stubs. RUN python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='MazCodes/fragmenta-models', local_dir='/app/models/pretrained', local_dir_use_symlinks=False)" # Fail the build if either weight file is suspiciously small (e.g. an LFS # pointer or a partial download). Real files are >1 GB. RUN test "$(stat -c%s /app/models/pretrained/stable-audio-open-small-model.safetensors)" -gt 1000000000 \ && test "$(stat -c%s /app/models/pretrained/stable-audio-open-model.safetensors)" -gt 1000000000 # Save default model configs (restored at runtime if dirs are empty) RUN mkdir -p /opt/fragmenta-defaults/models/config \ && if [ -d /app/models/config ] && ls /app/models/config/*.json 1>/dev/null 2>&1; then \ cp /app/models/config/*.json /opt/fragmenta-defaults/models/config/; \ fi # --------------------------------------------------------------------------- # HF Spaces runs as user "user" (uid 1000) — ensure write permissions # --------------------------------------------------------------------------- COPY docker/docker-entrypoint.sh /docker-entrypoint.sh RUN chmod +x /docker-entrypoint.sh RUN useradd -m -u 1000 user || true RUN chown -R 1000:1000 /app /opt/fragmenta-defaults USER user # --------------------------------------------------------------------------- # Configuration — HF Spaces requires port 7860 # --------------------------------------------------------------------------- ENV FLASK_HOST=0.0.0.0 ENV FLASK_PORT=7860 ENV FRAGMENTA_LOG_LEVEL=INFO ENV FRAGMENTA_DOCKER=1 ENV FRAGMENTA_USE_CUSTOM_MODELS=true ENV HOME=/home/user ENV PATH="/home/user/.local/bin:${PATH}" ENV OMP_NUM_THREADS=4 EXPOSE 7860 ENTRYPOINT ["/docker-entrypoint.sh"]