mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-23 23:18:13 +02:00
compaction: decider sur le VRAI decompte de tokens (ctx_used du client) au lieu de l'estimation
L'heuristique ~4 char/token sous-estimait le contexte (elle ne voit ni le prompt systeme injecte ni le gabarit de chat), donc le seuil 75% n'etait jamais franchi. Le client envoie desormais ctx_used (prompt_tokens_total deja affiche) ; repli sur l'estimation quand inconnu (terminal).
This commit is contained in:
1 parent
e6c0bbe3b7
commit
2daea32ade
4 files changed
+20
-5
No files matched your search
@@ -69,7 +69,7 @@ func cmdChat(args []string) error {
|
|||||||
caps := globalCaps()
|
caps := globalCaps()
|
||||||
// Compaction proactive (façon Hermes) : on résume les vieux tours quand
|
// Compaction proactive (façon Hermes) : on résume les vieux tours quand
|
||||||
// l'historique dépasse le seuil, au lieu d'imposer un /reset.
|
// l'historique dépasse le seuil, au lieu d'imposer un /reset.
|
||||||
if compacted, changed := MaybeCompact(context.Background(), msgs, caps); changed {
|
if compacted, changed := MaybeCompact(context.Background(), msgs, caps, 0); changed {
|
||||||
msgs = compacted
|
msgs = compacted
|
||||||
fmt.Println(dim("[contexte compacté pour tenir dans la fenêtre]"))
|
fmt.Println(dim("[contexte compacté pour tenir dans la fenêtre]"))
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-2
@@ -96,11 +96,22 @@ func estimateTokens(msgs []Message) int {
|
|||||||
// proactif. Renvoie l'historique (compacté ou inchangé) et un booléen indiquant
|
// proactif. Renvoie l'historique (compacté ou inchangé) et un booléen indiquant
|
||||||
// s'il a changé. À appeler sur l'historique BRUT (avant InjectSkills) pour que
|
// s'il a changé. À appeler sur l'historique BRUT (avant InjectSkills) pour que
|
||||||
// le résultat puisse être renvoyé au client sans le préfixe système injecté.
|
// le résultat puisse être renvoyé au client sans le préfixe système injecté.
|
||||||
func MaybeCompact(ctx context.Context, msgs []Message, caps Caps) ([]Message, bool) {
|
//
|
||||||
|
// knownTokens = taille RÉELLE du contexte au tour précédent (usage.prompt_tokens
|
||||||
|
// + tokens générés), telle que rapportée par llama.cpp et affichée par l'UI. On
|
||||||
|
// la préfère à estimateTokens() car cette dernière n'est qu'une heuristique et,
|
||||||
|
// surtout, ne « voit » pas le prompt système injecté (machine briefing) ni le
|
||||||
|
// gabarit de chat — donc elle sous-estime largement le vrai contexte. 0 = inconnu
|
||||||
|
// (clients sans compteur, ex. terminal) → repli sur l'estimation.
|
||||||
|
func MaybeCompact(ctx context.Context, msgs []Message, caps Caps, knownTokens int) ([]Message, bool) {
|
||||||
if !compactEnabled() {
|
if !compactEnabled() {
|
||||||
return msgs, false
|
return msgs, false
|
||||||
}
|
}
|
||||||
if estimateTokens(msgs) < int(float64(ctxWindow())*compactTriggerFrac) {
|
used := knownTokens
|
||||||
|
if used <= 0 {
|
||||||
|
used = estimateTokens(msgs)
|
||||||
|
}
|
||||||
|
if used < int(float64(ctxWindow())*compactTriggerFrac) {
|
||||||
return msgs, false
|
return msgs, false
|
||||||
}
|
}
|
||||||
return compactMessages(ctx, msgs, caps)
|
return compactMessages(ctx, msgs, caps)
|
||||||
|
|||||||
+1
-1
@@ -1445,7 +1445,7 @@ async function send(){
|
|||||||
try{
|
try{
|
||||||
const sys=(document.getElementById('sysprompt').value||'').trim();
|
const sys=(document.getElementById('sysprompt').value||'').trim();
|
||||||
const out = sys ? [{role:'system',content:sys}, ...msgs] : msgs;
|
const out = sys ? [{role:'system',content:sys}, ...msgs] : msgs;
|
||||||
const r=await jfetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({messages:out}),signal:abortCtrl.signal});
|
const r=await jfetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({messages:out,ctx_used:CTX_USED}),signal:abortCtrl.signal});
|
||||||
const reader=r.body.getReader(); const dec=new TextDecoder(); let buf='';
|
const reader=r.body.getReader(); const dec=new TextDecoder(); let buf='';
|
||||||
statsTimer = setInterval(updateStats, 250);
|
statsTimer = setInterval(updateStats, 250);
|
||||||
while(true){
|
while(true){
|
||||||
|
|||||||
@@ -783,6 +783,10 @@ type chatReq struct {
|
|||||||
Skills *bool `json:"skills"`
|
Skills *bool `json:"skills"`
|
||||||
// Override par requête de l'accès internet (outils web). nil = config machine.
|
// Override par requête de l'accès internet (outils web). nil = config machine.
|
||||||
Internet *bool `json:"internet"`
|
Internet *bool `json:"internet"`
|
||||||
|
// Taille réelle du contexte au tour précédent (usage.prompt_tokens + tokens
|
||||||
|
// générés), rapportée par le client qui l'affiche déjà. Sert à décider du
|
||||||
|
// compactage sur le VRAI décompte plutôt qu'une estimation. 0 = inconnu.
|
||||||
|
CtxUsed int `json:"ctx_used"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// sseHeartbeat garde la réponse SSE active en écrivant un commentaire (`: ping`,
|
// sseHeartbeat garde la réponse SSE active en écrivant un commentaire (`: ping`,
|
||||||
@@ -846,7 +850,7 @@ func runChatStream(ctx context.Context, body chatReq, emit func(map[string]any)
|
|||||||
// le résume AVANT le tour et on renvoie l'historique compacté au client pour
|
// le résume AVANT le tour et on renvoie l'historique compacté au client pour
|
||||||
// qu'il remplace le sien — évite de re-renvoyer tout à chaque tour.
|
// qu'il remplace le sien — évite de re-renvoyer tout à chaque tour.
|
||||||
hist := body.Messages
|
hist := body.Messages
|
||||||
if compacted, changed := MaybeCompact(ctx, hist, caps); changed {
|
if compacted, changed := MaybeCompact(ctx, hist, caps, body.CtxUsed); changed {
|
||||||
hist = compacted
|
hist = compacted
|
||||||
emit(map[string]any{"history_replace": hist})
|
emit(map[string]any{"history_replace": hist})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user