mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-15 19:29:16 +02:00
Lot 6, étape 1, sans changement de comportement : le switch des outils de la boucle de chat (une cinquantaine d'outils) devient execTool, extrait tel quel ; seule la question « ask », qui émettait un événement, le rend désormais à l'appelant qui l'émet au même moment. C'est la frontière par laquelle une boucle de chat écrite en Rust fera exécuter ses outils par le Go tant qu'ils ne seront pas portés : API interne /internal/v1/tool/exec, même fonction, donc mêmes garde-fous (commande dangereuse refusée avant exécution, cible distante, écriture hors du dossier de travail) ; la coupure de la connexion interrompt la commande comme le bouton stop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
2.0 KiB
Go
63 lines
2.0 KiB
Go
package loki
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func appelOutil(t *testing.T, body string) map[string]any {
|
|
t.Helper()
|
|
w := appelOAIInterne(t, "POST", "/internal/v1/tool/exec", body)
|
|
if w.Code != 200 {
|
|
t.Fatalf("code %d : %s", w.Code, w.Body.String())
|
|
}
|
|
var got map[string]any
|
|
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return got
|
|
}
|
|
|
|
// Un outil exécuté par l'API interne rend exactement ce que la boucle Go
|
|
// aurait obtenu : même fonction (execTool), mêmes garde-fous.
|
|
func TestOutilInterneMemesResultats(t *testing.T) {
|
|
testHome(t)
|
|
t.Setenv(internalTokenEnv, jetonTest)
|
|
|
|
got := appelOutil(t, `{"name":"outil_qui_n_existe_pas","args":{}}`)
|
|
if got["result"] != "[erreur] outil inconnu: outil_qui_n_existe_pas" {
|
|
t.Fatalf("outil inconnu : %v", got)
|
|
}
|
|
|
|
// Commande catastrophique : refusée AVANT exécution, comme dans la boucle.
|
|
got = appelOutil(t, `{"name":"bash","args":{"command":"rm -rf /"},"caps":{"Agent":true}}`)
|
|
res, _ := got["result"].(string)
|
|
if want := refusedCommandResult(dangerousCommand("rm -rf /")); res != want {
|
|
t.Fatalf("garde-fou bash : %q, attendu %q", res, want)
|
|
}
|
|
if got["label"] != "rm -rf /" {
|
|
t.Fatalf("libellé : %v", got["label"])
|
|
}
|
|
|
|
// Question à l'utilisateur : l'événement remonte à l'appelant.
|
|
got = appelOutil(t, `{"name":"ask","args":{"question":"On continue ?","options":["oui","non"]},"caps":{"Agent":true}}`)
|
|
ask, _ := got["ask"].(map[string]any)
|
|
if r, _ := got["result"].(string); strings.HasPrefix(r, "[erreur]") || ask["question"] != "On continue ?" {
|
|
t.Fatalf("ask : %v", got)
|
|
}
|
|
}
|
|
|
|
func TestOutilInterneAppelIllisible(t *testing.T) {
|
|
testHome(t)
|
|
t.Setenv(internalTokenEnv, jetonTest)
|
|
for _, body := range []string{"", "{", `{"args":{}}`} {
|
|
if w := appelOAIInterne(t, "POST", "/internal/v1/tool/exec", body); w.Code != 400 {
|
|
t.Fatalf("%q : %d", body, w.Code)
|
|
}
|
|
}
|
|
if w := appelOAIInterne(t, "GET", "/internal/v1/tool/exec", ""); w.Code != 405 {
|
|
t.Fatalf("GET : %d", w.Code)
|
|
}
|
|
}
|