feat: remote worker GPU support with nllb-1.3B

- Update Dockerfile.remote-worker with CUDA support
- Update docker-compose.yml with GPU settings
- Update remote_translator_worker.py to use nllb-200-1.3B
- Update worker_ws.go with model URL handler
This commit is contained in:
lordpietre 2026-04-01 03:55:40 +00:00
parent 8093cc1e40
commit 98e0cd0e46
4 changed files with 65 additions and 26 deletions

View file

@ -1,23 +1,20 @@
FROM python:3.11-slim-bookworm
SHELL ["/bin/bash", "-c"]
RUN apt-get update && apt-get install -y --no-install-recommends \
patchelf libpq-dev gcc git curl wget bash \
patchelf libpq-dev gcc git curl wget \
&& rm -rf /var/lib/apt/lists/*
ENV PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
TOKENIZERS_PARALLELISM=false \
HF_HOME=/root/.cache/huggingface \
DEBIAN_FRONTEND=noninteractive
HF_HOME=/root/.cache/huggingface
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip
RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cpu
RUN pip install --no-cache-dir torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118
RUN pip install --no-cache-dir \
ctranslate2==3.24.0 \
@ -33,18 +30,10 @@ RUN find /usr/local/lib/python3.11/site-packages/ctranslate2* \
-name "libctranslate2-*.so.*" -o -name "libctranslate2.so*" | \
xargs -I {} patchelf --clear-execstack {} || true
COPY workers/ ./workers/
COPY init-db/ ./init-db/
COPY migrations/ ./migrations/
COPY workers/remote_translator_worker.py /app/worker.py
ENV DB_HOST=db
ENV DB_PORT=5432
ENV DB_NAME=rss
ENV DB_USER=rss
ENV DB_PASS=x
ENV CT2_DEVICE=cuda
ENV CT2_COMPUTE_TYPE=float16
ENV UNIVERSAL_MODEL=facebook/nllb-200-1.3B
ENV WORKER_SERVER=ws://localhost:8080/ws/worker
ENV CT2_DEVICE=cpu
ENV CT2_COMPUTE_TYPE=int8
CMD ["python", "-m", "workers.remote_translator_worker"]
CMD ["python", "/app/worker.py"]

View file

@ -294,4 +294,46 @@ func StartJobAssigner() {
}
}
}()
}
func GetModelDownloadURL(c *gin.Context) {
apiKey := c.Query("api_key")
if apiKey == "" {
apiKey = c.GetHeader("X-API-Key")
}
if apiKey == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "API key required"})
return
}
ctx := c.Request.Context()
var workerID int
err := db.GetPool().QueryRow(ctx, `
SELECT id FROM remote_workers WHERE api_key = $1
`, apiKey).Scan(&workerID)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid API key"})
return
}
var modelURL, modelPath string
db.GetPool().QueryRow(ctx, `
SELECT value FROM config WHERE key = 'remote_worker_model_url'
`).Scan(&modelURL)
db.GetPool().QueryRow(ctx, `
SELECT value FROM config WHERE key = 'remote_worker_model_path'
`).Scan(&modelPath)
if modelPath == "" {
modelPath = "/app/models/nllb-ct2"
}
c.JSON(http.StatusOK, gin.H{
"model_download_url": modelURL,
"model_path": modelPath,
})
}

View file

@ -324,10 +324,10 @@ services:
WORKER_NAME: ${WORKER_NAME:-remote-worker-1}
WORKER_API_KEY: ${WORKER_API_KEY:-}
WORKER_SERVER: ${WORKER_SERVER:-ws://backend-go:8080/ws/worker}
CT2_DEVICE: ${CT2_DEVICE:-cpu}
CT2_DEVICE: ${CT2_DEVICE:-cuda}
CT2_MODEL_PATH: /app/models/nllb-ct2
CT2_COMPUTE_TYPE: ${CT2_COMPUTE_TYPE:-int8}
UNIVERSAL_MODEL: facebook/nllb-200-distilled-600M
CT2_COMPUTE_TYPE: ${CT2_COMPUTE_TYPE:-float16}
UNIVERSAL_MODEL: facebook/nllb-200-1.3B
HF_HOME: /app/hf_cache
TZ: Europe/Madrid
volumes:
@ -340,8 +340,12 @@ services:
deploy:
resources:
limits:
cpus: '1'
memory: 2G
memory: 6G
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [ gpu ]
restart: unless-stopped
# ==================================================================================

View file

@ -5,6 +5,7 @@ import json
import logging
import re
import threading
import hashlib
from typing import List, Optional
import websocket
@ -25,7 +26,7 @@ WORKER_SERVER = os.environ.get("WORKER_SERVER", "ws://localhost:8080/ws/worker")
DEVICE = os.environ.get("CT2_DEVICE", "cpu")
MODEL_PATH = os.environ.get("CT2_MODEL_PATH", "/app/models/nllb-ct2")
COMPUTE_TYPE = os.environ.get("CT2_COMPUTE_TYPE", "int8")
UNIVERSAL_MODEL = os.environ.get("UNIVERSAL_MODEL", "facebook/nllb-200-distilled-600M")
UNIVERSAL_MODEL = os.environ.get("UNIVERSAL_MODEL", "facebook/nllb-200-1.3B")
LANG_CODE_MAP = {
"en": "eng_Latn", "es": "spa_Latn", "fr": "fra_Latn", "de": "deu_Latn",
@ -60,7 +61,8 @@ def ensure_model():
model_bin = os.path.join(MODEL_PATH, "model.bin")
if not os.path.exists(model_bin):
LOG.info(f"CTranslate2 model not found at {MODEL_PATH}, converting...")
LOG.info(f"CTranslate2 model not found at {MODEL_PATH}")
LOG.info("Downloading from HuggingFace...")
convert_model()
LOG.info(f"Loading CTranslate2 model from {MODEL_PATH} on {DEVICE}")
@ -82,6 +84,8 @@ def convert_model():
quantization = COMPUTE_TYPE if COMPUTE_TYPE != "auto" else "int8"
LOG.info(f"Converting {UNIVERSAL_MODEL} to CTranslate2 format...")
cmd = [
"ct2-transformers-converter",
"--model", UNIVERSAL_MODEL,