mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-22 22:48:11 +02:00
Compaction : COMPACT_CONTINUATION résume dans le prolongement du prompt en cache, en opt-in
Le résumé d'une compaction part aujourd'hui dans une requête à part — prompt du résumeur et transcription des anciens tours — que le moteur local calcule à froid : des dizaines de secondes sur un 27B, des minutes sur un MoE, à chaque compaction. Nouvelle clé COMPACT_CONTINUATION (off par défaut) : quand le slot porte encore le prompt de la discussion, la requête de résumé est celle du tour telle qu'elle est partie, suivie d'une seule demande de résumé ; le moteur ne calcule qu'elle. Sans la clé, requêtes de compaction identiques à l'octet près (prompt du résumeur comparé à une copie figée, testé). - vue MODÈLE pour tout ce qui est rangé : bornes, archives recall, demande réinjectée, garantie de réduction et sortie ne changent pas ; la vue d'envoi (turnViewDry, wireMessages, buildChatPayload) ne sert qu'à la requête, rien d'injecté n'entre dans l'historique (testé) - frontière recalée sur la vue envoyée (messages non système un pour un, vérifiés rôle par rôle), désignée par le nombre de messages gardés et le début du premier ; tout résumer avant, dater l'avancement à la frontière - mêmes règles de résumé (+ mode Code), même budget, température 0.2 sans l'échantillonnage du preset, enable_thinking=false ajouté aux arguments du tour, reasoning_effort du tour, tool_choice « none », sans flux - messages utilisateur de fin hors de la vue : pas en cache, et pas deux `user` d'affilée pour les gabarits stricts - slot vérifié : tampon posé par une étape de tour acceptée (llm_slots.go, sous le verrou du compteur en vol), perdu dès qu'une autre requête part — vérification, sous-agent, tâche, préchauffage, résumé, bench, /v1 — ou que le modèle ou la fenêtre changent ; relu à l'envoi - marge en jetons réels (dernier compte + non vu + demande + budget + 5 %) - repli sur la transcription au moindre écart : refus du moteur, réseau, appel d'outil émis (tool_calls décodés) ou écrit en texte, raisonnement seul, résumé vide ; refus du gabarit ou appel d'outil = suspendue pour le modèle jusqu'au redémarrage, deux échecs de suite aussi - avec la clé : pas de résumé quand même un vide ne réduirait pas de 20 % (borne exacte, avant tout archivage) ; après un refus faute de réduction, pas de nouvel essai avant +10 % de contexte, jamais à 90 % de la fenêtre, oublié quand le contexte baisse - réactif, fenêtre pleine, bouton manuel, tâches, sous-agents, terminal, preset externe : chemin d'avant ; télémétrie kind=compact avec la discussion Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
eb58f7982f
commit
2097c9f915
11 files changed
+1342
-61
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -58,6 +59,57 @@ var engineGate struct {
|
||||
mu sync.Mutex
|
||||
inflight int
|
||||
served uint64 // réponses 200 du moteur obtenues par runChat ou le bench
|
||||
// seq numérote les requêtes de Loki vers le moteur local, préchauffage
|
||||
// compris. main : la dernière, quand c'était une étape d'un tour de
|
||||
// discussion acceptée par le moteur — le slot porte alors, selon toute
|
||||
// vraisemblance, le prompt de cette discussion (COMPACT_CONTINUATION).
|
||||
seq uint64
|
||||
main engineMainStamp
|
||||
}
|
||||
|
||||
// engineMainStamp : la requête d'un tour de discussion que le slot porte.
|
||||
// Modèle et fenêtre en font partie : un moteur relancé sur un autre modèle ou
|
||||
// une autre fenêtre a un slot vide.
|
||||
type engineMainStamp struct {
|
||||
seq uint64
|
||||
conv, model string
|
||||
window int
|
||||
}
|
||||
|
||||
// engineMainNow : modèle et fenêtre du moteur, pour le tampon.
|
||||
func engineMainNow() (model string, window int) {
|
||||
return filepath.Base(strings.TrimSpace(ReadConfig()["MODEL"])), ctxWindow()
|
||||
}
|
||||
|
||||
// engineMarkMain : la requête seq, étape d'un tour de la discussion conv, a été
|
||||
// acceptée par le moteur. Sans effet si une autre requête est partie depuis.
|
||||
func engineMarkMain(seq uint64, conv string) {
|
||||
if conv == "" {
|
||||
return
|
||||
}
|
||||
model, window := engineMainNow()
|
||||
engineGate.mu.Lock()
|
||||
defer engineGate.mu.Unlock()
|
||||
if engineGate.seq == seq {
|
||||
engineGate.main = engineMainStamp{seq: seq, conv: conv, model: model, window: window}
|
||||
}
|
||||
}
|
||||
|
||||
// engineSlotHolds : le slot porte-t-il plausiblement le prompt de conv ? Oui si
|
||||
// la dernière requête de Loki vers le moteur était une étape d'un de ses tours,
|
||||
// acceptée, sur le même modèle et la même fenêtre. Un vérificateur, un
|
||||
// sous-agent, une tâche, un préchauffage, un résumé, un bench, un client /v1 ou
|
||||
// une autre discussion passés depuis : non. Un client qui parlerait au moteur
|
||||
// sans passer par Loki échappe à ce compte — au pire, un recalcul.
|
||||
func engineSlotHolds(conv string) bool {
|
||||
if conv == "" {
|
||||
return false
|
||||
}
|
||||
model, window := engineMainNow()
|
||||
engineGate.mu.Lock()
|
||||
defer engineGate.mu.Unlock()
|
||||
m := engineGate.main
|
||||
return m.seq != 0 && m.seq == engineGate.seq && m.conv == conv && m.model == model && m.window == window
|
||||
}
|
||||
|
||||
// engineServed note qu'une requête de Loki a été acceptée par le moteur local :
|
||||
@@ -92,11 +144,20 @@ func engineRequestStart() func() { return engineRequestStartKeep(nil) }
|
||||
// Décidé sous le verrou du compteur : aucun préchauffage ne peut démarrer entre
|
||||
// la décision et le compte.
|
||||
func engineRequestStartKeep(keep func(*prewarmRun) bool) func() {
|
||||
end, _ := engineRequestBegin(keep)
|
||||
return end
|
||||
}
|
||||
|
||||
// engineRequestBegin : engineRequestStartKeep, avec le numéro de la requête
|
||||
// (engineMarkMain).
|
||||
func engineRequestBegin(keep func(*prewarmRun) bool) (func(), uint64) {
|
||||
engineGate.mu.Lock()
|
||||
if p := prewarmCur; p != nil && (keep == nil || !keep(p)) {
|
||||
prewarmDropLocked(p)
|
||||
}
|
||||
engineGate.inflight++
|
||||
engineGate.seq++
|
||||
seq := engineGate.seq
|
||||
engineGate.mu.Unlock()
|
||||
var once sync.Once
|
||||
return func() {
|
||||
@@ -105,7 +166,7 @@ func engineRequestStartKeep(keep func(*prewarmRun) bool) func() {
|
||||
engineGate.inflight--
|
||||
engineGate.mu.Unlock()
|
||||
})
|
||||
}
|
||||
}, seq
|
||||
}
|
||||
|
||||
// slotsWrite : une action sur /slots (save, restore, erase) plutôt qu'une
|
||||
|
||||
Reference in new issue
Block a user