mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-24 07:28:13 +02:00
UI: restaure le comptage tokens + vitesse en direct sur les bulles (raisonnement et reponse)
La refonte serveur avait supprime le compteur live par bulle. On le recalcule cote client au fil des deltas ; en replay (refresh) on n'affiche que le nombre de tokens (pas de vitesse absurde car tout arrive d'un bloc), via replayTarget.
This commit is contained in:
1 parent
4514a96585
commit
3748d3a25f
1 file changed
+24
-3
+24
-3
@@ -1373,17 +1373,25 @@ function saveChat(){ try{ localStorage.setItem('jean.chat', JSON.stringify(msgs)
|
|||||||
// permanent (connectStream), qui rejoue tout le fil depuis le serveur — texte,
|
// permanent (connectStream), qui rejoue tout le fil depuis le serveur — texte,
|
||||||
// appels d'outils, vitesses, raisonnement — puis suit le direct. Plus de
|
// appels d'outils, vitesses, raisonnement — puis suit le direct. Plus de
|
||||||
// localStorage : le même contexte est partagé par tous les appareils.
|
// localStorage : le même contexte est partagé par tous les appareils.
|
||||||
function restoreChat(){ connectStream(); }
|
async function restoreChat(){
|
||||||
|
// On note où en est le serveur : tout ce qui existe déjà sera rejoué d'un bloc
|
||||||
|
// (replay, sans vitesse) ; ce qui vient après est du direct (avec vitesse).
|
||||||
|
try{ const s=await (await jfetch('/api/chat/state')).json(); replayTarget=s.seq||0; }catch(e){}
|
||||||
|
connectStream();
|
||||||
|
}
|
||||||
function onKey(e){ if(e.key==='Enter' && !e.shiftKey){ e.preventDefault(); send(); } }
|
function onKey(e){ if(e.key==='Enter' && !e.shiftKey){ e.preventDefault(); send(); } }
|
||||||
// ===== Conversation SERVEUR (source de vérité, partagée entre appareils) =====
|
// ===== Conversation SERVEUR (source de vérité, partagée entre appareils) =====
|
||||||
// L'historique et la génération vivent sur le serveur jean. Le client ouvre un
|
// L'historique et la génération vivent sur le serveur jean. Le client ouvre un
|
||||||
// flux d'ABONNEMENT permanent (SSE) qui rejoue le journal depuis lastSeq puis
|
// flux d'ABONNEMENT permanent (SSE) qui rejoue le journal depuis lastSeq puis
|
||||||
// suit le direct. Fermer l'onglet n'arrête plus la génération (détachée côté
|
// suit le direct. Fermer l'onglet n'arrête plus la génération (détachée côté
|
||||||
// serveur) ; se reconnecter rejoue tout le fil, détails compris.
|
// serveur) ; se reconnecter rejoue tout le fil, détails compris.
|
||||||
let lastSeq=0, streamAbort=null;
|
// replayTarget = dernier Seq déjà présent sur le serveur au moment où l'on se
|
||||||
|
// (re)connecte. Les événements jusque-là sont du REPLAY (arrivent d'un bloc) →
|
||||||
|
// on n'affiche PAS de vitesse (elle serait absurde). Au-delà = direct → vitesse.
|
||||||
|
let lastSeq=0, streamAbort=null, replayTarget=0;
|
||||||
// État de rendu du tour courant, délimité par les événements user / turn_done.
|
// État de rendu du tour courant, délimité par les événements user / turn_done.
|
||||||
let T=null;
|
let T=null;
|
||||||
function newTurn(){ T={ reasonEl:null, contentEl:null, pendingToolEl:null, typingEl:null, fullContent:'', fullReason:'', turnCollapsibles:[], serverStats:null }; }
|
function newTurn(){ T={ reasonEl:null, contentEl:null, pendingToolEl:null, typingEl:null, fullContent:'', fullReason:'', turnCollapsibles:[], serverStats:null, reasonTok:0, contentTok:0, reasonStart:0, contentStart:0 }; }
|
||||||
newTurn();
|
newTurn();
|
||||||
const simpleMode=()=>document.documentElement.getAttribute('data-display')==='simple';
|
const simpleMode=()=>document.documentElement.getAttribute('data-display')==='simple';
|
||||||
function removeTyping(){ if(T.typingEl){ T.typingEl.remove(); T.typingEl=null; } }
|
function removeTyping(){ if(T.typingEl){ T.typingEl.remove(); T.typingEl=null; } }
|
||||||
@@ -1401,6 +1409,15 @@ function renderStats(el, s){
|
|||||||
if(s.gen_tokens) parts.push('decode '+s.gen_tokens+' tok · '+(s.gen_per_second||0).toFixed(1)+' tok/s');
|
if(s.gen_tokens) parts.push('decode '+s.gen_tokens+' tok · '+(s.gen_per_second||0).toFixed(1)+' tok/s');
|
||||||
setLabel(el, parts.join(' · '));
|
setLabel(el, parts.join(' · '));
|
||||||
}
|
}
|
||||||
|
// Label LIVE d'une bulle : nombre de tokens + vitesse mesurée côté client au fil
|
||||||
|
// des deltas (comme avant la refonte). En replay (seq<=replayTarget), on n'affiche
|
||||||
|
// que le nombre de tokens — pas la vitesse, car tout arrive d'un bloc.
|
||||||
|
function labelTokens(el, role, n, start, seq){
|
||||||
|
if(!el) return;
|
||||||
|
if(seq && seq<=replayTarget){ setLabel(el, role+' · '+n+' tok'); return; }
|
||||||
|
const secs=(performance.now()-start)/1000; const tps= secs>0.05 ? (n/secs):0;
|
||||||
|
setLabel(el, role+' · '+n+' tok · '+tps.toFixed(1)+' tok/s');
|
||||||
|
}
|
||||||
function setBusy(on){ busy=on;
|
function setBusy(on){ busy=on;
|
||||||
document.getElementById('send').style.display=on?'none':'inline-block';
|
document.getElementById('send').style.display=on?'none':'inline-block';
|
||||||
document.getElementById('stop').style.display=on?'inline-block':'none'; }
|
document.getElementById('stop').style.display=on?'inline-block':'none'; }
|
||||||
@@ -1430,11 +1447,15 @@ function handleDelta(d){
|
|||||||
killTyping();
|
killTyping();
|
||||||
if(!T.reasonEl){ collapseAll(T.turnCollapsibles); T.reasonEl=addMsg('reasoning',''); T.fullReason=''; T.turnCollapsibles.push(T.reasonEl); }
|
if(!T.reasonEl){ collapseAll(T.turnCollapsibles); T.reasonEl=addMsg('reasoning',''); T.fullReason=''; T.turnCollapsibles.push(T.reasonEl); }
|
||||||
showTyping(); T.fullReason+=d.reasoning_content; renderBody(T.reasonEl, T.fullReason);
|
showTyping(); T.fullReason+=d.reasoning_content; renderBody(T.reasonEl, T.fullReason);
|
||||||
|
if(!T.reasonStart) T.reasonStart=performance.now(); T.reasonTok++;
|
||||||
|
labelTokens(T.reasonEl, 'reasoning', T.reasonTok, T.reasonStart, d.seq);
|
||||||
return; }
|
return; }
|
||||||
if(d.content){
|
if(d.content){
|
||||||
removeTyping();
|
removeTyping();
|
||||||
if(!T.contentEl){ collapseAll(T.turnCollapsibles); T.contentEl=addMsg('assistant',''); T.fullContent=''; }
|
if(!T.contentEl){ collapseAll(T.turnCollapsibles); T.contentEl=addMsg('assistant',''); T.fullContent=''; }
|
||||||
T.fullContent+=d.content; renderBody(T.contentEl, T.fullContent);
|
T.fullContent+=d.content; renderBody(T.contentEl, T.fullContent);
|
||||||
|
if(!T.contentStart) T.contentStart=performance.now(); T.contentTok++;
|
||||||
|
labelTokens(T.contentEl, 'assistant', T.contentTok, T.contentStart, d.seq);
|
||||||
return; }
|
return; }
|
||||||
}
|
}
|
||||||
// Flux d'abonnement permanent + reconnexion auto (from=lastSeq → pas de
|
// Flux d'abonnement permanent + reconnexion auto (from=lastSeq → pas de
|
||||||
|
|||||||
Reference in new issue
Block a user