* fix(install): make the torch pin reach the Colab and Docker installs (#1357) #1358 pinned the trio in `[tool.uv] constraint-dependencies`, which fixes `uv sync` / `uv lock` / `uv run`. It does nothing for `uv pip install` -- that is the pip-compatible interface and ignores project-level uv settings -- and `uv pip install --system --no-cache .` is exactly what both the Colab notebook and deploy/Dockerfile run. Measured on one Python 3.12 environment, same command, pin present: without --constraint: torch 2.13.0 torchaudio 2.11.0 torchvision 0.28.0 with --constraint: torch 2.8.0 torchaudio 2.8.0 torchvision 0.23.0 So the reported install path was still resolving the three on their bare lower bounds (`torch>=2.4`, `torchvision>=0.19`), free to move torch past a torchvision built for an older ABI -- which is the reported failure, `operator torchvision::nms does not exist`, against the preinstalled torchvision in Colab's /usr/local/lib/python3.12/dist-packages/. The pins move to deploy/torch-constraints.txt and are passed explicitly at both call sites. No local version segment, so PEP 440 matches the base images' +cu128 and +rocm6.4 builds instead of replacing them -- the property the ROCm image depends on. Also extends the Docker guard, which asserted on torch and torchaudio only, omitting the one package that actually broke. It now imports torchvision.ops and touches nms, so an ABI mismatch fails the build rather than shipping. Recurrence: docker.yml builds only on push to main, never on a PR, so nothing would have caught a silent regression here before it shipped. tests/test_torch_constraints_are_applied.py fails if the file drifts from pyproject, if either call site drops --constraint, if the Dockerfile stops COPYing the file, or if the guard stops covering torchvision. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(install): assert the constraint in the argv, not the cell text (#1357) CodeRabbit on #1369, both findings valid. The notebook check scanned the whole cell, so it passed when --constraint was deleted from the run([...]) list but its explanatory comment survived -- exactly the "the pin looks present but does not apply" shape this PR exists to fix. It now parses the cell with ast and asserts --constraint is in the argument list AND immediately followed by the constraints file. Verified by deleting the flag from the argv while keeping the comment: the test fails. Also drops test_the_notebook_is_still_valid_json_and_has_its_cells -- it passed before the change and duplicated JSON parsing the constraint test already does. A tautology. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: debpalash <nizam4103@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
126 lines
5.7 KiB
Docker
126 lines
5.7 KiB
Docker
# Base image for the Python/PyTorch runtime stage. The default builds the
|
|
# CUDA variant; CI also builds a ROCm/AMD variant (issue #1165) by overriding:
|
|
# BASE_IMAGE=rocm/pytorch:rocm7.2.4_ubuntu24.04_py3.12_pytorch_release_2.8.0
|
|
# GPU_FLAVOR=rocm
|
|
# Both bases ship torch/torchaudio 2.8.0 preinstalled; the dependency install
|
|
# below deliberately preserves them (see the GPU_FLAVOR guard).
|
|
ARG BASE_IMAGE=pytorch/pytorch:2.8.0-cuda12.8-cudnn9-runtime
|
|
|
|
# ==========================================
|
|
# Builder Stage: Compile React Frontend
|
|
# ==========================================
|
|
FROM oven/bun:1-alpine AS frontend-builder
|
|
WORKDIR /app
|
|
|
|
# Monorepo — bun workspace with lockfile at repo root. Copy manifests first
|
|
# so `bun install` caches independently of source edits.
|
|
COPY package.json bun.lock ./
|
|
COPY frontend/package.json ./frontend/
|
|
|
|
RUN bun install --frozen-lockfile
|
|
|
|
# Build static files (output lands in /app/frontend/dist)
|
|
COPY frontend/ ./frontend/
|
|
RUN bun run --cwd frontend build
|
|
|
|
# ==========================================
|
|
# Runtime Stage: Python & PyTorch Backend
|
|
# ==========================================
|
|
FROM ${BASE_IMAGE} AS runtime
|
|
WORKDIR /app
|
|
|
|
# Enable unbuffered logs and optimizations
|
|
ENV PYTHONDONTWRITEBYTECODE=1
|
|
ENV PYTHONUNBUFFERED=1
|
|
ENV UV_SYSTEM_PYTHON=1
|
|
ENV HF_HOME=/app/omnivoice_data/huggingface
|
|
# Allow bare imports (from core.config, from services.*, etc.) when
|
|
# uvicorn is started as `backend.main:app` from WORKDIR /app.
|
|
ENV PYTHONPATH=/app/backend
|
|
# Headless server deployment: relax the desktop-only loopback origin gate.
|
|
# Docker's network NAT rewrites the client host to the bridge gateway, so the
|
|
# gate would otherwise 403 the operator out of /system/* and /api/settings/*
|
|
# ("Loopback origin required", issue #261). Exposure is governed by the
|
|
# operator's `-p` port mapping plus the optional share PIN. Desktop builds
|
|
# never set this, so their loopback boundary is unchanged.
|
|
ENV OMNIVOICE_SERVER_MODE=1
|
|
|
|
# Install system dependencies (FFmpeg is critical for torchaudio/scene splitting)
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
build-essential \
|
|
ffmpeg \
|
|
libsndfile1 \
|
|
curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# PEP 668: the ROCm base (Ubuntu 24.04) marks its system Python
|
|
# EXTERNALLY-MANAGED, which would refuse `pip install` / `uv pip install
|
|
# --system`. Inside a single-purpose container image installing into the
|
|
# base env is exactly what we want. No-ops on the conda-based CUDA image.
|
|
ENV PIP_BREAK_SYSTEM_PACKAGES=1
|
|
ENV UV_BREAK_SYSTEM_PACKAGES=1
|
|
|
|
# Install `uv` for blazing-fast reliable pip resolution
|
|
# (`python3 -m pip` — not every base symlinks a bare `pip` onto PATH)
|
|
RUN python3 -m pip install --no-cache-dir uv
|
|
|
|
# Copy python packaging specs (README.md required by hatchling metadata)
|
|
COPY pyproject.toml uv.lock README.md ./
|
|
COPY deploy/torch-constraints.txt ./deploy/torch-constraints.txt
|
|
|
|
# Install the project (non-editable — no need for -e in containers).
|
|
# Uses `uv` for exponentially faster resolution than plain pip.
|
|
#
|
|
# NOTE: `uv pip install` (without --upgrade) keeps already-installed packages
|
|
# that satisfy the requirements, so the base image's GPU-built torch/torchaudio
|
|
# (2.8.0, satisfying our `torch>=2.4`) survive this step instead of being
|
|
# clobbered by PyPI's CUDA-default wheels. That property is what makes the
|
|
# ROCm variant possible at all — the guard below pins it down.
|
|
#
|
|
# --constraint because `uv pip install` ignores `[tool.uv]
|
|
# constraint-dependencies` (that is a project-API setting), so without it the
|
|
# trio resolves on its bare lower bounds and torch may move while torchvision
|
|
# stays put — an ABI mismatch at import (#1357). The pins carry no local
|
|
# segment, so they match the base image's +cu128 / +rocm6.4 builds rather than
|
|
# replacing them.
|
|
RUN uv pip install --system --no-cache --constraint deploy/torch-constraints.txt .
|
|
|
|
# Guard (fails the build, not the user at runtime): assert the dependency
|
|
# install did NOT replace the base image's GPU torch. A future dep bump that
|
|
# forces a different torch version would otherwise silently ship a CUDA build
|
|
# in the ROCm image (= CPU-only for AMD users) — catch it here instead.
|
|
ARG GPU_FLAVOR=cuda
|
|
RUN python3 -c "import os, torch, torchaudio, torchvision; \
|
|
flavor = os.environ['GPU_FLAVOR']; \
|
|
accel = torch.version.hip if flavor == 'rocm' else torch.version.cuda; \
|
|
print(f'torch={torch.__version__} torchaudio={torchaudio.__version__} torchvision={torchvision.__version__} {flavor}={accel}'); \
|
|
assert accel, f'base image {flavor} torch was clobbered (now {torch.__version__})'; \
|
|
import torchvision.ops; torchvision.ops.nms; \
|
|
print('torchvision C++ ops resolve against this torch')"
|
|
|
|
# Copy application source
|
|
COPY backend/ ./backend/
|
|
COPY omnivoice/ ./omnivoice/
|
|
# Alembic config so schema migrations run natively on existing volumes
|
|
# (without it the backend fell back to the additive-column self-heal —
|
|
# functional, but the real migration chain is the first-class path).
|
|
COPY alembic.ini ./
|
|
|
|
# Copy the pre-built React frontend from the builder stage
|
|
COPY --from=frontend-builder /app/frontend/dist ./frontend/dist
|
|
|
|
# Expose the single unified API and UI port
|
|
EXPOSE 3900
|
|
|
|
# Image-level health probe (compose files define their own; this covers plain
|
|
# `docker run`). Generous start period: first boot creates the venv-less
|
|
# schema + may pull model metadata before /health answers.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=120s --retries=5 \
|
|
CMD curl -fsS http://127.0.0.1:3900/health || exit 1
|
|
|
|
# Mount points for persistent data (sqlite db, user voices, huggingface cache)
|
|
VOLUME ["/app/omnivoice_data"]
|
|
|
|
# Bind to 0.0.0.0 for external access
|
|
ENTRYPOINT ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "3900"]
|