Takosaga commited on
Commit
6db472d
Β·
1 Parent(s): 236b10b

docs: add Docker/HF Spaces deployment plan and design spec

Browse files
docs/superpowers/plans/2026-06-14-docker-hf-spaces-deployment.md ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Docker / HF Spaces Deployment Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Containerize EuropaLex for zero-cost deployment on Hugging Face Spaces with CPU-only inference and baked-in model weights.
6
+
7
+ **Architecture:** A single-stage Dockerfile downloads all four AI models at build time (using a HF token secret), installs CPU-only dependencies, and launches Gradio on port 7860. The existing `_auto_download_models()` in `app.py` already skips download when `.gguf` files exist, so no code changes are needed β€” the runtime simply finds the pre-baked models and starts instantly.
8
+
9
+ **Tech Stack:** Docker (python:3.12-slim base), CPU-only PyTorch, llama-cpp-python (CPU backend), Hugging Face Hub CLI for model download.
10
+
11
+ ---
12
+
13
+ ### Task 1: Archive Current README
14
+
15
+ **Files:**
16
+ - Create: `docs/europalex-readme-old.md`
17
+ - Modify: `README.md` (replaced in Task 2)
18
+
19
+ - [ ] **Step 1: Copy current README to archive location**
20
+
21
+ Run:
22
+ ```bash
23
+ cp README.md docs/europalex-readme-old.md
24
+ ```
25
+
26
+ This preserves the current development-focused README before replacing it with the deployment-focused version.
27
+
28
+ - [ ] **Step 2: Verify the archive**
29
+
30
+ Run:
31
+ ```bash
32
+ diff <(head -5 README.md) <(head -5 docs/europalex-readme-old.md)
33
+ # Should show no differences
34
+ ls -la docs/europalex-readme-old.md
35
+ ```
36
+
37
+ - [ ] **Step 3: Commit**
38
+
39
+ ```bash
40
+ git add docs/europalex-readme-old.md
41
+ git commit -m "docs: archive current README before Docker deployment rewrite"
42
+ ```
43
+
44
+ ---
45
+
46
+ ### Task 2: Create New Deployment-Focused README
47
+
48
+ **Files:**
49
+ - Modify: `README.md`
50
+
51
+ Replace the entire file with:
52
+
53
+ ```markdown
54
+ # EuropaLex β€” Docker / Hugging Face Spaces Deployment
55
+
56
+ AI-powered flashcard generator for European languages, deployed as a Docker container on [Hugging Face Spaces](https://huggingface.co/spaces). All four AI models are baked into the image at build time β€” the app starts instantly with zero wait.
57
+
58
+ > **CPU-only inference:** All inference runs on CPU. Expect slower performance (30+ seconds per sentence for translation, longer for TTS/images) in exchange for free hosting.
59
+
60
+ ## Quick Start: Deploy to HF Spaces
61
+
62
+ 1. **Create a new Space** on Hugging Face β†’ select **Docker** as the framework
63
+ 2. **Upload these files** from this repository: `Dockerfile`, `.dockerignore`, and all project files (`app.py`, `core/`, `frontend/`, `models/`, `export/`, `configs/`)
64
+ 3. **Set secret** `HUGGING_FACE_HUB_TOKEN` in your Space settings (under **Secrets**) β€” this authenticates model download during the Docker build step
65
+ 4. **Push to repo** β€” Hugging Face automatically builds the image and deploys
66
+
67
+ That's it. The first build takes ~5–15 minutes (downloading ~26 GB of models). Subsequent visits start instantly.
68
+
69
+ ## What Gets Deployed
70
+
71
+ | Model | Role | Size | Runtime |
72
+ |---|---|---|---|
73
+ | MiniCPM5-1B Q8_0 | English text generation (Phase 1) | ~1.1 GB | llama-cpp-python |
74
+ | tiny-aya-water Q4_K_M | Translation (Phase 2) | ~2.1 GB | llama-cpp-python |
75
+ | OmniVoice Q8_0 | Text-to-speech audio | ~950 MB | omnivoice.cpp |
76
+ | FLUX.2-klein 4B Q4_K_M | Image generation | ~2.6 GB | diffusers |
77
+
78
+ **Total image size:** ~20–30 GB (models baked in at build time)
79
+
80
+ ## How It Works
81
+
82
+ ```
83
+ Docker build:
84
+ python:3.12-slim β†’ pip install CPU deps β†’ huggingface-cli login (build secret) β†’ download all models β†’ CMD ["python", "app.py"]
85
+
86
+ HF Spaces runtime:
87
+ Container starts β†’ _auto_download_models() finds GGUF files β†’ skips download β†’ launches Gradio on :7860
88
+ ```
89
+
90
+ The Dockerfile downloads all models during `docker build` using your HF token as a build secret. At runtime, the app detects pre-existing model files and skips download entirely β€” no authentication needed, no waiting.
91
+
92
+ ## CPU Performance Expectations
93
+
94
+ | Operation | Expected Time |
95
+ |---|---|
96
+ | Phase 1: Generate 3 English sentences | ~30–60 seconds |
97
+ | Phase 2: Translate 3 sentences (tiny-aya) | ~1–3 minutes |
98
+ | Phase 2: TTS audio per sentence | ~5–15 seconds |
99
+ | Phase 2: Image generation per card | ~30–60+ seconds |
100
+
101
+ These are approximate and depend on the HF Spaces CPU tier. All features remain functional β€” just slower than a GPU setup.
102
+
103
+ ## Local Docker Testing (Optional)
104
+
105
+ Build and test locally before deploying:
106
+
107
+ ```bash
108
+ # Build the image (requires your HF token)
109
+ docker build \
110
+ --secret id=hf_token,env=HUGGING_FACE_HUB_TOKEN \
111
+ -t europalex .
112
+
113
+ # Run locally (port 7860)
114
+ docker run -p 7860:7860 europalex
115
+ ```
116
+
117
+ The container serves Gradio on `http://localhost:7860`. Press Ctrl+C to stop.
118
+
119
+ ## Architecture
120
+
121
+ EuropaLex uses a two-phase generation workflow:
122
+
123
+ 1. **Phase 1** β€” Enter a scenario, select CEFR level (A0–C2), set batch size β†’ MiniCPM5-1B generates English sentences
124
+ 2. **Phase 2** β€” Select target language, toggle Audio/Images β†’ tiny-aya translates, OmniVoice generates TTS, FLUX generates illustrations
125
+
126
+ Cards export as Anki `.apkg` files or zipped CSV folders with flat media files.
127
+
128
+ ## Repository Structure
129
+
130
+ ```
131
+ EuropaLex/
132
+ β”œβ”€β”€ Dockerfile # Single-stage build: deps + model download + Gradio launch
133
+ β”œβ”€β”€ .dockerignore # Exclude .venv, .git, models from build context
134
+ β”œβ”€β”€ README.md # This file β€” HF Spaces deployment guide
135
+ β”œβ”€β”€ app.py # Entry point β€” Gradio UI wiring, two-phase generation handlers
136
+ β”œβ”€β”€ pyproject.toml # Project config (uv)
137
+ β”œβ”€β”€ requirements.txt # pip install dependencies
138
+ β”œβ”€β”€ configs/settings.yaml # App settings, model paths, batch defaults
139
+ β”œβ”€β”€ core/ # Business logic
140
+ β”‚ β”œβ”€β”€ types.py # Pydantic models: CardData, CEFRLevel, TextResult, etc.
141
+ β”‚ β”œβ”€β”€ engine.py # MiniCPMTextEngine, LlamaCppTextEngine, EnginePool
142
+ β”‚ β”œβ”€β”€ audio_gen.py # TTSEngine (OmniVoice)
143
+ β”‚ β”œβ”€β”€ image_gen.py # ImageGenEngine (diffusers Flux2KleinPipeline)
144
+ β”‚ β”œβ”€β”€ text_gen.py # Sentence extraction + generation with retry loop
145
+ β”‚ └── pipeline.py # Phase 2 translation orchestration
146
+ β”œβ”€β”€ frontend/ # Gradio 6 UI
147
+ β”‚ β”œβ”€β”€ ui/
148
+ β”‚ β”‚ β”œβ”€β”€ widgets.py # Styled toggle checkbox wrappers, Blocks builder
149
+ β”‚ β”‚ └── cards.py # Card rendering, gallery layout, progress bar
150
+ β”‚ └── css/custom.css # Plain-white theme, card styling, disabled states
151
+ β”œβ”€β”€ models/
152
+ β”‚ └── download_models.py # HF Hub model downloader (runtime fallback)
153
+ β”œβ”€β”€ export/ # Export formats
154
+ β”‚ β”œβ”€β”€ apkg_export.py # Anki .apkg export via genanki
155
+ β”‚ β”œβ”€β”€ csv_export.py # CSV zip export with flat media files
156
+ β”‚ └── anki_tunnel.py # MCP tunnel sync for live Anki import
157
+ β”œβ”€β”€ docs/ # Design specs and implementation plans
158
+ β”‚ └── superpowers/ # Planning documents
159
+ └── tests/ # Test suite (pytest)
160
+ ```
161
+
162
+ ## CEFR Levels
163
+
164
+ `[A0, A1, A2, B1, B2, C1, C2]`
165
+
166
+ - **A0:** Uses curated common words list (no text generation model needed)
167
+ - **A1–C2:** MiniCPM5-1B generates English sentences at the selected level in Phase 1; tiny-aya-water translates them in Phase 2
168
+ ```
169
+
170
+ - [ ] **Step 2: Write the new README**
171
+
172
+ Write the content above to `README.md`, replacing all existing content.
173
+
174
+ - [ ] **Step 3: Verify**
175
+
176
+ Run:
177
+ ```bash
178
+ head -5 README.md
179
+ # Should show "# EuropaLex β€” Docker / HF Spaces Deployment"
180
+ grep -c "Dockerfile\|HF Spaces\|HUGGING_FACE_HUB_TOKEN" README.md
181
+ # Should be > 0 for each term
182
+ ```
183
+
184
+ - [ ] **Step 4: Commit**
185
+
186
+ ```bash
187
+ git add README.md docs/europalex-readme-old.md
188
+ git commit -m "docs: rewrite README for Docker/HF Spaces deployment"
189
+ ```
190
+
191
+ ---
192
+
193
+ ### Task 3: Create the Dockerfile
194
+
195
+ **Files:**
196
+ - Create: `Dockerfile`
197
+
198
+ Create `Dockerfile` with these exact contents:
199
+
200
+ ```dockerfile
201
+ # EuropaLex β€” Docker / Hugging Face Spaces Deployment
202
+ # Single-stage build: CPU-only deps + model download + Gradio launch
203
+ #
204
+ # Build:
205
+ # docker build --secret id=hf_token,env=HUGGING_FACE_HUB_TOKEN -t europalex .
206
+ #
207
+ # Run (local test):
208
+ # docker run -p 7860:7860 europalex
209
+
210
+ FROM python:3.12-slim
211
+
212
+ # ─── System dependencies ───────────────────────────────────────────────
213
+ # git for huggingface-cli, build-essential for llama-cpp-python compilation
214
+ RUN apt-get update && apt-get install -y --no-install-recommends \
215
+ git \
216
+ build-essential \
217
+ && rm -rf /var/lib/apt/lists/*
218
+
219
+ # ─── Build secret: Hugging Face token ──────────────────────────────────
220
+ # Mounted at /run/secrets/hf_token during docker build.
221
+ # Used to authenticate huggingface-cli download of gated models.
222
+ RUN --mount=type=secret,id=hf_token \
223
+ if [ -f /run/secrets/hf_token ]; then \
224
+ echo "Authenticated as $(huggingface-cli whoami --token $(cat /run/secrets/hf_token) 2>/dev/null || echo 'unknown')"; \
225
+ else \
226
+ echo "WARNING: No HUGGING_FACE_HUB_TOKEN secret provided. Model download will fail for gated models."; \
227
+ fi
228
+
229
+ # ─── CPU-only PyTorch ──────────────────────────────────────────────────
230
+ # Install from the official CPU wheel index to avoid ~2 GB of CUDA deps.
231
+ RUN pip install --no-cache-dir \
232
+ --extra-index-url https://download.pytorch.org/whl/cpu \
233
+ torch>=2.1.0
234
+
235
+ # ─── llama-cpp-python (CPU backend, no CUDA flags) ─────────────────────
236
+ # Build with default CPU backend β€” no CUDA/MKL flags needed on slim base.
237
+ RUN pip install --no-cache-dir \
238
+ llama-cpp-python>=0.3.28
239
+
240
+ # ─── Other Python dependencies ─────────────────────���───────────────────
241
+ COPY requirements.txt .
242
+ RUN pip install --no-cache-dir -r requirements.txt
243
+
244
+ # ─── Copy project source ───────────────────────────────────────────────
245
+ COPY . /app
246
+ WORKDIR /app
247
+
248
+ # ─── Download models at build time ─────────────────────────────────────
249
+ # Uses the HF token secret for authenticated downloads.
250
+ # Models are baked into the image (~26 GB total).
251
+ RUN --mount=type=secret,id=hf_token \
252
+ if [ -f /run/secrets/hf_token ]; then \
253
+ export HUGGING_FACE_HUB_TOKEN=$(cat /run/secrets/hf_token); \
254
+ fi && \
255
+ python -m models.download_models
256
+
257
+ # ─── Launch ────────────────────────────────────────────────────────────
258
+ EXPOSE 7860
259
+ CMD ["python", "app.py"]
260
+ ```
261
+
262
+ - [ ] **Step 1: Write the Dockerfile**
263
+
264
+ Write the content above to `Dockerfile`.
265
+
266
+ - [ ] **Step 2: Verify syntax**
267
+
268
+ Run:
269
+ ```bash
270
+ # Check Dockerfile syntax (requires docker)
271
+ docker build --check -f Dockerfile . 2>&1 || echo "Docker not available β€” manual review required"
272
+ ```
273
+
274
+ If Docker is not available, do a manual review:
275
+ - Verify `FROM python:3.12-slim` is the first instruction
276
+ - Verify `--mount=type=secret,id=hf_token` appears in both the auth check and model download steps
277
+ - Verify CPU-only PyTorch uses `--extra-index-url https://download.pytorch.org/whl/cpu`
278
+ - Verify `EXPOSE 7860` and `CMD ["python", "app.py"]` are at the end
279
+
280
+ - [ ] **Step 3: Commit**
281
+
282
+ ```bash
283
+ git add Dockerfile
284
+ git commit -m "feat: add Dockerfile for HF Spaces deployment (CPU-only)"
285
+ ```
286
+
287
+ ---
288
+
289
+ ### Task 4: Create `.dockerignore`
290
+
291
+ **Files:**
292
+ - Create: `.dockerignore`
293
+
294
+ Create `.dockerignore` with these exact contents:
295
+
296
+ ```dockerignore
297
+ # ─── Version control ───────────────────────────────────────────────────
298
+ .git/
299
+ .gitignore
300
+
301
+ # ─── Virtual environment (not needed β€” Docker installs deps) ───────────
302
+ .venv/
303
+ venv/
304
+
305
+ # ─── Python cache ──────────────────────────────────────────────────────
306
+ __pycache__/
307
+ *.pyc
308
+ *.pyo
309
+ .pytest_cache/
310
+
311
+ # ─── Model weights β€” downloaded inside Dockerfile, NOT from build context
312
+ .local/models/
313
+
314
+ # ─── Gradio cache ─────────────────────────────────────────────────────
315
+ .gradio/
316
+
317
+ # ─── OS files ──────────────────────────────────────────────────────────
318
+ .DS_Store
319
+ Thumbs.db
320
+
321
+ # ─── Test output files (from tests/test_outputs/) ──────────────────────
322
+ tests/test_outputs/
323
+
324
+ # ─── Lock file β€” not needed inside container (pip install handles it) ──
325
+ uv.lock
326
+
327
+ # ─── Docs / plans (not needed at runtime) ──────────────────────────────
328
+ docs/
329
+
330
+ # ─── Agent conventions (not needed at runtime) ─────────────────────────
331
+ AGENTS.md
332
+ ```
333
+
334
+ - [ ] **Step 1: Write the .dockerignore**
335
+
336
+ Write the content above to `.dockerignore`.
337
+
338
+ - [ ] **Step 2: Verify it excludes key paths**
339
+
340
+ Run:
341
+ ```bash
342
+ grep -c "local/models" .dockerignore # Should be 1
343
+ grep -c ".venv" .dockerignore # Should be >= 1
344
+ grep -c "__pycache__" .dockerignore # Should be 1
345
+ grep -c "docs/" .dockerignore # Should be 1
346
+ ```
347
+
348
+ - [ ] **Step 3: Commit**
349
+
350
+ ```bash
351
+ git add .dockerignore
352
+ git commit -m "feat: add .dockerignore for HF Spaces deployment"
353
+ ```
354
+
355
+ ---
356
+
357
+ ### Task 5: Final Verification
358
+
359
+ **Files:**
360
+ - All created files above
361
+
362
+ - [ ] **Step 1: Verify all files exist and are non-empty**
363
+
364
+ Run:
365
+ ```bash
366
+ for f in Dockerfile .dockerignore README.md docs/europalex-readme-old.md; do
367
+ echo "$f: $(wc -l < $f) lines, $(wc -c < $f) bytes"
368
+ done
369
+ ```
370
+
371
+ Expected output:
372
+ - `Dockerfile`: ~70 lines
373
+ - `.dockerignore`: ~35 lines
374
+ - `README.md`: ~120 lines (new deployment-focused version)
375
+ - `docs/europalex-readme-old.md`: ~180 lines (archived original)
376
+
377
+ - [ ] **Step 2: Verify the Dockerfile references correct paths**
378
+
379
+ Run:
380
+ ```bash
381
+ grep "models.download_models" Dockerfile # Should find the download command
382
+ grep "requirements.txt" Dockerfile # Should find COPY + pip install
383
+ grep "EXPOSE 7860" Dockerfile # Should find port exposure
384
+ grep 'CMD \["python"' Dockerfile # Should find launch command
385
+ ```
386
+
387
+ - [ ] **Step 3: Verify .dockerignore does NOT exclude files needed at runtime**
388
+
389
+ Run:
390
+ ```bash
391
+ # These should NOT appear in .dockerignore (they are needed):
392
+ grep "app.py" .dockerignore && echo "FAIL: app.py excluded" || echo "OK: app.py not excluded"
393
+ grep "core/" .dockerignore && echo "FAIL: core/ excluded" || echo "OK: core/ not excluded"
394
+ grep "frontend/" .dockerignore && echo "FAIL: frontend/ excluded" || echo "OK: frontend/ not excluded"
395
+ ```
396
+
397
+ - [ ] **Step 4: Final commit (if any uncommitted changes remain)**
398
+
399
+ ```bash
400
+ git status
401
+ git add -A
402
+ git diff --cached --stat
403
+ git commit -m "feat: Docker/HF Spaces deployment β€” Dockerfile, .dockerignore, README rewrite"
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Self-Review Checklist
409
+
410
+ 1. **Spec coverage:** All four spec requirements are addressed β€” `Dockerfile` (Task 3), `.dockerignore` (Task 4), `README.md` replacement (Task 2), archived README (Task 1). No code changes needed because `_auto_download_models()` already skips download when GGUF files exist.
411
+
412
+ 2. **Placeholder scan:** No placeholders found. All file contents are complete with exact text.
413
+
414
+ 3. **Type consistency:** N/A β€” no code changes, only infrastructure and documentation files.
415
+
416
+ 4. **Dockerfile correctness:**
417
+ - `python:3.12-slim` base βœ“
418
+ - Build-essential for llama-cpp-python compilation βœ“
419
+ - CPU-only PyTorch via `--extra-index-url https://download.pytorch.org/whl/cpu` βœ“
420
+ - `llama-cpp-python>=0.3.28` without CUDA flags βœ“
421
+ - HF token build secret (`--mount=type=secret,id=hf_token`) βœ“
422
+ - Model download via `python -m models.download_models` at build time βœ“
423
+ - `EXPOSE 7860` and `CMD ["python", "app.py"]` βœ“
424
+
425
+ 5. **`.dockerignore` correctness:**
426
+ - Excludes `.local/models/` (models downloaded inside Dockerfile) βœ“
427
+ - Excludes `.venv/`, `__pycache__/`, test outputs, docs βœ“
428
+ - Does NOT exclude `core/`, `frontend/`, `app.py`, `configs/` βœ“
docs/superpowers/specs/2026-06-14-europalex-docker-hf-spaces-deployment-design.md ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EuropaLex β€” Docker / Hugging Face Spaces Deployment
2
+
3
+ **Date:** 2026-06-14
4
+ **Status:** Approved
5
+ **Scope:** Containerize EuropaLex for deployment on Hugging Face Spaces (CPU tier)
6
+
7
+ ## Overview
8
+
9
+ EuropaLex is a Gradio-based flashcard generator that uses four local AI models (MiniCPM5-1B, tiny-aya-water, OmniVoice, FLUX.2-klein). This design specifies how to deploy it as a Docker container on Hugging Face Spaces with CPU-only inference, accepting slower performance in exchange for zero-cost hosting.
10
+
11
+ ## Design Decisions
12
+
13
+ ### 1. Keep All Four Models
14
+ All four models are retained: MiniCPM5-1B (text generation), tiny-aya-water (translation), OmniVoice (TTS), FLUX.2-klein (image generation). CPU inference will be slow β€” expect 30+ seconds per sentence for translation and longer for TTS/image generation β€” but all features remain functional.
15
+
16
+ ### 2. Model Download at Docker Build Time
17
+ Models are downloaded during the `docker build` step, not at runtime. The resulting image is ~20–30 GB but the app starts instantly when deployed to HF Spaces. This avoids timeout issues with HF Spaces' build limits and means users don't wait minutes on first visit.
18
+
19
+ ### 3. Single-Stage Dockerfile
20
+ A single `Dockerfile` handles everything: base image, dependency installation, model download, and launch. No multi-stage complexity. HF Spaces handles large images without issue.
21
+
22
+ ### 4. Build-Time Hugging Face Token
23
+ The `HUGGING_FACE_HUB_TOKEN` is passed as a Docker build secret (`--secret id=hf_token`). This authenticates the `huggingface-cli download` step during build time so models are baked into the image. No runtime authentication is needed.
24
+
25
+ ### 5. CPU-Only PyTorch
26
+ PyTorch is installed from the official CPU wheel index (`https://download.pytorch.org/whl/cpu`) to avoid pulling in ~2 GB of CUDA dependencies. llama-cpp-python is built with the default CPU backend (no CUDA flags). diffusers and omnivoice fall back to CPU automatically when no GPU is available.
27
+
28
+ ## Files to Create / Modify
29
+
30
+ | File | Action | Purpose |
31
+ |---|---|---|
32
+ | `Dockerfile` | **Create** | Single-stage build: deps + model download + Gradio launch |
33
+ | `.dockerignore` | **Create** | Exclude `.venv/`, `.git/`, `__pycache__/`, test outputs, large binaries. Explicitly exclude `.local/models/` from build context (models downloaded inside Dockerfile). |
34
+ | `README.md` | **Replace** | New README focused on HF Spaces deployment instructions |
35
+ | `docs/europalex-readme-old.md` | **Create** | Archive of current README for reference |
36
+
37
+ ## Data Flow
38
+
39
+ ```
40
+ Docker build:
41
+ python:3.12-slim β†’ pip install deps β†’ huggingface-cli login (build secret) β†’ download models β†’ CMD ["python", "app.py"]
42
+
43
+ HF Spaces runtime:
44
+ Container starts β†’ _auto_download_models() checks .local/models/ β†’ finds GGUF files β†’ skips download β†’ launches Gradio on :7860
45
+ ```
46
+
47
+ ## Error Handling
48
+
49
+ - **Model download failure at build time:** Docker build fails with a clear error. User knows immediately, not after deploying to HF Spaces.
50
+ - **Missing HF token:** Build fails with `huggingface-cli` auth error. Documented in README as a known setup step.
51
+ - **CPU OOM at runtime:** If RAM is insufficient, PyTorch/llama-cpp-python will fail gracefully. `app.py` already has try/except around engine initialization that surfaces errors to the Gradio UI.
52
+
53
+ ## HF Spaces Configuration (Documented in New README)
54
+
55
+ User workflow:
56
+ 1. Create a new Hugging Face Space β†’ select **Docker** framework
57
+ 2. Upload `Dockerfile`, `.dockerignore`, and project files
58
+ 3. Set secret `HUGGING_FACE_HUB_TOKEN` in Space settings (under Secrets)
59
+ 4. Push to repo β€” HF builds the image automatically
60
+
61
+ ## Constraints
62
+
63
+ - **CPU performance:** All inference runs on CPU. Translation of a batch of 3 sentences may take 1–3 minutes. TTS and image generation are slower still. This is acceptable for a free-tier demo.
64
+ - **Image size:** ~20–30 GB due to model weights baked in. HF Spaces handles this, but the initial build takes longer (5–15 minutes).
65
+ - **No GPU acceleration:** FLUX.2-klein and OmniVoice are designed for GPU. CPU inference will be very slow but functional.