# ============================================================ # SmallClaw Voice – Dockerfile.voice # ============================================================ # Voice-enabled build with ffmpeg, whisper.cpp (STT), and Piper (TTS). # Use this when deploying with voice support on a capable server. # # Build: # docker build -f Dockerfile.voice -t smallclaw-voice . # # Run: # docker run -d --name smallclaw-voice \ # -p 18789:18789 \ # -v smallclaw_data:/data \ # -v smallclaw_workspace:/data/workspace \ # -v ~/.localclaw:/root/.localclaw \ # -e OLLAMA_HOST=http://host.docker.internal:11434 \ # smallclaw-voice # # Or with docker compose (see docker-compose.voice.yml): # docker compose -f docker-compose.voice.yml up -d # ============================================================ # ── Stage 1: Builder ──────────────────────────────────────── FROM node:20-slim AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY tsconfig.json ./ COPY src/ ./src/ RUN npm run build # ── Stage 2: Runtime ──────────────────────────────────────── FROM node:20-slim AS runtime # System deps: Playwright/Chromium + Tesseract OCR + ffmpeg (for voice audio conversion) RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ wget \ ffmpeg \ fonts-liberation \ libatk-bridge2.0-0 \ libatk1.0-0 \ libcairo2 \ libcups2 \ libdbus-1-3 \ libdrm2 \ libexpat1 \ libgbm1 \ libglib2.0-0 \ libgtk-3-0 \ libnspr4 \ libnss3 \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libx11-6 \ libx11-xcb1 \ libxcb1 \ libxcomposite1 \ libxdamage1 \ libxext6 \ libxfixes3 \ libxrandr2 \ libxrender1 \ libxss1 \ libxtst6 \ xdg-utils \ tesseract-ocr \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Production deps only COPY package.json package-lock.json ./ RUN npm ci --omit=dev # Install Playwright browser binaries RUN npx playwright install chromium --with-deps 2>/dev/null || true # ── Voice: Piper TTS ──────────────────────────────────────── # Install Piper binary + shared libs + Korean voice model RUN ARCH=$(dpkg --print-architecture) && \ if [ "$ARCH" = "amd64" ]; then PIPER_ARCH="x86_64"; \ elif [ "$ARCH" = "arm64" ]; then PIPER_ARCH="aarch64"; \ elif [ "$ARCH" = "armhf" ]; then PIPER_ARCH="armv7l"; \ else PIPER_ARCH="$ARCH"; fi && \ cd /tmp && \ curl -L "https://github.com/rhasspy/piper/releases/download/2023.11.14-2/piper_linux_${PIPER_ARCH}.tar.gz" -o piper.tar.gz && \ tar xzf piper.tar.gz && \ cp piper/piper /usr/local/bin/piper && \ chmod +x /usr/local/bin/piper && \ cp piper/libespeak-ng.so* /usr/local/lib/ && \ cp piper/libonnxruntime.so* /usr/local/lib/ && \ cp piper/libpiper_phonemize.so* /usr/local/lib/ && \ cp piper/espeak-ng /usr/local/lib/ && \ cp -r piper/espeak-ng-data /usr/local/lib/ && \ ldconfig && \ rm -rf piper piper.tar.gz && \ mkdir -p /usr/local/share/piper-voices && \ curl -L "https://huggingface.co/neurlang/piper-onnx-kss-korean/resolve/main/piper-kss-korean.onnx" \ -o /usr/local/share/piper-voices/ko_KR-kss-medium.onnx && \ curl -L "https://huggingface.co/neurlang/piper-onnx-kss-korean/raw/main/piper-kss-korean.onnx.json" \ -o /usr/local/share/piper-voices/ko_KR-kss-medium.onnx.json # ── Voice: whisper.cpp (STT) ──────────────────────────────── # Build from source (no pre-built Linux binary available) RUN apt-get update && apt-get install -y --no-install-recommends cmake git build-essential && \ cd /tmp && \ git clone https://github.com/ggml-org/whisper.cpp.git && \ cd whisper.cpp && \ cmake -B build && \ cmake --build build --config Release -j$(nproc) && \ cp build/bin/whisper-cli /usr/local/bin/whisper-cli && \ rm -rf /tmp/whisper.cpp && \ apt-get remove -y git build-essential && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* && \ mkdir -p /usr/local/share/whisper-models && \ curl -L "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin" \ -o /usr/local/share/whisper-models/ggml-base.bin # ── Voice: espeak-ng-data symlink ──────────────────────────── # Piper expects espeak-ng-data at /usr/share/espeak-ng-data RUN ln -sf /usr/local/lib/espeak-ng-data /usr/share/espeak-ng-data # ── Voice: Telegram DNS fix ─────────────────────────────────── # Some networks block 149.154.166.110; use working IP instead RUN echo "149.154.167.220 api.telegram.org" >> /etc/hosts # ── App ────────────────────────────────────────────────────── COPY --from=builder /app/dist ./dist # Static web UI COPY web-ui/ ./web-ui/ # Data directories (overridden by volumes in compose) RUN mkdir -p /data/workspace /data/logs /root/.localclaw # ── Environment defaults ────────────────────────────────────── ENV NODE_ENV=production \ DOCKER_CONTAINER=true \ SMALLCLAW_DATA_DIR=/data \ SMALLCLAW_WORKSPACE_DIR=/data/workspace \ GATEWAY_PORT=18789 \ GATEWAY_HOST=0.0.0.0 \ PLAYWRIGHT_BROWSERS_PATH=/root/.cache/ms-playwright \ \ SMALLCLAW_PROVIDER=ollama \ \ OLLAMA_HOST=http://ollama:11434 \ \ LM_STUDIO_ENDPOINT=http://host.docker.internal:1234 \ LM_STUDIO_API_KEY="" \ LM_STUDIO_MODEL="" \ \ LLAMA_CPP_ENDPOINT=http://host.docker.internal:8080 \ LLAMA_CPP_MODEL="" \ \ OPENAI_API_KEY="" \ OPENAI_MODEL=gpt-4o \ \ CODEX_MODEL=gpt-5.3-codex \ \ PIPER_MODEL_PATH=/usr/local/share/piper-voices/ko_KR-kss-medium.onnx \ PIPER_CONFIG_PATH=/usr/local/share/piper-voices/ko_KR-kss-medium.onnx.json \ WHISPER_MODEL_PATH=/usr/local/share/whisper-models/ggml-base.bin EXPOSE 18789 HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ CMD curl -f http://localhost:18789/health || exit 1 CMD ["node", "dist/cli/index.js", "gateway"]