mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-17 20:26:35 +02:00
Contexte : le raisonnement jamais renvoyé ne compte plus, et la compaction ne part plus trop tôt
CtxUsed valait usage.prompt_tokens + généré, raisonnement compris. Or Loki ne renvoie jamais ce raisonnement au modèle (Message n'a pas de champ pour lui) : la requête suivante pesait 1 à 8 k jetons de moins que le compte, et la compaction, qui perd de l'information, partait vers 44-47 k au lieu de 49 k sur une fenêtre de 65 k. Rien ne change dans les requêtes : seul le compte qui décide de compacter. - Comptage : seulement les morceaux reasoning_content séparés par le llama-server local, remis à zéro à chaque tentative. Le <think> découpé chez nous reste dans le message renvoyé pendant le tour, il n'est pas retiré ; l'API externe garde l'ancien calcul. Un morceau vaut au plus un jeton : on ne peut que sous-estimer, le sens sans danger. - Début de tour : le message utilisateur (et la file) arrivé depuis la dernière mesure s'ajoute en estimation, comme en cours de tour. - Vérificateur : sa trace isolée écrasait CtxUsed avec sa petite taille, et la fin de tour décidait sur ce chiffre-là. Plus maintenant ; la jauge de l'UI ne bouge pas non plus et suit le même calcul que le serveur. - Marge de génération : le compte gonflé offrait une marge par accident. Elle est remplacée par une vraie garde — compacter si 1,2 × la plus longue génération récente ne tient plus dans la fenêtre. Plancher et marge seuls ne devancent jamais le seuil de 75 %. - Fenêtre pleine en plein raisonnement (finish « length » au-delà du seuil) : compaction puis étape rejouée, au lieu du nudge « arrête de raisonner ». - Journal [ctx] : estimation contre compte réel au-delà de 2 % d'écart, chaque « length » et chaque nudge, pour vérifier qu'ils n'augmentent pas. - Le seuil reste à 75 % : la réserve fixe proposée (toolResultMax/3 + 6 k) laissait trop peu de place sans budget de raisonnement ni max_tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
18a723a671
commit
d6023ef8a0
7 files changed
+514
-25
No files matched your search
@@ -116,7 +116,7 @@ func (c *Conversation) runVerifyPass(ctx context.Context, caps Caps, temperature
|
||||
// dans le moteur aussi : effacé à la fin de la passe (llm_slots.go).
|
||||
defer engineSideJob()()
|
||||
_, _ = runChat(withPerfKind(ctx, perfVerify), msgs, temperature, vcaps, func(ev StreamEvent) bool {
|
||||
c.forwardStream(ev, epoch)
|
||||
c.forwardStream(ev, epoch, true)
|
||||
return true
|
||||
})
|
||||
}
|
||||
@@ -165,7 +165,11 @@ func (c *Conversation) runBuilderTurn(ctx context.Context, caps Caps, temperatur
|
||||
final = append([]Message{{Role: "system", Content: sp}}, final...)
|
||||
}
|
||||
var content strings.Builder
|
||||
sawUsage := false
|
||||
extra, _ := runChat(ctx, InjectSkills(final, caps), temperature, caps, func(ev StreamEvent) bool {
|
||||
if ev.Stats != nil && ev.Stats.PromptTokensTotal > 0 {
|
||||
sawUsage = true
|
||||
}
|
||||
if ev.Content != "" {
|
||||
content.WriteString(ev.Content)
|
||||
}
|
||||
@@ -175,7 +179,7 @@ func (c *Conversation) runBuilderTurn(ctx context.Context, caps Caps, temperatur
|
||||
if ev.Ask != nil {
|
||||
asked = true
|
||||
}
|
||||
c.forwardStream(ev, epoch)
|
||||
c.forwardStream(ev, epoch, false)
|
||||
return true
|
||||
})
|
||||
c.mu.Lock()
|
||||
@@ -184,6 +188,9 @@ func (c *Conversation) runBuilderTurn(ctx context.Context, caps Caps, temperatur
|
||||
if s := content.String(); strings.TrimSpace(s) != "" {
|
||||
c.Messages = append(c.Messages, Message{Role: "assistant", Content: s})
|
||||
}
|
||||
if sawUsage {
|
||||
c.ctxUsedLen = len(c.Messages) // même règle que generate
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
c.persist()
|
||||
@@ -193,7 +200,13 @@ func (c *Conversation) runBuilderTurn(ctx context.Context, caps Caps, temperatur
|
||||
// forwardStream relaie les événements d'un runChat secondaire (vérification,
|
||||
// correction) vers le journal d'affichage — même mapping que generate(), sans
|
||||
// la gestion de compaction (ces passes n'en déclenchent pas : contexte court).
|
||||
func (c *Conversation) forwardStream(ev StreamEvent, epoch int) {
|
||||
//
|
||||
// isolated : la passe tourne sur sa propre trace (vérificateur), PAS sur
|
||||
// l'historique de la discussion. Ses comptes ne disent rien du contexte de
|
||||
// celle-ci : ils écrasaient CtxUsed avec la petite taille du vérificateur, et
|
||||
// le contrôle de fin de tour — puis celui du tour suivant — décidaient sur ce
|
||||
// chiffre-là. La jauge de l'UI ne doit pas bouger non plus (ctx_isolated).
|
||||
func (c *Conversation) forwardStream(ev StreamEvent, epoch int, isolated bool) {
|
||||
switch {
|
||||
case ev.Err != nil:
|
||||
c.appendDelta(epoch, map[string]any{"error": ev.Err.Error()})
|
||||
@@ -223,13 +236,21 @@ func (c *Conversation) forwardStream(ev StreamEvent, epoch int) {
|
||||
case ev.Ask != nil:
|
||||
c.appendDelta(epoch, map[string]any{"ask": ev.Ask})
|
||||
case ev.Stats != nil:
|
||||
if ev.Stats.PromptTokensTotal > 0 {
|
||||
c.mu.Lock()
|
||||
if c.epoch == epoch {
|
||||
c.CtxUsed = ev.Stats.PromptTokensTotal + ev.Stats.GenTokens
|
||||
}
|
||||
c.mu.Unlock()
|
||||
if isolated {
|
||||
c.appendDelta(epoch, map[string]any{"stats": ev.Stats, "ctx_isolated": true})
|
||||
break
|
||||
}
|
||||
local := !externalActive()
|
||||
c.mu.Lock()
|
||||
if c.epoch == epoch {
|
||||
if ev.Stats.PromptTokensTotal > 0 {
|
||||
c.CtxUsed = ev.Stats.ctxAfter()
|
||||
}
|
||||
if local {
|
||||
c.genPeak = max(c.genPeak, ev.Stats.GenTokens)
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
c.appendDelta(epoch, map[string]any{"stats": ev.Stats})
|
||||
case ev.DropReasoning:
|
||||
c.appendDelta(epoch, map[string]any{"drop_reasoning": true})
|
||||
|
||||
Reference in new issue
Block a user