diff --git a/Dockerfile b/Dockerfile index 7a8edee..318a190 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,6 +31,20 @@ RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/loki ./cmd/loki # limité à cette étape de build. RUN GOBIN=/out GOTOOLCHAIN=auto go install golang.org/x/tools/gopls@latest +# ── Étape 1 bis : whisper-cli (dictée vocale) ─────────────────────────── +# whisper.cpp compilé CPU seul, en statique : un binaire unique, aucune .so à +# trimballer, et pas de compilation CUDA (des minutes de build pour un gain +# nul sur des dictées de quelques secondes). Ubuntu 22.04 : glibc plus +# ancienne que l'image runtime, donc compatible quoi qu'elle embarque. +FROM ubuntu:22.04 AS whisperbuild +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential cmake git ca-certificates \ + && rm -rf /var/lib/apt/lists/* +RUN git clone --depth 1 https://github.com/ggml-org/whisper.cpp /w \ + && cmake -S /w -B /w/build -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF -DWHISPER_BUILD_TESTS=OFF \ + && cmake --build /w/build -j --target whisper-cli + # ── Étape 2 : runtime sur l'image serveur CUDA officielle ─────────────── FROM ${LLAMACPP_IMAGE} AS runtime @@ -84,6 +98,9 @@ RUN if [ "$PLAYWRIGHT" = "1" ]; then \ COPY --from=gobuild /out/loki /usr/local/bin/loki COPY --from=gobuild /out/gopls /usr/local/bin/gopls +# Dictée vocale (POST /api/transcribe). Le modèle (~190 Mo) n'est PAS dans +# l'image : téléchargé au premier usage dans /data/whisper/. +COPY --from=whisperbuild /w/build/bin/whisper-cli /usr/local/bin/whisper-cli COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh && mkdir -p /data /models @@ -92,6 +109,7 @@ ENV LOKI_CONTAINER=1 \ LOKI_HOME=/data \ LOKI_MODEL_DIRS=/models \ LOKI_ENGINE_BIN=/app/llama-server \ + LOKI_WHISPER_BIN=/usr/local/bin/whisper-cli \ LD_LIBRARY_PATH=/app \ NVIDIA_VISIBLE_DEVICES=all \ NVIDIA_DRIVER_CAPABILITIES=compute,utility diff --git a/README.md b/README.md index 91b4952..e2c6d07 100644 --- a/README.md +++ b/README.md @@ -327,6 +327,12 @@ Ajoutées par ce fork : - **Nom du modèle sur chaque réponse** : une pastille à côté de « Loki » dit quel modèle a produit la réponse. Elle est journalisée avec le tour : elle survit au rechargement, et un vieux tour garde le modèle de l'époque. +- **Dictée vocale** : un bouton micro dans la carte de saisie enregistre, + transcrit **en local** (whisper.cpp, compilé dans l'image ; modèle + `small-q5_1` multilingue ~190 Mo téléchargé au premier usage dans + `/data/whisper/`) et pose le texte dans le champ. ⚠️ le navigateur n'autorise + le micro qu'en **HTTPS** (ou sur `localhost`) — derrière un reverse proxy + TLS, rien à faire ; en `http://IP:8090`, le bouton l'explique. - **Cartes raisonnement/outils à hauteur bornée** : un long raisonnement ne fait plus grandir la page de plusieurs écrans — la carte reste à taille fixe et défile toute seule pendant la génération. Sur un raisonnement géant, seul diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9aa152c..1dbe935 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -32,6 +32,13 @@ entier à chaque token, en O(n²)). La carte reste maintenant à taille fixe et suit la génération en défilant toute seule ; en direct, seul le bas d'un bloc géant est re-rendu, et le texte complet est posé en fin de bloc. +## Dictée vocale + +Un bouton micro dans la carte de saisie : tu parles, le texte s'écrit. La +transcription est 100 % locale — whisper.cpp compilé dans l'image, modèle +multilingue téléchargé au premier usage (~190 Mo, dans `/data/whisper/`). +Le navigateur exige HTTPS (ou localhost) pour donner accès au micro. + ## Divers - Jeton Hugging Face réglable dans l'interface (dépôts verrouillés : le diff --git a/internal/loki/ui/index.html b/internal/loki/ui/index.html index eb577bb..6491eb5 100644 --- a/internal/loki/ui/index.html +++ b/internal/loki/ui/index.html @@ -2059,6 +2059,12 @@ html[data-files="1"] #files-btn{color:var(--accent)} .rolebadge{display:inline-block;margin-left:6px;padding:0 6px;border:1px solid var(--accent);border-radius:3px;color:var(--accent);font-size:9px;letter-spacing:.05em;text-transform:uppercase;vertical-align:middle} +/* Dictée : anneau rouge pulsé pendant l'enregistrement, estompé pendant la + transcription. Le bouton garde le gabarit de ses voisins (joindre, fichiers). */ +#mic.rec{color:var(--err);border-color:var(--err);animation:micpulse 1.2s ease-in-out infinite} +@keyframes micpulse{50%{box-shadow:0 0 0 5px color-mix(in srgb,var(--err) 25%,transparent)}} +#mic.busy{opacity:.55} + /* ── Réglages en modale ───────────────────────────────────────────────────── Deux volets : nav des sections à gauche, LE panneau choisi à droite. Les panneaux réutilisent tels quels les gabarits de la barre latérale (.slist, @@ -2284,6 +2290,12 @@ html[data-files="1"] #files-btn{color:var(--accent)} + +