diff --git a/internal/loki/sys_loadpct_linux.go b/internal/loki/sys_loadpct_linux.go index c41ad23..206f605 100644 --- a/internal/loki/sys_loadpct_linux.go +++ b/internal/loki/sys_loadpct_linux.go @@ -20,6 +20,24 @@ import ( "strings" ) +// procVmRSS lit la mémoire résidente (octets) d'un process — /proc//status, +// ligne « VmRSS: N kB ». 0 si illisible. +func procVmRSS(pid int) int64 { + b, err := os.ReadFile("/proc/" + strconv.Itoa(pid) + "/status") + if err != nil { + return 0 + } + for _, line := range strings.Split(string(b), "\n") { + if v, ok := strings.CutPrefix(line, "VmRSS:"); ok { + v = strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(v), "kB")) + if n, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64); err == nil { + return n * 1024 + } + } + } + return 0 +} + // engineLoadPct renvoie le pourcentage estimé de chargement du modèle, ou -1 // quand il n'est pas mesurable (pas de PID, /proc illisible, taille inconnue). func engineLoadPct() int { @@ -54,6 +72,16 @@ func engineLoadPct() int { if read < 0 { return -1 } + // Modèle déjà dans le cache disque (redémarrage à chaud) : llama-server ne + // LIT plus rien sur le disque — les pages viennent du cache, read_bytes + // reste à zéro et le pourcentage restait figé à 0 jusqu'au « prêt ». La + // mémoire résidente (VmRSS), elle, grandit quoi qu'il arrive : les pages du + // .gguf touchées via mmap y comptent, cache ou pas. On prend le plus avancé + // des deux — approximatif (l'offload GPU peut relâcher des pages), mais le + // compteur BOUGE au lieu de mentir. + if rss := procVmRSS(pid); rss > read { + read = rss + } pct := int(read * 100 / size) if pct > 99 { // 100 % n'existe pas ici : c'est /health qui dit « prêt ». Au-delà de 99 diff --git a/internal/loki/ui/index.html b/internal/loki/ui/index.html index 6491eb5..0aee259 100644 --- a/internal/loki/ui/index.html +++ b/internal/loki/ui/index.html @@ -2069,7 +2069,7 @@ html[data-files="1"] #files-btn{color:var(--accent)} Deux volets : nav des sections à gauche, LE panneau choisi à droite. Les panneaux réutilisent tels quels les gabarits de la barre latérale (.slist, .switchrow, .subhead…), seuls la coque et le routage sont neufs. */ -#side-settings-btn{display:flex;align-items:center;gap:8px;width:100%;margin:8px 0 0;padding:10px 12px; +#side-settings-btn{display:flex;align-items:center;gap:8px;width:100%;margin:0;padding:10px 14px;flex:none; background:none;border:0;border-top:1px solid var(--border);border-radius:0;box-shadow:none; font-size:13px;color:var(--text);cursor:pointer;text-align:left} #side-settings-btn:hover{background:var(--bubble)} @@ -2205,15 +2205,16 @@ html[data-files="1"] #files-btn{color:var(--accent)} latérale par défaut, c'est les discussions et la machine. Les sections ouvertes par programme (journal du moteur, installation llama.cpp) passent par openDetails(), qui déplie aussi les parents. --> - + + - - + + -