Beemer Claude Opus 4.7 commited on
Commit
e1efaab
·
1 Parent(s): 874ccdf

Ship prebuilt embeddings; stop embedding at Docker build time (fixes build timeout)

Browse files

The Dockerfile ran 'python -m canlex.embed' at image build, embedding the whole
corpus on the build host. As the corpus grew to ~20k chunks that step reached
~33 minutes and began tripping Hugging Face Spaces' job timeout -- the build
finished and pushed the image, but the job was marked failed and the container
torn down, taking the server down.

Fix: commit data/processed/embeddings.npz as a git-LFS artifact (it is
deterministic and ~34 MB) and COPY it into the image; the build now only
pre-fetches the bge query model and the reranker (a few minutes) instead of
re-embedding. embeddings.npz un-gitignored, LFS-tracked, and removed from
.dockerignore. Verified the committed vectors' ids match the committed corpus
exactly (20,381).

Workflow change: after any corpus edit, run 'python -m canlex.embed' and commit
the regenerated embeddings.npz (it no longer rebuilds itself in the image).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Files changed (5) hide show
  1. .dockerignore +1 -4
  2. .gitattributes +1 -0
  3. .gitignore +0 -1
  4. Dockerfile +12 -4
  5. data/processed/embeddings.npz +3 -0
.dockerignore CHANGED
@@ -6,12 +6,9 @@ __pycache__/
6
  **/__pycache__/
7
  *.pyc
8
 
9
- # Raw downloads are not needed -- only data/processed/*.json is.
10
  data/raw/
11
 
12
- # Regenerated inside the image by 'python -m canlex.embed'.
13
- data/processed/embeddings.npz
14
-
15
  # Local-only: the CanLII key (injected as a runtime secret) and disk caches.
16
  canlii_key.txt
17
  data/citator_dbmap.json
 
6
  **/__pycache__/
7
  *.pyc
8
 
9
+ # Raw downloads are not needed -- only data/processed/ is.
10
  data/raw/
11
 
 
 
 
12
  # Local-only: the CanLII key (injected as a runtime secret) and disk caches.
13
  canlii_key.txt
14
  data/citator_dbmap.json
.gitattributes CHANGED
@@ -1 +1,2 @@
1
  data/processed/caselaw.json filter=lfs diff=lfs merge=lfs -text
 
 
1
  data/processed/caselaw.json filter=lfs diff=lfs merge=lfs -text
2
+ data/processed/embeddings.npz filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -3,6 +3,5 @@ __pycache__/
3
  .venv/
4
  venv/
5
  data/raw/
6
- data/processed/embeddings.npz
7
  canlii_key.txt
8
  data/citator_*.json
 
3
  .venv/
4
  venv/
5
  data/raw/
 
6
  canlii_key.txt
7
  data/citator_*.json
Dockerfile CHANGED
@@ -19,9 +19,15 @@ WORKDIR /app
19
  COPY requirements.txt .
20
  RUN pip install --no-cache-dir -r requirements.txt
21
 
22
- # Application code and the processed corpus (section-chunk JSON).
 
 
 
 
 
23
  COPY --chown=app:app canlex/ ./canlex/
24
  COPY --chown=app:app data/processed/*.json ./data/processed/
 
25
 
26
  USER app
27
  ENV HOME=/app \
@@ -30,9 +36,11 @@ ENV HOME=/app \
30
  PORT=7860 \
31
  PYTHONUNBUFFERED=1
32
 
33
- # Build the semantic embeddings and pre-fetch the cross-encoder model, so the
34
- # model cache is baked into the image and the first request needs no network.
35
- RUN python -m canlex.embed \
 
 
36
  && python -c "from canlex.rerank import Reranker; Reranker()"
37
 
38
  # From here on, model files are served from the baked cache, never fetched.
 
19
  COPY requirements.txt .
20
  RUN pip install --no-cache-dir -r requirements.txt
21
 
22
+ # Application code, the processed corpus (section-chunk JSON), and the prebuilt
23
+ # semantic embeddings (embeddings.npz, committed via git-LFS). Shipping the
24
+ # embeddings as an artifact keeps the build fast: embedding ~20k chunks on the
25
+ # build host took ~33 min and was tripping Hugging Face's job timeout as the
26
+ # corpus grew. Regenerate with 'python -m canlex.embed' after any corpus change
27
+ # and commit the result.
28
  COPY --chown=app:app canlex/ ./canlex/
29
  COPY --chown=app:app data/processed/*.json ./data/processed/
30
+ COPY --chown=app:app data/processed/embeddings.npz ./data/processed/
31
 
32
  USER app
33
  ENV HOME=/app \
 
36
  PORT=7860 \
37
  PYTHONUNBUFFERED=1
38
 
39
+ # Pre-fetch the embedder and cross-encoder models so the cache is baked into the
40
+ # image and the first request needs no network. The corpus is NOT re-embedded
41
+ # here -- the committed embeddings.npz is used as-is; only the bge query model
42
+ # (for embedding live queries) and the reranker are downloaded.
43
+ RUN python -c "from canlex.embed import Embedder; Embedder()" \
44
  && python -c "from canlex.rerank import Reranker; Reranker()"
45
 
46
  # From here on, model files are served from the baked cache, never fetched.
data/processed/embeddings.npz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c1e6554d83a8a3a37d77a0f0e032e311ceb3187d2bb742170c05549e2c4d65f7
3
+ size 36126270