Files
Loki/internal/loki/tool_results_test.go
T
MichaelandClaude Opus 5.5 ae0e181711 Outils : aperçu dans le flux, « voir plus » à la demande, coupes annoncées
Repris d'AJEAN 0.15.1 → 0.15.7 (tool_results.go adapté : pas de
chiffrement chez nous).

VOIR PLUS
Chaque résultat d'outil partait en entier dans le flux (jusqu'à 12 000
caractères) et dans le journal rejoué à chaque ouverture : un long fil
d'agent en transportait des centaines. Le flux ne porte plus qu'un aperçu
de 1 600 caractères, la taille réelle (le « ~N tok » de la bulle reste
juste) et un id. Le résultat complet est rangé dans le bucket `toolres`,
sous « <discussion>.<aléa> » — un id propre, pas le tool_call_id que
certains parseurs recyclent. « voir plus » le charge au clic via
/api/chat/tool-result et lève le plafond de hauteur du bloc ; il se range
avec « copier » dans une barre au coin du résultat. Les résultats partent
avec leur discussion (convDelete) ; les orphelins sont nettoyés toutes les
200 écritures.

COUPES ANNONCÉES
- Terminal : une sortie de plus de 8 000 caractères était coupée en
  silence ; le modèle croyait tout voir. Elle commence désormais par
  « [sortie tronquée : N caractères au total, seuls les 8000 derniers sont
  gardés] ».
- MCP : l'amont transmet désormais la réponse en entier. On s'en approche
  sans renoncer au garde-fou (65k de contexte) : le plafond suit la
  fenêtre (CTX caractères, ≈ un quart de la fenêtre, jamais sous les
  12 000 d'avant), et la coupe dit la taille réelle.

Vérifié dans le navigateur avec un faux moteur qui appelle bash : aperçu
de 1 600 caractères, « voir plus » charge les 8 099, mention de coupe en
tête.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:08:46 +02:00

43 lines
1.5 KiB
Go

package loki
import (
"strings"
"testing"
)
// Un résultat coupé est rangé à part, relu en entier, et part avec sa discussion.
func TestToolResultsSaveLoadDelete(t *testing.T) {
testHome(t)
_ = putStr(bkChat, ckActive, "c42")
long := strings.Repeat("ligne de sortie\n", 500)
ev := fillToolResult(&ToolUsedEvent{Name: "bash", Done: true}, long)
if ev.ResultID == "" || !strings.HasPrefix(ev.ResultID, "c42.") {
t.Fatalf("id attendu préfixé par la discussion, got %q", ev.ResultID)
}
if len([]rune(ev.Result)) != toolPreviewChars || ev.ResultChars != len([]rune(long)) {
t.Fatalf("aperçu %d car., taille %d — attendu %d et %d", len([]rune(ev.Result)), ev.ResultChars, toolPreviewChars, len([]rune(long)))
}
if got, ok := loadToolResult(ev.ResultID); !ok || got != long {
t.Fatal("le résultat complet doit être relisible")
}
short := fillToolResult(&ToolUsedEvent{}, "ok")
if short.ResultID != "" || short.Result != "ok" {
t.Fatal("un résultat court part entier, sans id")
}
deleteToolResultsFor("c42")
if _, ok := loadToolResult(ev.ResultID); ok {
t.Fatal("les résultats doivent partir avec leur discussion")
}
}
// Une sortie de commande coupée le DIT, avec sa taille réelle.
func TestTailOutputAnnouncesCut(t *testing.T) {
if tailOutput("court") != "court" {
t.Fatal("une sortie courte reste intacte")
}
out := tailOutput(strings.Repeat("x", toolMaxOutput+10))
if !strings.HasPrefix(out, "[sortie tronquée : ") || !strings.Contains(out, "au total") {
t.Fatalf("mention de coupe absente : %q", out[:60])
}
}