@@ -4,13 +4,18 @@ FROM pytorch/pytorch:2.8.0-cuda12.8-cudnn9-devel AS builder
44ENV DEBIAN_FRONTEND=noninteractive
55ENV CMAKE_ARGS="-DGGML_CUDA=on"
66ENV FORCE_CMAKE=1
7+ # Cap flash-attn's parallel nvcc jobs so the build doesn't exhaust RAM on small
8+ # CI runners (each job needs a few GB). Override with --build-arg MAX_JOBS=N.
9+ ARG MAX_JOBS=4
10+ ENV MAX_JOBS=${MAX_JOBS}
711
812RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
913
1014COPY requirements.txt /tmp/requirements.txt
1115RUN pip install --no-cache-dir -r /tmp/requirements.txt && \
1216 pip install --no-deps git+https://github.com/liampetti/Qwen3-TTS-streaming.git@97da215 && \
13- pip install flash-attn --no-build-isolation
17+ pip install flash-attn --no-build-isolation && \
18+ python -m spacy download en_core_web_sm
1419
1520# Stage 2: Runtime (no CUDA compilers/headers)
1621FROM pytorch/pytorch:2.8.0-cuda12.8-cudnn9-runtime
@@ -20,7 +25,10 @@ ENV DEBIAN_FRONTEND=noninteractive
2025WORKDIR /app
2126
2227ENV HF_HOME=/app/data/models
23- ENV HF_HUB_OFFLINE=1
28+ # HF_HUB_OFFLINE is managed at runtime by app.py (online for the first-run
29+ # wizard download, offline once models are cached) — not pinned here.
30+ # Image variant — the wizard offers all tiers on the GPU image.
31+ ENV FULLOCH_VARIANT=gpu
2432
2533# Install runtime system dependencies
2634# - gcc: needed by Triton JIT for TTS kernels
@@ -33,6 +41,7 @@ RUN apt-get update && apt-get install -y \
3341 libsox-dev \
3442 libsox-fmt-all \
3543 ffmpeg \
44+ espeak-ng \
3645 libportaudio2 \
3746 libasound2-plugins \
3847 libpulse0 \
@@ -52,12 +61,21 @@ RUN useradd -m -u 1000 -s /bin/bash appuser
5261USER appuser
5362
5463# Copy application code
55- COPY --chown=appuser:appuser app.py fulloch.png ./
64+ COPY --chown=appuser:appuser app.py fulloch.png parloch.png ./
5665COPY --chown=appuser:appuser core/ core/
5766COPY --chown=appuser:appuser tools/ tools/
5867COPY --chown=appuser:appuser utils/ utils/
5968COPY --chown=appuser:appuser audio/ audio/
6069COPY --chown=appuser:appuser server/ server/
6170COPY --chown=appuser:appuser wav/ wav/
6271
72+ # First-run seeds (copied into the empty ./data volume by core/bootstrap.py):
73+ # the config template the wizard fills in, and the app's own GBNF grammar (which
74+ # the wizard's downloader can't fetch). Weights stay OUT of the image — the
75+ # wizard pulls them on first run.
76+ COPY --chown=appuser:appuser data/config.example.yml /app/seed/config.example.yml
77+ COPY --chown=appuser:appuser data/models/grammars/agent.gbnf /app/seed/grammars/agent.gbnf
78+
79+ # Bootstrap + setup-or-run is handled in app.main() (core/bootstrap.py): an empty
80+ # ./data boots into the setup wizard, a populated one straight to the assistant.
6381CMD ["python" , "app.py" ]
0 commit comments