mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-19 21:18:20 +02:00
VRAM : le bilan mentait, un GET coupait le moteur, la dictée bloquait
Relecture du bouton « Libérer la VRAM » (009b585) — six défauts, tous
sur la même feature :
- gpuUsedSettled comptait une lecture ratée de nvidia-smi comme « 0 Mo »
(pilote en réinitialisation juste après l'arrêt) : freed = before, et
l'interface annonçait 14 Gio rendus alors que rien n'avait bougé. Elle
rendait aussi la main au premier palier, AVANT que le pilote ait réagi
(taskkill et Process.Kill reviennent avant le ménage CUDA) : « 0 Mo »
après un déchargement qui marchait. gpuSettle saute les lectures
ratées, n'accepte un palier qu'une fois la baisse observée, et est
testable (sampler injecté).
- /api/vram/unload et /reload acceptaient GET : sans clé de pilotage,
une balise <img> sur une page tierce suffisait à couper le moteur.
405 + Allow: POST.
- whisperShutdown prend wsrvMu, que whisperEnsure garde jusqu'à deux
minutes pendant un chargement de modèle : le geste dépassait le délai
du navigateur, moteur pourtant déjà arrêté. whisperShutdownVite tue le
processus en train de démarrer (poignée atomique hors verrou) au lieu
d'attendre derrière lui.
- Sous systemd, une unité en crash-loop répond « activating », pas
« active » : le stop était sauté et systemd relançait llama-server
toutes les trois secondes pendant que l'UI disait « déjà arrêté ».
engineNeedsStop élargit aux états transitoires.
- Un moteur planté au chargement était présenté comme « modèle
déchargé — recharge-le » : LOAD_ERROR distingue les deux, le conseil
renvoie vers l'erreur affichée dans le moniteur.
- Deux clics concurrents (moniteur + réglages) lançaient un stop au
milieu d'un start ; VRAM_BUSY fait verrou, et le bouton se repeint
depuis l'état renvoyé par le serveur, pas depuis l'ancien poll.
Quelques Mo de bruit entre deux lectures ne font plus « VRAM libérée :
0.0 Gio ».
Au passage, le 404 d'un téléchargement nomme le fichier manquant : sur
un dépôt qui publie six fragments sur sept (table PLE livrée à part),
« la révision a pu être réécrite » envoyait chercher au mauvais endroit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01961iDyM6pwn2dE2SW23gYX
This commit is contained in:
1 parent
009b585e75
commit
33de4a1144
8 files changed
+279
-32
No files matched your search
@@ -22,6 +22,9 @@ async function loadStatus(){
|
||||
// local) : un arrêt venu d'un autre onglet, du terminal ou d'un job doit se
|
||||
// lire ici aussi, sinon le bouton propose de libérer une carte déjà libre.
|
||||
ENGINE_ACTIVE = !!s.active;
|
||||
// Un moteur qui a PLANTÉ au chargement n'est pas « déchargé » : sans ce
|
||||
// drapeau, syncSendBtn proposerait de recharger un modèle qui va replanter.
|
||||
LOAD_ERROR = s.load_error || '';
|
||||
paintVramBtn();
|
||||
// Le bouton d'envoi suit l'état du moteur : inutile de pouvoir envoyer un
|
||||
// message à un modèle qui n'est pas encore chargé (voir syncSendBtn).
|
||||
@@ -197,7 +200,7 @@ async function loadVram(){
|
||||
// ENGINE_ACTIVE vient de /api/status (relu toutes les 5 s), jamais d'un drapeau
|
||||
// posé au clic : sinon un second onglet, ou un arrêt venu du terminal,
|
||||
// afficherait un bouton qui ment.
|
||||
let ENGINE_ACTIVE = true, VRAM_BUSY = false;
|
||||
let ENGINE_ACTIVE = true, VRAM_BUSY = false, LOAD_ERROR = '';
|
||||
function paintVramBtn(){
|
||||
const b = document.getElementById('vram-btn');
|
||||
if(!b || VRAM_BUSY) return; // pendant l'opération, le libellé appartient à vramBusy
|
||||
@@ -217,7 +220,11 @@ function vramBusy(on, label){
|
||||
}
|
||||
// force=true : on a déjà demandé confirmation ET accepté de couper une réponse
|
||||
// en cours d'écriture (le serveur refuse sans ça, voir handleVramUnload).
|
||||
// Les deux gestes sont exclusifs : un second clic (le bouton des réglages
|
||||
// pendant que celui du moniteur travaille) lancerait un « stop » au milieu d'un
|
||||
// démarrage, ou l'inverse. VRAM_BUSY tient lieu de verrou.
|
||||
async function vramUnload(force){
|
||||
if(VRAM_BUSY) return;
|
||||
if(!force){
|
||||
const ok = await askConfirm(
|
||||
'Décharger le modèle ? Le moteur s\'arrête et rend la mémoire vidéo — le chat reste indisponible jusqu\'au rechargement.',
|
||||
@@ -227,6 +234,10 @@ async function vramUnload(force){
|
||||
vramBusy(true, 'libération…');
|
||||
let r = null;
|
||||
try{ r = await jpost('/api/vram/unload', {force: !!force}); }catch(_){}
|
||||
// Le serveur dit l'état RÉEL après coup : le repeindre depuis l'ancien
|
||||
// ENGINE_ACTIVE (relu seulement toutes les 5 s) afficherait « Libérer » sur
|
||||
// un moteur déjà arrêté.
|
||||
if(r && typeof r.active === 'boolean') ENGINE_ACTIVE = r.active;
|
||||
vramBusy(false);
|
||||
if(r && r.generating){
|
||||
const ok = await askConfirm('Une réponse est en cours d\'écriture : décharger maintenant l\'interrompt. Continuer ?',
|
||||
@@ -236,17 +247,21 @@ async function vramUnload(force){
|
||||
}
|
||||
// La mesure d'abord : la dictée peut avoir rendu de la VRAM alors même que le
|
||||
// moteur était déjà arrêté — annoncer « déjà arrêté » nierait ce qui vient
|
||||
// d'être libéré.
|
||||
// d'être libéré. Sous quelques dizaines de Mo, c'est le bruit du bureau entre
|
||||
// deux lectures, pas une libération : on ne l'annonce pas.
|
||||
if(!r || !r.ok) toast('échec : ' + ((r && r.error) || 'réseau'));
|
||||
else if(r.gpu && r.freed_mb > 0) toast('VRAM libérée : ' + (r.freed_mb/1024).toFixed(1) + ' Gio');
|
||||
else if(r.gpu && r.freed_mb >= 64) toast('VRAM libérée : ' + fmtMo(r.freed_mb));
|
||||
else if(!r.was_active) toast('le moteur était déjà arrêté');
|
||||
else toast('modèle déchargé');
|
||||
loadAll();
|
||||
}
|
||||
function fmtMo(mb){ return mb >= 1024 ? (mb/1024).toFixed(1) + ' Gio' : mb + ' Mo'; }
|
||||
async function vramReload(){
|
||||
if(VRAM_BUSY) return;
|
||||
vramBusy(true, 'démarrage…');
|
||||
let r = null;
|
||||
try{ r = await jpost('/api/vram/reload', {}); }catch(_){}
|
||||
if(r && r.ok) ENGINE_ACTIVE = true;
|
||||
vramBusy(false);
|
||||
if(!r || !r.ok){ toast('échec : ' + ((r && r.error) || 'réseau')); return; }
|
||||
toast('modèle en cours de chargement…');
|
||||
|
||||
@@ -280,12 +280,18 @@ function syncSendBtn(){
|
||||
// Moteur ARRÊTÉ (VRAM libérée à la demande, ou service coupé) : dire « le
|
||||
// modèle charge » serait faux, et on attendrait un chargement qui ne viendra
|
||||
// jamais. On nomme alors le geste qui remet le modèle en mémoire.
|
||||
const unloaded = !ready && !ENGINE_ACTIVE;
|
||||
sb.title = ready ? '' : (unloaded ? 'le modèle est déchargé — recharge-le pour écrire'
|
||||
// Trois raisons de ne pas être prêt, trois messages : planté au chargement
|
||||
// (recharger ne ferait que replanter — la cause est affichée dans le
|
||||
// moniteur), déchargé à la demande, ou simplement en train de charger.
|
||||
const failed = !ready && !!LOAD_ERROR;
|
||||
const unloaded = !ready && !failed && !ENGINE_ACTIVE;
|
||||
sb.title = ready ? '' : failed ? 'le modèle n\'a pas pu charger — voir l\'erreur dans le moniteur'
|
||||
: (unloaded ? 'le modèle est déchargé — recharge-le pour écrire'
|
||||
: 'le modèle n\'est pas encore chargé');
|
||||
const hint=document.getElementById('sendhint');
|
||||
if(hint){
|
||||
hint.textContent = ready ? 'Entrée pour envoyer · Maj+Entrée = nouvelle ligne'
|
||||
: failed ? 'Le modèle n\'a pas pu charger — l\'erreur est affichée dans le moniteur, en bas de la barre latérale.'
|
||||
: unloaded ? 'Modèle déchargé — « Recharger le modèle » dans le moniteur, en bas de la barre latérale.'
|
||||
: 'Le modèle charge — envoi possible dès qu\'il est prêt.';
|
||||
hint.classList.toggle('waiting', !ready);
|
||||
|
||||
Reference in new issue
Block a user