mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-13 18:28:03 +02:00
Chaque dictée relançait whisper-cli, qui relisait le modèle depuis le disque avant de transcrire. Ce chargement dominait le temps de réponse — 3,2 s de calcul pour 3,4 s d'audio — et aucun réglage ne pouvait le rattraper. whisper-server garde le modèle en mémoire entre deux phrases. Loki le supervise : démarrage au premier clic sur le micro, extinction après dix minutes sans dictée. Une dictée n'est pas un service permanent, et garder un modèle chargé toute la journée priverait le moteur de chat de sa VRAM. - Réglages serveur (modèle, langue, matériel, réactivité) dans bkState, avec leurs routes. La langue par défaut passe de « auto » à « fr » : sur quelques secondes d'audio la détection se trompe, et une langue mal détectée produit du charabia — des suites de caractères géorgiens ont été observées. - Catalogue de quatre modèles, de small à large-v3 ; large-v3-turbo par défaut. Téléchargement par identifiant, dans un .part renommé à la fin : un transfert interrompu ne laisse plus un .bin tronqué qu'on croirait bon. - Le GPU se choisit par CUDA_VISIBLE_DEVICES, posé en REMPLAÇANT toute valeur héritée. Dupliquée, la variable laisse le gagnant dépendre de la libc. - Les marqueurs de whisper ([BLANK_AUDIO], (silence)) ne sont plus collés dans le champ de saisie comme s'ils étaient du texte dicté. L'image construit DEUX binaires whisper-server, CPU et CUDA. LLAMACPP_IMAGE accepte la variante CPU de l'image amont ; sur cette base les .so CUDA sont absentes et un binaire lié à CUDA n'a même pas de quoi démarrer, donc aucun repli n'est possible depuis le programme. Le garde-fou du Dockerfile vérifie maintenant les deux étapes : les drapeaux de portabilité de la PR #18 doivent survivre à l'arrivée de CUDA. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3ewMAsXhkw9RY11kb9Dc9
91 lines
2.9 KiB
Go
91 lines
2.9 KiB
Go
// Réglages de la dictée vocale — modèle, langue, matériel, réactivité.
|
|
//
|
|
// Ils vivent dans le magasin clé/valeur (bkState), comme le jeton Hugging
|
|
// Face : ce sont des réglages de SERVEUR, pas de navigateur. Le modèle chargé
|
|
// et le GPU occupé sont des propriétés de la machine, identiques pour tous les
|
|
// onglets ouverts.
|
|
package loki
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
const dictateCfgKey = "dictate_cfg"
|
|
|
|
// DictateCfg : Device vaut "cpu" ou un indice de GPU en décimal ("0", "1"),
|
|
// dans l'ordre de nvidia-smi — le même que celui affiché par /api/vram.
|
|
type DictateCfg struct {
|
|
Model string `json:"model"`
|
|
Lang string `json:"lang"`
|
|
Device string `json:"device"`
|
|
Reactivity string `json:"reactivity"`
|
|
}
|
|
|
|
// withDefauts comble les champs vides. Appliqué à la LECTURE plutôt qu'à
|
|
// l'écriture : une version future qui ajoute un champ trouvera les réglages
|
|
// déjà enregistrés sans champ correspondant, et doit pouvoir le combler.
|
|
func (c DictateCfg) withDefauts() DictateCfg {
|
|
if c.Model == "" {
|
|
// large-v3-turbo : meilleur rapport qualité/vitesse en français à ce
|
|
// jour. Le défaut penche vers la qualité.
|
|
c.Model = "large-v3-turbo-q5_0"
|
|
}
|
|
if c.Lang == "" {
|
|
// Pas "auto" : sur des tranches de quelques secondes la détection se
|
|
// trompe, et une langue mal détectée produit du charabia — des suites
|
|
// de caractères géorgiens ont été observées en production.
|
|
c.Lang = "fr"
|
|
}
|
|
if c.Device == "" {
|
|
c.Device = "cpu"
|
|
}
|
|
if c.Reactivity == "" {
|
|
c.Reactivity = "moyen"
|
|
}
|
|
return c
|
|
}
|
|
|
|
func dictateCfgLoad() DictateCfg {
|
|
var c DictateCfg
|
|
getJSON(bkState, dictateCfgKey, &c)
|
|
return c.withDefauts()
|
|
}
|
|
|
|
func dictateCfgSave(c DictateCfg) error {
|
|
c = c.withDefauts()
|
|
// Valider ICI plutôt qu'au démarrage du serveur : un modèle hors catalogue
|
|
// accepté en silence ne se manifesterait qu'au premier clic sur le micro,
|
|
// loin du geste qui l'a causé.
|
|
if _, ok := whisperCatalogue[c.Model]; !ok {
|
|
return fmt.Errorf("modèle de dictée inconnu : %s", c.Model)
|
|
}
|
|
return putJSON(bkState, dictateCfgKey, c)
|
|
}
|
|
|
|
// cudaVisibleDevices traduit le réglage en valeur de CUDA_VISIBLE_DEVICES.
|
|
// Vide = aucun GPU visible = whisper tourne sur le CPU. Toute valeur qui n'est
|
|
// pas un indice positif retombe sur le CPU : mieux vaut une dictée lente qu'un
|
|
// GPU choisi au hasard sur une machine dont on ne sait rien.
|
|
func (c DictateCfg) cudaVisibleDevices() string {
|
|
n, err := strconv.Atoi(c.Device)
|
|
if err != nil || n < 0 {
|
|
return ""
|
|
}
|
|
return strconv.Itoa(n)
|
|
}
|
|
|
|
// chunkBounds : réserve minimale avant de couper, et coupure forcée quand
|
|
// aucun silence n'arrive. Consommé par le découpeur du plan « temps réel ».
|
|
func (c DictateCfg) chunkBounds() (time.Duration, time.Duration) {
|
|
switch c.Reactivity {
|
|
case "court":
|
|
return 1000 * time.Millisecond, 5 * time.Second
|
|
case "long":
|
|
return 3 * time.Second, 15 * time.Second
|
|
default:
|
|
return 1500 * time.Millisecond, 8 * time.Second
|
|
}
|
|
}
|