mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Chercher à la main le bon micro-lot, les threads ou la marge --fit d'un preset demandait de dupliquer, basculer et comparer des bench complets un par un. L'optimiseur le fait sur un moteur d'essai, sans jamais toucher à config.env ni à ce que calcule le modèle, et n'écrit rien sans un clic. - Isolation : moteur principal arrêté puis relancé ; chaque essai est une copie de la configuration (LOKI_HOME/tune/run), lancée par « loki serve » sur 127.0.0.1 et un port libre, groupe de processus tué en sortie - Verrou exclusif inter-processus (LOKI_HOME/tune.lock, PID + démarrage + binaire) vérifié par serviceAction start/restart et par le chat, les tâches, bash_bg, /v1, le bench, la bascule et l'enregistrement de preset, GPU, clé d'API, moteur ; essai orphelin arrêté avant tout démarrage du moteur - Essais : lots, threads, délestage, marges --fit, files CUDA ; placement --fit et SPEC/CUDA_GRAPH_OPT/--backend-sampling seulement sur demande - EXTRA_ARGS jeton par jeton, liste blanche seulement ; ligne de commande de chaque essai composée à blanc et comparée à la référence (« dénature »), contexte, slots et cache KV recontrôlés une fois chargé - Mesure : bench complet du lot 1 à profondeur fixe ; score = durée d'un tour type (médianes de la télémétrie) ; gain > max(3 %, écart entre passages) - Application sur clic : copie du preset, ou preset actuel sauvegardé, réécrit clé par clé, vérifié par une sonde et rétabli en cas d'échec Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
142 lines
4.4 KiB
Go
142 lines
4.4 KiB
Go
//go:build linux
|
|
|
|
package loki
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// serviceActionOS wraps `systemctl <action> <svc>` with passwordless sudo where
|
|
// it makes sense, and prints a follow-up status check after start/restart.
|
|
// Sans systemd (conteneur Docker, LOKI_CONTAINER=1), on bascule sur la
|
|
// supervision par fichier PID (sys_service_container.go).
|
|
func serviceActionOS(action string) error {
|
|
if !systemdAvailable() {
|
|
return userSvcAction(action)
|
|
}
|
|
svc := serviceName()
|
|
needsRoot := action == "start" || action == "stop" || action == "restart" || action == "enable" || action == "disable"
|
|
args := []string{}
|
|
bin := "systemctl"
|
|
if needsRoot && os.Geteuid() != 0 {
|
|
bin = "sudo"
|
|
args = append(args, "-n", "systemctl")
|
|
}
|
|
args = append(args, action, svc)
|
|
cmd := exec.Command(bin, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err != nil {
|
|
return err
|
|
}
|
|
switch action {
|
|
case "start", "restart":
|
|
return checkStarted(svc)
|
|
case "stop":
|
|
fmt.Println(green("[ok]") + " arrêté")
|
|
case "enable":
|
|
fmt.Println(green("[ok]") + " démarrage auto activé")
|
|
case "disable":
|
|
fmt.Println(green("[ok]") + " démarrage auto désactivé")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// unitState renvoie ActiveState et SubState de l'unité (« activating » /
|
|
// « auto-restart » par exemple).
|
|
func unitState(svc string) (active, sub string) {
|
|
out, _ := exec.Command("systemctl", "show", svc, "-p", "ActiveState", "-p", "SubState").Output()
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
k, v, ok := strings.Cut(strings.TrimSpace(line), "=")
|
|
if !ok {
|
|
continue
|
|
}
|
|
switch k {
|
|
case "ActiveState":
|
|
active = v
|
|
case "SubState":
|
|
sub = v
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// checkStarted attend que l'unité se stabilise, puis dit la vérité.
|
|
//
|
|
// ⚠️ « activating » n'est PAS un succès : avec Restart=on-failure, un moteur qui
|
|
// meurt au démarrage repasse en boucle par activating (SubState auto-restart).
|
|
// L'ancien test l'acceptait, et `loki start` répondait « [ok] activating » sur
|
|
// un service qui ne démarrerait jamais — d'où des rapports « loki start dit ok
|
|
// mais loki test ne répond pas ». On distingue donc le SubState, et on laisse
|
|
// au moteur le temps de charger un gros modèle (il reste en activating, mais
|
|
// PAS en auto-restart).
|
|
func checkStarted(svc string) error {
|
|
var state, sub string
|
|
for i := 0; i < 5; i++ {
|
|
time.Sleep(2 * time.Second)
|
|
state, sub = unitState(svc)
|
|
if state == "active" {
|
|
fmt.Printf("%s %s: actif\n", green("[ok]"), svc)
|
|
return nil
|
|
}
|
|
if state == "failed" || sub == "auto-restart" {
|
|
break // inutile d'attendre : il boucle sur un échec
|
|
}
|
|
}
|
|
if state == "activating" && sub != "auto-restart" {
|
|
// Chargement en cours (un gros .gguf prend des minutes) : légitime.
|
|
fmt.Printf("%s %s: démarrage en cours (chargement du modèle) — %s pour suivre\n",
|
|
green("[ok]"), svc, bold("loki logs"))
|
|
return nil
|
|
}
|
|
what := state
|
|
if sub == "auto-restart" {
|
|
what = "redémarre en boucle (le moteur meurt au lancement)"
|
|
}
|
|
fmt.Printf("%s %s: %s — derniers logs :\n", red("[ERREUR]"), svc, what)
|
|
fmt.Println("------------------------------------------------")
|
|
logs, _ := exec.Command("journalctl", "-u", svc, "-n", "20", "--no-pager").Output()
|
|
fmt.Print(string(logs))
|
|
fmt.Println("------------------------------------------------")
|
|
fmt.Printf("→ loki logs pour plus de détails\n→ loki edit pour corriger la configuration (BIN, MODEL…)\n")
|
|
return fmt.Errorf("service %s non démarré", svc)
|
|
}
|
|
|
|
func serviceLogs() error {
|
|
if !systemdAvailable() {
|
|
return userServiceLogs()
|
|
}
|
|
cmd := exec.Command("journalctl", "-u", serviceName(), "-n", "80", "-f")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
return cmd.Run()
|
|
}
|
|
|
|
// serviceIsActive reports whether the systemd unit is currently running.
|
|
func serviceIsActive() bool {
|
|
if !systemdAvailable() {
|
|
pid := readServicePID()
|
|
return pid > 0 && processAlive(pid)
|
|
}
|
|
out, _ := exec.Command("systemctl", "is-active", serviceName()).Output()
|
|
return strings.TrimSpace(string(out)) == "active"
|
|
}
|
|
|
|
// serviceLogTail renvoie les n dernières lignes du journal du service (pour
|
|
// l'UI web). Linux : journalctl.
|
|
func serviceLogTail(n int) string {
|
|
if !systemdAvailable() {
|
|
return tailFile(logFilePath(), n)
|
|
}
|
|
out, err := exec.Command("journalctl", "-u", serviceName(), "-n", strconv.Itoa(n), "--no-pager").CombinedOutput()
|
|
if err != nil && len(out) == 0 {
|
|
return "journalctl indisponible : " + err.Error()
|
|
}
|
|
return string(out)
|
|
}
|