# syntax=docker/dockerfile:1.7

# MemPalace — GPU (NVIDIA CUDA) image.
#
# Multi-stage build using uv (the project ships a uv.lock, so we install from
# the frozen lockfile for reproducible images). The `gpu` extra pulls
# onnxruntime-gpu, which needs CUDA + cuDNN shared libraries at runtime, so
# this variant builds on an nvidia/cuda base instead of python:slim.
#
# The builder stage is dropped from the final image, so build tools
# (compilers, apt cache) never reach production — only the uv-managed
# interpreter and the resolved virtualenv do.
#
# Build:
#   docker build -f Dockerfile.gpu -t mempalace:gpu .
#
# Run (requires the NVIDIA Container Toolkit on the host):
#   docker run -i --rm --gpus all \
#     -e MEMPALACE_EMBEDDING_DEVICE=cuda \
#     -v mempalace-data:/data mempalace:gpu
#
# NOTE: onnxruntime-gpu ties itself to a CUDA major version. If embeddings
# fail to load on the GPU, align CUDA_IMAGE below with the CUDA release that
# the resolved onnxruntime-gpu wheel targets (see its release notes), then
# rebuild.

ARG CUDA_IMAGE=nvidia/cuda:12.6.3-cudnn-runtime-ubuntu22.04

# --- builder ----------------------------------------------------------------
FROM ${CUDA_IMAGE} AS builder

COPY --from=ghcr.io/astral-sh/uv:0.5 /uv /uvx /bin/

# Minimal toolchain for any source-built wheels; dropped before the runtime
# stage so compilers never ship in the production image.
RUN apt-get update \
    && apt-get install -y --no-install-recommends build-essential \
    && rm -rf /var/lib/apt/lists/*

ARG PYTHON_VERSION=3.12
ARG EXTRAS="extract,spellcheck,gpu"

ENV UV_COMPILE_BYTECODE=1 \
    UV_LINK_MODE=copy \
    UV_PYTHON_INSTALL_DIR=/opt/uv/python \
    UV_PYTHON_PREFERENCE=only-managed

WORKDIR /app

# Layer 1: dependencies only (no project source). Bind-mounted files keep the
# project tree out of this layer, so changing source code does not bust the
# deps cache. The uv-managed interpreter is also installed here, into a
# stable path that the runtime stage can copy verbatim.
RUN --mount=type=cache,target=/root/.cache/uv \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=README.md,target=README.md \
    set -e; \
    uv python install ${PYTHON_VERSION}; \
    flags=""; \
    for e in $(echo "${EXTRAS}" | tr ',' ' '); do flags="${flags} --extra ${e}"; done; \
    uv sync --frozen --no-install-project --no-dev --python ${PYTHON_VERSION} ${flags}

# Layer 2: the project itself. --no-editable installs mempalace into the
# venv's site-packages (instead of an .pth pointing at /app), so the runtime
# stage can copy only /app/.venv and drop the source tree.
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv \
    set -e; \
    flags=""; \
    for e in $(echo "${EXTRAS}" | tr ',' ' '); do flags="${flags} --extra ${e}"; done; \
    uv sync --frozen --no-dev --no-editable ${flags}

# --- runtime ----------------------------------------------------------------
FROM ${CUDA_IMAGE} AS runtime

LABEL org.opencontainers.image.title="MemPalace (GPU)" \
      org.opencontainers.image.description="Local-first AI memory with CUDA-accelerated embeddings." \
      org.opencontainers.image.source="https://github.com/MemPalace/mempalace" \
      org.opencontainers.image.licenses="MIT"

# ca-certificates only — needed for the lazy HuggingFace model download on
# first use. No build toolchain in this stage.
RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates \
    && rm -rf /var/lib/apt/lists/*

ENV HOME=/data \
    PATH="/app/.venv/bin:${PATH}" \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    MEMPALACE_EMBEDDING_DEVICE=cuda

# Non-root user owning the data volume.
RUN groupadd --gid 1000 mempalace \
    && useradd --uid 1000 --gid 1000 --home-dir /data --create-home mempalace

WORKDIR /app

# Bring the uv-managed interpreter and the resolved venv across the stage
# boundary. /opt/uv/python must be copied alongside .venv: the venv's
# shebangs and binary launcher reference it.
COPY --from=builder /opt/uv/python /opt/uv/python
COPY --from=builder --chown=mempalace:mempalace /app/.venv /app/.venv
COPY --chown=mempalace:mempalace docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

USER mempalace
VOLUME ["/data"]

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["mcp"]
