mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-22 14:38:12 +02:00
Reprise des commits 91f7f977 (PR #100 de Trombo38) et 1dfac0cf d'AJEAN. Une vidéo jointe (mp4, webm, mov, mkv, avi) part au modèle en partie input_video, et l'outil see_video charge une vidéo du disque, mais seulement si le moteur actif sait la lire : preset llama.cpp local avec projecteur (MMPROJ) et binaire dont l'aide annonce --video-fps (videoInputSupported, aide relue quand le binaire change, chemin des bibliothèques passé à la seule commande pour ne pas figer binHelp). Strata et les API externes ignoraient la partie en silence : la vidéo reste alors un fichier, annoncé avec la marche à suivre (ffmpeg puis see_image), et see_video n'est pas proposé. llama.cpp a --video-fps depuis la PR #24318 (fin août 2026) ; l'image server-cuda officielle, base du conteneur, installe ffmpeg. La consigne de repli dit quoi faire si ffmpeg manque (installation hors conteneur). Adapté à Loki : - la vidéo de see_video est toujours éphémère (jamais dans l'historique, KEEP_TURN_IMAGES compris) ; même ordre de refus que see_image, sonde /props en dernier, refus sur un poste distant ; - une vidéo jointe est rangée par référence (loki-vid:, LOKI_HOME/chatvid, chiffrée comme le fil), parties recopiées et non mutées comme les images ; retirée d'un fil rouvert (stripImageParts, videoLostMarker) et effacée par le ménage des médias ; - marqueur de perte à la compaction, élision dans l'export JSON, relais par l'API interne des outils (vision_video). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
3.4 KiB
Go
93 lines
3.4 KiB
Go
package loki
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// API interne d'exécution des outils (lot 6 de la migration Rust).
|
|
//
|
|
// Une boucle de chat écrite en Rust fera exécuter ses appels d'outils par ce
|
|
// process, le temps de porter les outils eux-mêmes (lot 7) : exactement la
|
|
// fonction qu'emploie runChatLoop (execTool), donc mêmes garde-fous (commande
|
|
// dangereuse, écriture hors du dossier de travail, cible distante…) et mêmes
|
|
// résultats.
|
|
//
|
|
// POST /internal/v1/tool/exec
|
|
// {"name":"bash","args":{"command":"ls"},"caps":{"Agent":true,"Code":false,…}}
|
|
// → {"label":"ls","result":"…","diff":[…],"added":N,"removed":N,"vision_img":{…},"ask":{…},
|
|
// "image":"…","shot":"…","relay":{…}}
|
|
//
|
|
// image, shot et relay sont ce que la boucle Go tire du résultat APRÈS
|
|
// l'exécution, et qui demande le disque ou le navigateur (toolStepExtras) :
|
|
// la capture à montrer dans la bulle, l'aperçu en direct du navigateur piloté,
|
|
// et le message `user` qui porte l'image au modèle (capture, see_image) —
|
|
// éphémère, à ne jamais ranger dans l'historique.
|
|
//
|
|
// Le contexte de la requête porte l'arrêt : si l'appelant coupe la connexion
|
|
// (bouton stop), la commande en cours est interrompue comme dans le Go.
|
|
|
|
// internalToolMaxBody : les arguments d'un appel (contenu d'un fichier écrit
|
|
// par l'agent compris) restent très en dessous.
|
|
const internalToolMaxBody = 32 << 20
|
|
|
|
func handleInternalToolExec(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
w.Header().Set("Allow", http.MethodPost)
|
|
sendJSON(w, http.StatusMethodNotAllowed, map[string]any{"error": "POST attendu"})
|
|
return
|
|
}
|
|
var in struct {
|
|
Name string `json:"name"`
|
|
Args map[string]any `json:"args"`
|
|
Caps Caps `json:"caps"`
|
|
}
|
|
raw, err := io.ReadAll(io.LimitReader(r.Body, internalToolMaxBody+1))
|
|
if err != nil || len(raw) > internalToolMaxBody || json.Unmarshal(raw, &in) != nil || in.Name == "" {
|
|
sendJSON(w, http.StatusBadRequest, map[string]any{"error": "appel d'outil illisible"})
|
|
return
|
|
}
|
|
if in.Args == nil {
|
|
in.Args = map[string]any{}
|
|
}
|
|
label := toolCallLabel(in.Name, in.Args)
|
|
res := execTool(r.Context(), in.Name, in.Args, label, in.Caps)
|
|
image, shot, relay := toolStepExtras(in.Name, label, res)
|
|
sendJSON(w, http.StatusOK, struct {
|
|
Label string `json:"label"`
|
|
toolExecResult
|
|
Image string `json:"image,omitempty"`
|
|
Shot string `json:"shot,omitempty"`
|
|
Relay *Message `json:"relay,omitempty"`
|
|
}{label, res, image, shot, relay})
|
|
}
|
|
|
|
// toolStepExtras : ce que runChatLoop tire d'un résultat d'outil après
|
|
// l'exécution — capture de web_screenshot pour la bulle, aperçu du navigateur
|
|
// après une action qui change la page (jamais après une erreur), image relayée
|
|
// au modèle (capture avec la vision active, ou see_image réussi).
|
|
func toolStepExtras(name, label string, res toolExecResult) (image, shot string, relay *Message) {
|
|
if !strings.HasPrefix(res.Result, "[erreur]") && cuAutoShotTool(name) {
|
|
shot = cuAutoShot()
|
|
}
|
|
if name == "web_screenshot" {
|
|
image = capturedRelPath(res.Result)
|
|
if image != "" {
|
|
if m, ok := screenshotImageMessage(image); ok {
|
|
relay = &m
|
|
}
|
|
}
|
|
}
|
|
if res.VisionImg != nil {
|
|
m := seeImageMessage(label, res.VisionImg)
|
|
relay = &m
|
|
}
|
|
if res.VisionVideo != nil {
|
|
m := seeVideoMessage(label, res.VisionVideo)
|
|
relay = &m
|
|
}
|
|
return image, shot, relay
|
|
}
|