mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-12 01:37:06 +02:00
Moteur : corrections de relecture des points de reprise — un slot de plus compte, et un « --version » coincé ne bloque plus le lancement
La liste des points de reprise de llama-server est PAR SLOT : la garde de RAM de l'espacement d'office ne comptait qu'une liste, et sous-estimait d'autant le pire cas avec PARALLEL > 1. Et « loki serve » lit désormais le build du moteur avant de le lancer (hybrides seulement) : sans délai, un binaire coincé à l'initialisation de ses backends bloquait le lancement, là où binHelp, lui, était déjà borné. - ckptSlots : --parallel d'EXTRA_ARGS, sinon PARALLEL, sinon 1 ; « auto » ou illisible compté pour 4. Le pire cas devient points × slots × poids. - engineBuildOf borné à 15 s (WaitDelay pour un descendant qui garde la sortie) : passé le délai, build inconnu, ni avis ni drapeau. Profite aussi au panneau Moteur. - Tests : PARALLEL=2 garde l'espacement, PARALLEL=4 et --parallel 4 le retirent ; table de ckptSlots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dd27b81af0
commit
7f49310b7f
3 files changed
+59
-3
No files matched your search
@@ -26,6 +26,7 @@ package loki
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -281,11 +282,19 @@ func parseEngineBuild(out string) (build int, commit string) {
|
||||
// engineBuildOf interroge un llama-server pour son numéro de build. Renvoie
|
||||
// (0, "") si le binaire ne répond pas — un moteur cassé ne doit pas empêcher
|
||||
// d'en installer un autre.
|
||||
//
|
||||
// Borné dans le temps comme binHelp : « loki serve » le lance avant le moteur
|
||||
// (points de reprise d'un hybride, voir ckptArgs), et un binaire qui reste
|
||||
// coincé à l'initialisation de ses backends ne doit pas bloquer le lancement —
|
||||
// passé le délai, build inconnu, donc ni avis ni drapeau.
|
||||
func engineBuildOf(bin string) (int, string) {
|
||||
if bin == "" || !isFile(bin) {
|
||||
return 0, ""
|
||||
}
|
||||
cmd := hideCmd(exec.Command(bin, "--version"))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
cmd := hideCmd(exec.CommandContext(ctx, bin, "--version"))
|
||||
cmd.WaitDelay = 2 * time.Second // un descendant qui garde la sortie ouverte ne retient pas Wait
|
||||
cmd.Env = libraryPathEnv(filepath.Dir(bin))
|
||||
out, _ := cmd.CombinedOutput() // --version sort parfois en code non nul
|
||||
return parseEngineBuild(string(out))
|
||||
|
||||
@@ -167,6 +167,24 @@ func ckptCount(cfg map[string]string, extra []string, argEnv map[string]string)
|
||||
return n
|
||||
}
|
||||
|
||||
// ckptSlots : nombre de slots que le moteur ouvrira, chacun avec sa propre liste
|
||||
// de points de reprise. --parallel d'EXTRA_ARGS, sinon PARALLEL, sinon 1 (Loki
|
||||
// pose toujours --parallel, voir buildServeArgs). Illisible ou « auto » (-1) :
|
||||
// 4, ce que choisissent les moteurs récents — on compte large.
|
||||
func ckptSlots(cfg map[string]string, extra []string) int {
|
||||
v := flagValue(extra, "-np", "--parallel")
|
||||
if v == "" {
|
||||
v = strings.TrimSpace(cfg["PARALLEL"])
|
||||
}
|
||||
if v == "" {
|
||||
return 1
|
||||
}
|
||||
if n, err := strconv.Atoi(strings.TrimSpace(v)); err == nil && n > 0 {
|
||||
return n
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
// ckptMiB : poids d'un point de reprise d'un hybride (état récurrent en f32).
|
||||
func ckptMiB(g *GGUFInfo) int64 {
|
||||
if g != nil {
|
||||
@@ -254,10 +272,12 @@ func ckptArgs(cfg map[string]string, extra []string, si serveSysInfo) (args, not
|
||||
return args, append(notes, notice+" Pas d'espacement resserré d'office : RAM inconnue ou déjà prise par le modèle "+
|
||||
"(CKPT_MIN_STEP=2048 pour l'imposer).")
|
||||
}
|
||||
worst := int64(n) * ckptMiB(si.GGUF)
|
||||
// La liste est PAR SLOT : avec PARALLEL=4, quatre listes se remplissent.
|
||||
total := n * ckptSlots(cfg, extra)
|
||||
worst := int64(total) * ckptMiB(si.GGUF)
|
||||
if si.RAMAvailMiB > 0 && worst > si.RAMAvailMiB/4 {
|
||||
return args, append(notes, fmt.Sprintf("%s Pas d'espacement resserré d'office : %d points de reprise (~%d Mio) "+
|
||||
"dépasseraient le quart de la RAM libre (CKPT_MIN_STEP=2048 pour l'imposer).", notice, n, worst))
|
||||
"dépasseraient le quart de la RAM libre (CKPT_MIN_STEP=2048 pour l'imposer).", notice, total, worst))
|
||||
}
|
||||
args = append(args, "--checkpoint-min-step", strconv.Itoa(ckptAutoMinStep))
|
||||
return args, append(notes, fmt.Sprintf("%s → --checkpoint-min-step %d : points de reprise exacts plus rapprochés, "+
|
||||
|
||||
@@ -76,6 +76,12 @@ func TestCkptArgs(t *testing.T) {
|
||||
si: func(s *serveSysInfo) { s.RAMMiB = 0 }},
|
||||
{name: "RAM libre trop juste pour 32 points", notes: 1,
|
||||
si: func(s *serveSysInfo) { s.RAMAvailMiB = 8000 }},
|
||||
// Une liste par slot : 2 × 32 × ~150 Mio tient dans le quart de 50 Go, 4 × non.
|
||||
{name: "PARALLEL=2 : deux listes, encore dans le quart de la RAM libre", cfg: map[string]string{"PARALLEL": "2"},
|
||||
want: cms, notes: 1},
|
||||
{name: "PARALLEL=4 : quatre listes, trop pour la RAM libre", cfg: map[string]string{"PARALLEL": "4"}, notes: 1},
|
||||
{name: "--parallel 4 d'EXTRA_ARGS l'emporte sur PARALLEL", cfg: map[string]string{"PARALLEL": "1"},
|
||||
extra: []string{"--parallel", "4"}, notes: 1},
|
||||
{name: "CTX_CHECKPOINTS=16 passé", cfg: map[string]string{"CTX_CHECKPOINTS": "16"},
|
||||
want: []string{"--ctx-checkpoints", "16", "--checkpoint-min-step", "2048"}, notes: 1},
|
||||
{name: "CTX_CHECKPOINTS illisible", cfg: map[string]string{"CTX_CHECKPOINTS": "beaucoup"}, want: cms, notes: 2},
|
||||
@@ -218,3 +224,24 @@ func TestEngineKeepAfterUpdate(t *testing.T) {
|
||||
t.Fatalf("même version : %v", keep)
|
||||
}
|
||||
}
|
||||
|
||||
// Nombre de slots : chacun a sa liste de points de reprise.
|
||||
func TestCkptSlots(t *testing.T) {
|
||||
cases := []struct {
|
||||
cfg map[string]string
|
||||
extra []string
|
||||
want int
|
||||
}{
|
||||
{want: 1},
|
||||
{cfg: map[string]string{"PARALLEL": "3"}, want: 3},
|
||||
{cfg: map[string]string{"PARALLEL": "3"}, extra: []string{"-np", "2"}, want: 2},
|
||||
{extra: []string{"--parallel=6"}, want: 6},
|
||||
{extra: []string{"--parallel", "-1"}, want: 4},
|
||||
{cfg: map[string]string{"PARALLEL": "auto"}, want: 4},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := ckptSlots(c.cfg, c.extra); got != c.want {
|
||||
t.Errorf("ckptSlots(%v, %v) = %d, attendu %d", c.cfg, c.extra, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user