# syntax=docker/dockerfile:1
# llm_monitor — production image (Podman/Docker compatible).
#
#   Stage 1 (python):  install the locked dependencies with uv (deps only — the app is
#                      a single module, there is nothing to build or install).
#   Stage 2 (runtime): slim, non-root, no build tools.
#
# There is deliberately NO frontend build stage: static/index.html is hand-rolled vanilla
# JS + inline SVG with no bundler and no CDN (the engines live on an internet-disconnected
# podman network, so a CDN asset would be a dead request and a blank chart). The file is
# copied verbatim and served as-is.
#
#   podman build -t localhost/llm-monitor:main -f Containerfile .
#   podman run -d --name llm-monitor -p 8090:8090 \
#     -e GUFO_BASE_URL=http://deskwork-ai:8731 \
#     -e VLLM_BASE_URL=http://driveripper-ai:8000 \
#     localhost/llm-monitor:main
#
# Both env vars are optional — unset means this deployment does not run that engine, the
# proxy answers 404 {"configured": false} without dialling, and the page drops the panel.
# Nothing is baked into the image: no host guesses, no keys.
#
# Both stages must stay on the SAME Python (3.14): stage 1 builds the venv that stage 2
# copies verbatim, so a mismatch would ship a venv the runtime interpreter cannot import.
# requires-python is >=3.10; 3.14 is what the box that runs this is on.

# ---------- Stage 1: python dependencies ----------
FROM docker.io/python:3.14-slim AS python
WORKDIR /app
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
ENV UV_LINK_MODE=copy \
    UV_PYTHON_DOWNLOADS=0 \
    UV_PROJECT_ENVIRONMENT=/app/.venv
# Lockfile first so dependency layers cache independently of the app source.
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-cache

# ---------- Stage 2: runtime ----------
FROM docker.io/python:3.14-slim AS runtime
ENV PATH="/app/.venv/bin:$PATH" \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1
RUN useradd --create-home --uid 10001 monitor
WORKDIR /app
COPY --from=python /app/.venv /app/.venv
COPY app.py ./app.py
COPY static ./static
RUN chown -R monitor:monitor /app
USER monitor
# The dashboard listens on 8090 on purpose: 8000 is vLLM's port in this fleet and 8731 is
# gufo's, so binding either would collide with an engine (or scrape itself).
EXPOSE 8090
ENV DASHBOARD_HOST=0.0.0.0 \
    DASHBOARD_PORT=8090
# /api/config answers 200 with no upstream configured and never dials an engine, so a dead
# gufo or vLLM can never make the dashboard container unhealthy.
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
  CMD ["python", "-c", "import sys, httpx; sys.exit(0 if httpx.get('http://127.0.0.1:8090/api/config', timeout=4).status_code == 200 else 1)"]
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8090"]
