mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Repris du buildAgentNudge d'OpenFox (2.0.133). La passe de vérification partait à chaque fin de tour de build, y compris quand Qwen s'arrêtait sur « ensuite je vais… » ou venait de poser une question avec ask. Chaque passe inutile coûte un prefill complet, évince le cache KV du fil, et produit des « failed » qui ne disent que « pas encore fait » — en courant par-dessus la question restée sans réponse. - Nouveau statut « completed » : le builder marque un critère fait une fois vérifié par lui-même. Seule la vérification marque passed/failed. - Fin de tour avec des critères encore ouverts : le builder est relancé sur ces critères (deux fois au plus) avant toute vérification. - Fin de tour sur une question (ask) : pas de vérification, ni de correction, avant la réponse de l'utilisateur ; idem si le builder pose une question pendant une correction. - Un id de critère écrit entre guillemets (« "2" », « "#2" ») vise bien le bon critère au lieu de #0. - Le panneau des critères montre « completed » (◐). - Test de non-régression de la passe de vérification (code_verify_test.go). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
2.5 KiB
Go
71 lines
2.5 KiB
Go
package loki
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
)
|
|
|
|
// fauxMoteurOutil : un llama-server de comédie qui, à la PREMIÈRE requête,
|
|
// répond par un appel d'outil (name/args), puis par un texte à la suivante.
|
|
func fauxMoteurOutil(t *testing.T, name, args string) (port string, requêtes *int32) {
|
|
t.Helper()
|
|
var n int32
|
|
esc := strings.ReplaceAll(args, `"`, `\"`)
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
w.WriteHeader(200)
|
|
if atomic.AddInt32(&n, 1) == 1 {
|
|
_, _ = w.Write([]byte(`data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":"c1","type":"function","function":{"name":"` + name + `","arguments":"` + esc + `"}}]},"finish_reason":null}]}` + "\n\n"))
|
|
_, _ = w.Write([]byte(`data: {"choices":[{"delta":{},"finish_reason":"tool_calls"}]}` + "\n\n"))
|
|
} else {
|
|
_, _ = w.Write([]byte(sseChunk("Verdict enregistré.")))
|
|
_, _ = w.Write([]byte(`data: {"choices":[{"delta":{},"finish_reason":"stop"}]}` + "\n\n"))
|
|
}
|
|
_, _ = w.Write([]byte("data: [DONE]\n\n"))
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
u, _ := url.Parse(srv.URL)
|
|
return u.Port(), &n
|
|
}
|
|
|
|
// La passe de vérification (Role=verifier) doit pouvoir marquer un critère
|
|
// passed via l'outil criteria — c'est tout son rôle. Vécu : « en mode code,
|
|
// le système de vérification ne coche plus les critères ».
|
|
func TestPasseDeVerificationCocheLesCriteres(t *testing.T) {
|
|
withWorkspace(t)
|
|
id := convEnsureActive()
|
|
toolCriteria(map[string]any{"action": "add", "texts": []any{"le build compile"}}, false)
|
|
|
|
port, n := fauxMoteurOutil(t, "criteria", `{"action":"set","id":1,"status":"passed"}`)
|
|
if err := SetConfigKey("PORT", port); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
caps := Caps{Agent: true, Code: true, Role: "verifier"}
|
|
var tools []string
|
|
_, err := runChat(context.Background(), []Message{
|
|
{Role: "system", Content: rolePrompt("verifier")},
|
|
{Role: "user", Content: "Verify."},
|
|
}, 0.2, caps, func(ev StreamEvent) bool {
|
|
if ev.ToolUsed != nil && ev.ToolUsed.Done {
|
|
tools = append(tools, ev.ToolUsed.Name+" → "+ev.ToolUsed.Result)
|
|
}
|
|
return true
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if *n < 2 {
|
|
t.Fatalf("%d requête(s) au moteur : l'appel d'outil n'a pas été suivi d'un second tour", *n)
|
|
}
|
|
list := critList(id)
|
|
if len(list) != 1 || list[0].Status != "passed" {
|
|
t.Fatalf("critère non coché par le vérificateur : %+v — outils : %v", list, tools)
|
|
}
|
|
}
|