mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-19 05:07:20 +02:00
Repris d'AJEAN 0.15.7 (chat_images.go et son test), adapté. Une image jointe (ou une capture de web_screenshot) vivait en base64 dans les messages de la conversation, et persist() réécrit la conversation ENTIÈRE à chaque fin de tour : une photo de téléphone, c'étaient des Mo remis sur disque à chaque réponse. Elle est désormais rangée une fois sous $LOKI_HOME/chatimg/<empreinte>.<ext> (hors du dossier de travail, que l'IA peut vider) ; l'historique n'en garde que « loki-img:<nom> ». Juste avant l'envoi, expandImageRefs remet les octets exacts (cache mémoire borné à 64 Mo) : pour le modèle, rien ne change. DEUX ÉCARTS AVEC L'AMONT - Copie à l'écriture : l'amont remplace l'URL en place. Chez nous, persist peut tourner pendant qu'un runChat sérialise ces mêmes maps (compactage en vol, bascule de projet) — « concurrent map read and map write », fatal. Les parties modifiées sont donc recopiées. - Le rechargement reste éphémère : stripImageParts retire toujours les images d'une conversation rouverte (garde-fou contre un modèle sans vision qui lirait le base64 comme du texte). L'amont, lui, les garde ; on pourra le suivre plus tard si l'on veut. Pas de migration des archives : une conversation rouverte est allégée par stripImageParts, comme avant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
1.5 KiB
Go
47 lines
1.5 KiB
Go
package loki
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Une image en base64 part sur disque et revient identique à l'envoi au modèle ;
|
|
// l'historique ne garde qu'une référence de quelques octets.
|
|
func TestImageRefsRoundTrip(t *testing.T) {
|
|
testHome(t)
|
|
raw := []byte(strings.Repeat("\x89PNG-fake-image-bytes", 5000))
|
|
url := "data:image/png;base64," + base64.StdEncoding.EncodeToString(raw)
|
|
msgs := []Message{
|
|
{Role: "user", Content: []map[string]any{
|
|
{"type": "text", "text": "regarde"},
|
|
{"type": "image_url", "image_url": map[string]any{"url": url}},
|
|
}},
|
|
{Role: "assistant", Content: "vu"},
|
|
}
|
|
// Relu depuis le JSON (forme []any), comme après un redémarrage.
|
|
var reread []Message
|
|
b, _ := json.Marshal(msgs)
|
|
_ = json.Unmarshal(b, &reread)
|
|
|
|
for _, set := range [][]Message{msgs, reread} {
|
|
if !hasInlineImages(set) || !refImagesInMessages(set) || hasInlineImages(set) {
|
|
t.Fatal("l'image aurait dû être rangée par référence")
|
|
}
|
|
js, _ := json.Marshal(set)
|
|
if len(js) > 500 || !strings.Contains(string(js), imgRefScheme) {
|
|
t.Fatalf("historique encore lourd : %d octets", len(js))
|
|
}
|
|
out := expandImageRefs(set)
|
|
_, got := partImageURL(contentParts(out[0].Content)[1])
|
|
if got != url {
|
|
t.Fatal("l'image renvoyée au modèle diffère de l'originale")
|
|
}
|
|
// L'historique lui-même n'est pas modifié par l'expansion.
|
|
if _, u := partImageURL(contentParts(set[0].Content)[1]); !strings.HasPrefix(u, imgRefScheme) {
|
|
t.Fatal("expandImageRefs ne doit pas toucher l'historique")
|
|
}
|
|
}
|
|
}
|