# SIOP V2 — image du service IA (siop2-ai, ADR-004). # Contexte de build : apps/ai (le service est autonome, pas de dépendance au # monorepo). Étage 1 : uv sync + téléchargement du modèle ONNX AU BUILD # (ADR-004 §1 — jamais au démarrage) ; étage 2 : runtime minimal non-root. # Ce service n'est JAMAIS exposé publiquement : seul siop2-api le contacte, # porteur du secret AI_SERVICE_TOKEN (ADR-004 §4). FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim AS builder WORKDIR /app ENV UV_LINK_MODE=copy \ FASTEMBED_CACHE_PATH=/opt/fastembed \ HF_HOME=/opt/whisper # Manifestes d'abord (cache de couche), puis le code. L'installation du projet # reste éditable (.pth → /app/src) : src est donc copié dans l'image finale. COPY pyproject.toml uv.lock ./ RUN uv sync --frozen --no-install-project --no-dev \ --extra embeddings --extra generation --extra transcription COPY src src RUN uv sync --frozen --no-dev \ --extra embeddings --extra generation --extra transcription # Les modèles locaux (embeddings + dictée) sont EMBARQUÉS dans l'image : pas # de téléchargement au boot (démarrage prévisible, marche sans accès à # Hugging Face en production). La dictée reste opt-in (AI_TRANSCRIPTION=off # par défaut) — le modèle est prêt si elle est activée un jour, sans rebuild. RUN uv run python -c "from siop_ai.embeddings import EmbeddeurLocal; EmbeddeurLocal()" RUN uv run python -c "from siop_ai.transcription import TranscripteurLocal; TranscripteurLocal('small')" FROM python:3.11-slim-bookworm WORKDIR /app ENV PATH=/app/.venv/bin:$PATH \ FASTEMBED_CACHE_PATH=/opt/fastembed \ HF_HOME=/opt/whisper RUN useradd --system --create-home siop COPY --from=builder --chown=siop:siop /app/.venv /app/.venv COPY --from=builder --chown=siop:siop /app/src /app/src COPY --from=builder --chown=siop:siop /opt/fastembed /opt/fastembed COPY --from=builder --chown=siop:siop /opt/whisper /opt/whisper USER siop EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \ CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/healthz', timeout=4).status==200 else 1)" CMD ["uvicorn", "siop_ai.app:app", "--host", "0.0.0.0", "--port", "8000"]