diff --git a/internal/loki/chat_conversation.go b/internal/loki/chat_conversation.go index a20119f..5260d3c 100644 --- a/internal/loki/chat_conversation.go +++ b/internal/loki/chat_conversation.go @@ -442,6 +442,9 @@ func (c *Conversation) generate(ctx context.Context, caps Caps, temperature floa if len(ev.ToolUsed.Diff) > 0 { tu["diff"] = ev.ToolUsed.Diff } + if ev.ToolUsed.Image != "" { + tu["image"] = ev.ToolUsed.Image + } c.appendDelta(epoch, map[string]any{"tool_used": tu}) case ev.NewHistory != nil: // Compaction faite en cours de tour : elle remplace la base au lieu de diff --git a/internal/loki/code_verify.go b/internal/loki/code_verify.go index ce1e94f..799b8fe 100644 --- a/internal/loki/code_verify.go +++ b/internal/loki/code_verify.go @@ -155,6 +155,9 @@ func (c *Conversation) forwardStream(ev StreamEvent, epoch int) { if len(ev.ToolUsed.Diff) > 0 { tu["diff"] = ev.ToolUsed.Diff } + if ev.ToolUsed.Image != "" { + tu["image"] = ev.ToolUsed.Image + } c.appendDelta(epoch, map[string]any{"tool_used": tu}) case ev.Ask != nil: c.appendDelta(epoch, map[string]any{"ask": ev.Ask}) diff --git a/internal/loki/llm_client.go b/internal/loki/llm_client.go index 38b99e3..484bc6f 100644 --- a/internal/loki/llm_client.go +++ b/internal/loki/llm_client.go @@ -370,6 +370,12 @@ type ToolUsedEvent struct { // Diff : lignes ajoutées/retirées quand l'outil a MODIFIÉ quelque chose // (edit, mem_add, mem_edit). L'UI les affiche en vert (+) et rouge (-). Diff []DiffLine + // Image : chemin (relatif au dossier de travail) d'une image PRODUITE par + // l'outil — aujourd'hui la capture de web_screenshot. L'UI l'affiche dans la + // bulle de l'outil. Sans ça, la capture n'apparaissait QUE si le modèle + // pensait à recopier la ligne markdown rendue par l'outil : un petit modèle + // l'oublie, et l'utilisateur ne voyait jamais l'image qu'il avait demandée. + Image string } // shownDisplayMax borne ce qu'un résultat d'outil occupe dans le FLUX vers l'UI. @@ -1210,7 +1216,13 @@ func runChat(ctx context.Context, messages []Message, temperature float64, caps if !strings.HasPrefix(result, "[erreur]") { doneCalls[callKey] = result } - cb(StreamEvent{ToolUsed: &ToolUsedEvent{Name: tc.Function.Name, Label: label, Result: shownResult(result), Done: true, Diff: diff}}) + // Capture d'écran : l'image part avec l'événement pour être + // affichée dans la bulle, quoi que le modèle en fasse ensuite. + shot := "" + if tc.Function.Name == "web_screenshot" { + shot = capturedRelPath(result) + } + cb(StreamEvent{ToolUsed: &ToolUsedEvent{Name: tc.Function.Name, Label: label, Result: shownResult(result), Done: true, Diff: diff, Image: shot}}) toolMsg := Message{Role: "tool", ToolCallID: tc.ID, Content: result} messages = append(messages, toolMsg) extra = append(extra, toolMsg) diff --git a/internal/loki/ui/index.html b/internal/loki/ui/index.html index d8b6dad..57ea365 100644 --- a/internal/loki/ui/index.html +++ b/internal/loki/ui/index.html @@ -2026,6 +2026,11 @@ html[data-files="1"] #files-btn{color:var(--accent)} .askopts button{border:1px solid var(--accent);background:transparent;color:var(--accent);border-radius:3px;padding:4px 10px;font-size:13px;cursor:pointer;font-family:inherit} .askopts button:hover:not(:disabled){background:var(--accent-bg)} .askopts button:disabled{opacity:.5;cursor:default} + +/* Capture d'écran rendue dans la bulle de l'outil (web_screenshot) : l'image + ne dépend plus du modèle qui pense — ou non — à la recopier dans sa réponse. */ +.tool-shot{margin-top:8px} +.tool-shot img{max-width:100%;max-height:420px;border:1px solid var(--border);border-radius:4px;display:block;cursor:zoom-in}