mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Journal d'installation : la progression de Strata visible, plus de lignes en double
- Les barres de progression qui se redessinent avec \r (téléchargement du modèle par l'installeur de Strata) n'apparaissaient jamais : le journal ne découpait que sur \n et restait figé pendant tout le téléchargement. Une version toutes les 10 s est montrée ; \r\n reste une fin de ligne. - Le suivi du job relançait une requête chaque seconde sans attendre la précédente : quand le serveur était occupé, deux réponses ajoutaient les mêmes lignes au journal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5016396998
commit
ac6d8ad144
4 files changed
+82
-6
No files matched your search
@@ -44,20 +44,45 @@ func emitBuildLine(line string) {
|
||||
// Sert à téer la sortie des commandes de runStep quand un sink est actif.
|
||||
// (mutex : stdout et stderr d'une même commande peuvent écrire en parallèle)
|
||||
type sinkWriter struct {
|
||||
mu sync.Mutex
|
||||
buf []byte
|
||||
mu sync.Mutex
|
||||
buf []byte
|
||||
lastProg time.Time // dernière ligne de progression (\r) émise
|
||||
}
|
||||
|
||||
// sinkProgressEvery : une barre de progression (tqdm, téléchargements de
|
||||
// l'installeur de Strata) se redessine sur place avec \r, sans jamais finir sa
|
||||
// ligne. On en montre une version toutes les 10 s : sans elle, un téléchargement
|
||||
// de 76 Go laissait le journal figé pendant une heure ; toutes, elles le noieraient.
|
||||
const sinkProgressEvery = 10 * time.Second
|
||||
|
||||
func (s *sinkWriter) Write(p []byte) (int, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.buf = append(s.buf, p...)
|
||||
for {
|
||||
i := strings.IndexByte(string(s.buf), '\n')
|
||||
i := strings.IndexAny(string(s.buf), "\r\n")
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
emitBuildLine(strings.TrimRight(string(s.buf[:i]), "\r"))
|
||||
if s.buf[i] == '\n' {
|
||||
emitBuildLine(strings.TrimRight(string(s.buf[:i]), "\r"))
|
||||
s.buf = s.buf[i+1:]
|
||||
continue
|
||||
}
|
||||
// \r : fin de ligne Windows (\r\n) ou barre qui se redessine. Le \n peut
|
||||
// arriver dans l'écriture suivante : on attend de le savoir.
|
||||
if i+1 >= len(s.buf) {
|
||||
break
|
||||
}
|
||||
if s.buf[i+1] == '\n' {
|
||||
emitBuildLine(string(s.buf[:i]))
|
||||
s.buf = s.buf[i+2:]
|
||||
continue
|
||||
}
|
||||
if prog := strings.TrimSpace(string(s.buf[:i])); prog != "" && time.Since(s.lastProg) >= sinkProgressEvery {
|
||||
s.lastProg = time.Now()
|
||||
emitBuildLine(prog)
|
||||
}
|
||||
s.buf = s.buf[i+1:]
|
||||
}
|
||||
return len(p), nil
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package loki
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Une barre de progression (\r sans \n) doit apparaître dans le journal, sans
|
||||
// le noyer ; \r\n reste une fin de ligne ordinaire, même coupé entre deux écritures.
|
||||
func TestSinkWriterProgression(t *testing.T) {
|
||||
var got []string
|
||||
setBuildSink(func(l string) { got = append(got, l) })
|
||||
defer setBuildSink(nil)
|
||||
|
||||
s := &sinkWriter{}
|
||||
s.Write([]byte("Step 5\r"))
|
||||
s.Write([]byte("\n modèle : 1 / 76 GB\r modèle : 2 / 76 GB\r"))
|
||||
s.Write([]byte(" modèle : 3 / 76 GB\r"))
|
||||
s.lastProg = time.Now().Add(-time.Minute) // 10 s écoulées
|
||||
s.Write([]byte(" modèle : 40 / 76 GB\r[ok] fini\n"))
|
||||
|
||||
// « 3 » finissait sa propre écriture : il attendait de savoir si un saut de
|
||||
// ligne suivait, et part donc avec l'écriture suivante.
|
||||
want := []string{"Step 5", "modèle : 1 / 76 GB", "modèle : 3 / 76 GB", "[ok] fini"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("lignes %q, attendu %q", got, want)
|
||||
}
|
||||
}
|
||||
@@ -9097,10 +9097,21 @@ function lcStartPolling(){
|
||||
lcPollJob();
|
||||
}
|
||||
|
||||
// Une seule requête à la fois : l'intervalle d'une seconde relançait un appel
|
||||
// avant la réponse du précédent quand le serveur était occupé (téléchargement,
|
||||
// accès distant), et les deux ajoutaient les MÊMES lignes au journal.
|
||||
let lcPollInflight = false;
|
||||
async function lcPollJob(quiet){
|
||||
if(lcPollInflight) return;
|
||||
lcPollInflight = true;
|
||||
const from = lcLogNext;
|
||||
let j;
|
||||
try{ j = await jget('/api/llamacpp/job?from='+lcLogNext); }catch(_){ return; }
|
||||
try{ j = await jget('/api/llamacpp/job?from='+from); }catch(_){ return; }
|
||||
finally{ lcPollInflight = false; }
|
||||
if(!j.exists) return;
|
||||
// Le journal a été remis à zéro ou avancé entre-temps (nouveau job) : ces
|
||||
// lignes ne suivent plus ce qui est affiché.
|
||||
if(from !== lcLogNext) return;
|
||||
const phaseEl = document.getElementById('lc-job-phase');
|
||||
if(j.lines && j.lines.length){
|
||||
const pre = document.getElementById('lc-log');
|
||||
|
||||
@@ -453,10 +453,21 @@ function lcStartPolling(){
|
||||
lcPollJob();
|
||||
}
|
||||
|
||||
// Une seule requête à la fois : l'intervalle d'une seconde relançait un appel
|
||||
// avant la réponse du précédent quand le serveur était occupé (téléchargement,
|
||||
// accès distant), et les deux ajoutaient les MÊMES lignes au journal.
|
||||
let lcPollInflight = false;
|
||||
async function lcPollJob(quiet){
|
||||
if(lcPollInflight) return;
|
||||
lcPollInflight = true;
|
||||
const from = lcLogNext;
|
||||
let j;
|
||||
try{ j = await jget('/api/llamacpp/job?from='+lcLogNext); }catch(_){ return; }
|
||||
try{ j = await jget('/api/llamacpp/job?from='+from); }catch(_){ return; }
|
||||
finally{ lcPollInflight = false; }
|
||||
if(!j.exists) return;
|
||||
// Le journal a été remis à zéro ou avancé entre-temps (nouveau job) : ces
|
||||
// lignes ne suivent plus ce qui est affiché.
|
||||
if(from !== lcLogNext) return;
|
||||
const phaseEl = document.getElementById('lc-job-phase');
|
||||
if(j.lines && j.lines.length){
|
||||
const pre = document.getElementById('lc-log');
|
||||
|
||||
Reference in new issue
Block a user