From 3dff7e0e042274e02776039c20ba7f1704bd1bac Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sun, 4 Oct 2026 01:54:19 +0200 Subject: [PATCH] =?UTF-8?q?Flux=20:=20corrections=20de=20relecture=20?= =?UTF-8?q?=E2=80=94=20le=20=C2=AB=20+N=20=C2=BB=20d'une=20=C3=A9criture?= =?UTF-8?q?=20ne=20recompte=20plus=20tout=20le=20corps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chaque événement de frappe (un par ligne) appelait bodyLineCount sur le corps ENTIER pour remplir body_lines : un reste de coût quadratique, léger mais inutile, puisque argPreview tient déjà le compte des sauts de ligne. - argPreview.lineCount : même règle que bodyLineCount (saut de ligne final sans ligne de plus), en temps constant. - Le test de décodage par morceaux vérifie l'égalité avec bodyLineCount à chaque préfixe (fuzz compris). Co-Authored-By: Claude Opus 5.5 --- internal/loki/llm_client.go | 17 ++++++++++++++++- internal/loki/stream_hotpath_test.go | 3 +++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/loki/llm_client.go b/internal/loki/llm_client.go index edfe99d..3e44a91 100644 --- a/internal/loki/llm_client.go +++ b/internal/loki/llm_client.go @@ -822,6 +822,21 @@ func (a *argPreview) value() string { return a.val.String() } // done : guillemet fermant reçu. func (a *argPreview) done() bool { return a.phase == 4 } +// lineCount : bodyLineCount(value()) sans relire la valeur. Recompter les '\n' +// de tout le corps à chaque ligne émise restait quadratique. +func (a *argPreview) lineCount() int { + n := a.val.Len() + switch { + case n == 0: + return 0 + case a.val.String()[n-1] != '\n': + return a.lines + 1 + case n == 1: + return 0 // "\n" seul : bodyLineCount le compte vide + } + return a.lines +} + // bodyTailLines / bodyTailBytes : ce qu'un événement de frappe montre du corps. const ( bodyTailLines = 40 @@ -1667,7 +1682,7 @@ func runChatTools(ctx context.Context, messages []Message, tools []Tool, tempera tu := &ToolUsedEvent{Name: cur.Function.Name, Label: lastPreview, Typing: true} if full != "" { tu.Body, tu.BodyTail = bodyTail(full) - tu.BodyLines = bodyLineCount(full) + tu.BodyLines = bodyDec.lineCount() } if !scb(StreamEvent{ToolUsed: tu}) { aborted = true diff --git a/internal/loki/stream_hotpath_test.go b/internal/loki/stream_hotpath_test.go index 900ee89..c2ffb75 100644 --- a/internal/loki/stream_hotpath_test.go +++ b/internal/loki/stream_hotpath_test.go @@ -54,6 +54,9 @@ func checkArgPreview(t *testing.T, args, key string, chunks []string) { if n := strings.Count(a.value(), "\n"); n != a.lines { t.Fatalf("lines=%d, la valeur en compte %d", a.lines, n) } + if n := bodyLineCount(a.value()); n != a.lineCount() { + t.Fatalf("lineCount=%d, bodyLineCount=%d (valeur %q)", a.lineCount(), n, a.value()) + } } }