# ============================================================= # Multi-stage Docker build for Contextual Similarity Engine # Single container: React frontend + FastAPI backend # Deploys to: HuggingFace Spaces (Docker SDK), local, Railway # ============================================================= # Stage 1: Build frontend FROM node:22-slim AS frontend-build WORKDIR /app/frontend COPY frontend/package.json frontend/package-lock.json ./ RUN npm ci COPY frontend/ ./ RUN npm run build && node inline-assets.cjs # Stage 2: Python runtime FROM python:3.12-slim AS runtime # Create non-root user (required by HF Spaces) RUN useradd -m -u 1000 appuser WORKDIR /app # System deps for faiss-cpu and torch RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ && rm -rf /var/lib/apt/lists/* # Install uv for fast dependency resolution COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv # Copy dependency files first (cache layer) COPY --chown=appuser pyproject.toml uv.lock ./ # Install Python dependencies RUN uv sync --frozen --no-dev # Copy backend source COPY --chown=appuser *.py ./ # Copy pre-built frontend COPY --chown=appuser --from=frontend-build /app/frontend/dist ./frontend/dist # Data directories (HF cache, gensim/GloVe cache, engine state, trained models) RUN mkdir -p /data/huggingface /data/gensim-data /data/engine_state /data/w2v_state /data/trained_model \ && chown -R appuser:appuser /app /data ENV HF_HOME=/data/huggingface ENV TRANSFORMERS_CACHE=/data/huggingface ENV ENGINE_STATE_DIR=/data/engine_state ENV W2V_STATE_DIR=/data/w2v_state # GloVe background model cache for anomaly detection — keep it under /data so it # sits on persistent storage and isn't re-downloaded (~128MB) on every cold start. ENV GENSIM_DATA_DIR=/data/gensim-data # Override the background model (e.g. glove-wiki-gigaword-50) without code changes. ENV BACKGROUND_MODEL=glove-wiki-gigaword-100 # Bake the background model into the image so anomaly detection works offline, # with no runtime download or cold-start wait (adds ~128MB to the image). # return_path=True just ensures the download/cache without loading it into RAM. RUN uv run python -c "import os, gensim.downloader as d; d.load(os.environ['BACKGROUND_MODEL'], return_path=True)" \ && chown -R appuser:appuser /data/gensim-data # Switch to non-root user USER appuser # Expose port (HF Spaces expects 7860, override via PORT env) EXPOSE 7860 # Run the server — HOST and PORT configurable via env ENV HOST=0.0.0.0 ENV PORT=7860 CMD ["uv", "run", "python", "server.py"]