File size: 3,642 Bytes
ddb6059
1d423f0
 
 
 
 
ccfab90
4acdfaf
1d423f0
 
 
ccfab90
 
1d423f0
 
 
 
 
 
ccfab90
 
 
1d423f0
 
ddb6059
1d423f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# ── RAG Spectrum β€” public Space build file ──────────────────────────────────────────
# Generated by scripts/deploy_space.py; do not edit by hand. The source code is
# not in this repo: stage 0 pulls it from a private repo with the Space's
# HF_TOKEN secret, pinned to one snapshot. The stages after it are the
# project's own Dockerfile with every COPY reading from that snapshot.
# ─────────────────────────────────────────────────────────────────────────────
ARG RAG_SPECTRUM_CODE_REPO=APC15/prism-code
ARG RAG_SPECTRUM_REV=7aa8d746f692a09aacbda502737ad259468b4747

# ── Stage 0: private source ──
FROM alpine:3.20 AS source
ARG RAG_SPECTRUM_CODE_REPO
ARG RAG_SPECTRUM_REV
RUN apk add --no-cache git
# HF's git endpoint takes the token as a Basic-auth password (it rejects
# Bearer). A header keeps the token out of the URL and out of /src/.git.
RUN --mount=type=secret,id=HF_TOKEN,mode=0444,required=true \
    AUTH="$(printf 'hf:%s' "$(cat /run/secrets/HF_TOKEN)" | base64 | tr -d '\n')" \
    && git -c "http.extraHeader=Authorization: Basic ${AUTH}" \
        clone --quiet --depth 1 "https://huggingface.co/${RAG_SPECTRUM_CODE_REPO}" /src \
    && test "$(git -C /src rev-parse HEAD)" = "${RAG_SPECTRUM_REV}" \
    || { echo "Could not fetch ${RAG_SPECTRUM_CODE_REPO} at ${RAG_SPECTRUM_REV}: check that the HF_TOKEN secret can read it and that no newer snapshot was pushed after this build file." >&2; exit 1; }
RUN rm -rf /src/.git

# ── RAG Spectrum β€” single-container deployment (Hugging Face Spaces) ────────
# Stage 1 builds the React frontend; stage 2 runs FastAPI, which serves the
# built frontend as static files (see the SPA fallback at the end of api/main.py).
# HF Spaces expects the app on port 7860 and runs the container as uid 1000.
# ─────────────────────────────────────────────────────────────────────────────

# ── Stage 1: frontend build ──
FROM node:20-alpine AS frontend
WORKDIR /build
COPY --from=source /src/frontend/package*.json ./
RUN npm ci
COPY --from=source /src/frontend/ ./
RUN npm run build

# ── Stage 2: Python runtime ──
FROM python:3.13-slim
ENV PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1

# CPU-only torch first β€” the default PyPI wheel bundles CUDA (~3GB heavier).
RUN pip install torch --index-url https://download.pytorch.org/whl/cpu

WORKDIR /app
COPY --from=source /src/requirements.txt ./requirements.txt
COPY --from=source /src/api/requirements.txt ./api-requirements.txt
RUN pip install -r requirements.txt -r api-requirements.txt

COPY --from=source /src/ .
COPY --from=frontend /build/dist ./frontend/dist

# HF Spaces runs as uid 1000; give it a home + writable app dir
# (SQLite DB in api/db/, model caches, per-job scratch under /tmp).
RUN useradd -m -u 1000 user && chown -R user:user /app
USER user
ENV HOME=/home/user \
    HF_HOME=/tmp/hf_cache \
    PYTHONPATH=/app

WORKDIR /app/api
EXPOSE 7860
# --proxy-headers + trusted forwarded IPs so request.client.host resolves to the
# real client (HF's router fronts the container). Without this, rate limiting
# keys on the proxy IP and every visitor shares one bucket.
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", \
     "--proxy-headers", "--forwarded-allow-ips=*"]