mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-12 01:37:06 +02:00
Repris d'AJEAN 0.15.8, adapté : chez nous le contexte projet n'est jamais persisté, il est injecté à chaque tour sous forme de messages système préfixés — ce qui rend le déplacement trivial et sans migration. LE PROBLÈME Sur un modèle hybride (Qwen3.5 et suivants, couches récurrentes), llama.cpp ne peut reprendre un prompt déjà calculé qu'à un point de sauvegarde, et il n'en pose qu'au DÉBUT d'un message utilisateur. La description du projet, l'index mémoire et l'index des trackers étaient fusionnés dans le bloc système : deux projets divergeaient AVANT le premier point de reprise, et le premier message après un changement de projet recalculait tout (l'amont mesure ~8 s pour 5 000 tokens sur un 27B). LE CORRECTIF normalizeSystemMessages sort ces messages (isProjectSystem, par leurs préfixes) du bloc système et les place, dans un bloc <project_context>, en tête du PREMIER message utilisateur de la séquence envoyée. Le système commun reste identique d'un projet à l'autre, donc en cache. L'historique persisté et l'affichage ne changent pas (copie, entrée non mutée — testé). InjectSkills ne fusionne plus son préambule DANS un message projet placé en tête (cas d'un preset sans prompt système) : il y aurait perdu son préfixe et serait resté dans le bloc commun. Le gain ne vaut qu'entre projets de même mode mémoire : la consigne mémoire fait partie du système commun. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
54 lines
2.0 KiB
Go
54 lines
2.0 KiB
Go
package loki
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Le contexte PROJET quitte le bloc système pour la tête du 1er message user :
|
|
// le système commun reste identique d'un projet à l'autre (cache de prompt), et
|
|
// l'historique d'origine n'est pas muté.
|
|
func TestNormalizeMovesProjectContextToFirstUser(t *testing.T) {
|
|
in := []Message{
|
|
{Role: "system", Content: "Préambule commun"},
|
|
{Role: "system", Content: projectContextPrefix + " — projet A"},
|
|
{Role: "system", Content: memIndexPrefix + " for project A"},
|
|
{Role: "user", Content: "bonjour"},
|
|
{Role: "assistant", Content: "salut"},
|
|
{Role: "user", Content: "suite"},
|
|
}
|
|
ref := append([]Message(nil), in...)
|
|
out := normalizeSystemMessages(in)
|
|
if len(out) != 4 || out[0].Content != "Préambule commun" {
|
|
t.Fatalf("système commun attendu seul en tête : %#v", out)
|
|
}
|
|
first, _ := out[1].Content.(string)
|
|
if !strings.HasPrefix(first, "<project_context>\n"+projectContextPrefix) ||
|
|
!strings.Contains(first, memIndexPrefix) || !strings.HasSuffix(first, "bonjour") {
|
|
t.Fatalf("contexte projet absent du 1er message user : %q", first)
|
|
}
|
|
if out[3].Content != "suite" {
|
|
t.Fatal("seul le PREMIER message user reçoit le contexte")
|
|
}
|
|
if !reflect.DeepEqual(in, ref) {
|
|
t.Fatal("l'entrée a été mutée (interdit)")
|
|
}
|
|
}
|
|
|
|
// Sans prompt de preset, le premier système est un message projet : le préambule
|
|
// ne doit pas l'absorber (il perdrait son préfixe et resterait dans le système).
|
|
func TestInjectSkillsKeepsProjectMessageSeparate(t *testing.T) {
|
|
in := []Message{
|
|
{Role: "system", Content: projectContextPrefix + " — projet A"},
|
|
{Role: "user", Content: "bonjour"},
|
|
}
|
|
out := normalizeSystemMessages(InjectSkills(in, Caps{Agent: true}))
|
|
if s, _ := out[0].Content.(string); out[0].Role != "system" || strings.Contains(s, projectContextPrefix) {
|
|
t.Fatalf("le contexte projet ne doit pas rester dans le système : %#v", out[0])
|
|
}
|
|
if u, _ := out[1].Content.(string); !strings.Contains(u, projectContextPrefix) {
|
|
t.Fatalf("contexte projet attendu dans le 1er message user : %#v", out[1])
|
|
}
|
|
}
|