| # CanLex MCP server -- remote (streamable-HTTP) image. | |
| # | |
| # Builds anywhere: Hugging Face Spaces, Google Cloud Run, Fly.io, plain Docker. | |
| # Retrieval is fully local and key-free; the optional CanLII case citator reads | |
| # its key from the CANLII_API_KEY environment variable (supplied as a host | |
| # secret -- the key is never copied into the image). | |
| FROM python:3.12-slim | |
| # libgomp1 is the OpenMP runtime that onnxruntime (the reranker) links against. | |
| RUN apt-get update \ | |
| && apt-get install -y --no-install-recommends libgomp1 \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # Run as a non-root user (UID 1000) -- required by Hugging Face Spaces. | |
| RUN useradd --create-home --home-dir /app --uid 1000 app | |
| WORKDIR /app | |
| # Python dependencies first, so this layer caches across code changes. | |
| COPY requirements.txt . | |
| RUN pip install --no-cache-dir -r requirements.txt | |
| # Application code, the processed corpus (section-chunk JSON), and the prebuilt | |
| # semantic embeddings (embeddings.npz, committed via git-LFS). Shipping the | |
| # embeddings as an artifact keeps the build fast: embedding ~20k chunks on the | |
| # build host took ~33 min and was tripping Hugging Face's job timeout as the | |
| # corpus grew. Regenerate with 'python -m canlex.embed' after any corpus change | |
| # and commit the result. | |
| COPY --chown=app:app canlex/ ./canlex/ | |
| COPY --chown=app:app data/processed/*.json ./data/processed/ | |
| COPY --chown=app:app data/processed/embeddings.npz ./data/processed/ | |
| USER app | |
| ENV HOME=/app \ | |
| HF_HOME=/app/.hf_cache \ | |
| CANLEX_HTTP=1 \ | |
| PORT=7860 \ | |
| PYTHONUNBUFFERED=1 | |
| # Pre-fetch the embedder and cross-encoder models so the cache is baked into the | |
| # image and the first request needs no network. The corpus is NOT re-embedded | |
| # here -- the committed embeddings.npz is used as-is; only the bge query model | |
| # (for embedding live queries) and the reranker are downloaded. | |
| RUN python -c "from canlex.embed import Embedder; Embedder()" \ | |
| && python -c "from canlex.rerank import Reranker; Reranker()" | |
| # From here on, model files are served from the baked cache, never fetched. | |
| ENV HF_HUB_OFFLINE=1 | |
| EXPOSE 7860 | |
| CMD ["python", "-m", "canlex.server"] | |