mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-24 15:38:12 +02:00
Rebaptise AJEAN en Loki (fork, lignée conservée)
- module github.com/R0m1k3/Loki, cmd/loki, internal/loki (package loki) - LOKI_HOME, LOKI_MODEL_DIRS, LOKI_SERVICE, LOKI_DL_CONNS ; /etc/loki ; units loki-engine / loki-ui ; binaire et aide CLI - updateRepo pointe sur R0m1k3/Loki (l'auto-update ne tirera plus les binaires AJEAN amont) Conservé à l'identique : le domaine ajean.link (service de tunnel amont), les littéraux de migration 0.7.x (migrate_07.go), RELEASE_NOTES.md et LICENSE (historique et licence de l'amont). go build/vet/test : verts.
This commit is contained in:
157 files changed
+928
-928
No files matched your search
@@ -0,0 +1,126 @@
|
||||
// web_llamacpp_job.go — persistance du job llama.cpp (install / update /
|
||||
// backend custom).
|
||||
//
|
||||
// Le job vivait UNIQUEMENT en mémoire du process web. Conséquence : au moindre
|
||||
// redémarrage du service (mise à jour du binaire, crash, reboot) l'état
|
||||
// disparaissait — la page rechargée n'affichait plus rien du tout, sans dire si
|
||||
// la compilation continuait ou pas. Or elle ne continue pas : le build est un
|
||||
// processus ENFANT, il meurt avec le service. On écrit donc l'état sur disque
|
||||
// pour pouvoir l'annoncer honnêtement au lieu de laisser l'utilisateur devant
|
||||
// un écran muet.
|
||||
package loki
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// L'ENTÊTE du job vit en base ; ses LIGNES restent un fichier, écrit en append.
|
||||
// Une compilation en produit des milliers : les faire transiter par une
|
||||
// transaction chacune coûterait un fsync par ligne.
|
||||
func lcJobLogPath() string { return filepath.Join(LokiHome(), "loki-build.log") }
|
||||
|
||||
// lcPersisted est la forme sur disque : l'entête du job, sans les lignes (elles
|
||||
// vivent dans le .log à côté, en append).
|
||||
type lcPersisted struct {
|
||||
Action string `json:"action"`
|
||||
Phase string `json:"phase"`
|
||||
Compiled int `json:"compiled"`
|
||||
Running bool `json:"running"`
|
||||
Err string `json:"error"`
|
||||
StartedAt int64 `json:"started_at"`
|
||||
EndedAt int64 `json:"ended_at"`
|
||||
OldCommit string `json:"old"`
|
||||
NewCommit string `json:"new"`
|
||||
}
|
||||
|
||||
var (
|
||||
lcLastSave time.Time
|
||||
lcRestoreOnce sync.Once
|
||||
)
|
||||
|
||||
// lcSave écrit l'état courant. Appelée sous lcMu. Les changements d'état (début,
|
||||
// fin, échec) sont écrits tout de suite ; la progression est throttlée à 2 s
|
||||
// pour ne pas marteler le disque pendant une compilation (des milliers de
|
||||
// lignes).
|
||||
func lcSave(force bool) {
|
||||
if lcCur == nil {
|
||||
return
|
||||
}
|
||||
if !force && time.Since(lcLastSave) < 2*time.Second {
|
||||
return
|
||||
}
|
||||
lcLastSave = time.Now()
|
||||
b, err := json.Marshal(lcPersisted{
|
||||
Action: lcCur.Action, Phase: lcCur.Phase, Compiled: lcCur.Compiled,
|
||||
Running: lcCur.Running, Err: lcCur.Err,
|
||||
StartedAt: lcCur.StartedAt, EndedAt: lcCur.EndedAt,
|
||||
OldCommit: lcCur.OldCommit, NewCommit: lcCur.NewCommit,
|
||||
})
|
||||
if err == nil {
|
||||
_ = putBytes(bkState, "build_job", b)
|
||||
}
|
||||
}
|
||||
|
||||
// lcSaveLine ajoute une ligne au journal sur disque (append, pas de réécriture).
|
||||
func lcSaveLine(line string) {
|
||||
f, err := os.OpenFile(lcJobLogPath(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _ = f.WriteString(line + "\n")
|
||||
_ = f.Close()
|
||||
}
|
||||
|
||||
// lcResetLog repart d'un journal vide au démarrage d'un nouveau job.
|
||||
func lcResetLog() { _ = os.Remove(lcJobLogPath()) }
|
||||
|
||||
// lcRestore recharge le dernier job connu au démarrage du process. Un job noté
|
||||
// « en cours » ne peut pas être le nôtre (on vient de démarrer) : sa
|
||||
// compilation est morte avec le process précédent, on le marque interrompu
|
||||
// plutôt que de le faire passer pour vivant ou de l'effacer en silence.
|
||||
func lcRestore() {
|
||||
var p lcPersisted
|
||||
if !getJSON(bkState, "build_job", &p) || p.Action == "" {
|
||||
return
|
||||
}
|
||||
j := &lcJob{
|
||||
Action: p.Action, Phase: p.Phase, Compiled: p.Compiled,
|
||||
Running: false, Err: p.Err,
|
||||
StartedAt: p.StartedAt, EndedAt: p.EndedAt,
|
||||
OldCommit: p.OldCommit, NewCommit: p.NewCommit,
|
||||
}
|
||||
if p.Running {
|
||||
j.Err = "interrompu : le service a redémarré pendant l'opération — relancez-la"
|
||||
j.Phase = "interrompu"
|
||||
j.EndedAt = time.Now().Unix()
|
||||
}
|
||||
if logb, err := os.ReadFile(lcJobLogPath()); err == nil {
|
||||
lines := strings.Split(strings.TrimRight(string(logb), "\n"), "\n")
|
||||
if len(lines) == 1 && lines[0] == "" {
|
||||
lines = nil
|
||||
}
|
||||
if len(lines) > lcMaxLines {
|
||||
j.base = len(lines) - lcMaxLines
|
||||
lines = lines[j.base:]
|
||||
}
|
||||
j.lines = lines
|
||||
}
|
||||
if p.Running {
|
||||
j.lines = append(j.lines, "✗ interrompu : le service a redémarré (la compilation ne survit pas au redémarrage)")
|
||||
}
|
||||
lcMu.Lock()
|
||||
if lcCur == nil {
|
||||
lcCur = j
|
||||
}
|
||||
lcMu.Unlock()
|
||||
if p.Running {
|
||||
lcMu.Lock()
|
||||
lcSave(true)
|
||||
lcMu.Unlock()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user