mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-23 23:18:13 +02:00
Navigateur piloté, images redressées : reprises d'AJEAN
L'amont (AJEAN 0.15) donne à l'IA le contrôle d'un navigateur ; Loki embarquait déjà un Chromium pour `web_screenshot` sans jamais le piloter. Contrôle du navigateur (computer_use.go, computer_cdp.go, browser_grid.go, portés d'AJEAN) : l'IA ouvre une page en CDP, en reçoit les éléments interactifs NUMÉROTÉS et agit par numéro (browser_open/snapshot/find/click/ type/key/scroll). Aucune vision requise — un petit modèle texte s'en sort. Avec un projecteur chargé s'ajoutent browser_screenshot (image quadrillée) et browser_click_xy. Interrupteur dédié (Réglages, `loki computer`, /api/computer), sous le mode agent : cliquer et taper dans une page sont des actions réelles, même niveau de confiance que bash. Adaptation au conteneur : chromePath cherche D'ABORD le Chromium de Playwright (PLAYWRIGHT_BROWSERS_PATH, /opt/pw-browsers), le seul navigateur de l'image — sinon la fonctionnalité se déclarait absente là où le navigateur est présent. LOKI_CHROME force un chemin, LOKI_CU_HEADFUL ouvre une vraie fenêtre. Préparation des images (web_upload_orient.go, porté d'AJEAN) : l'orientation EXIF est cuite dans les pixels — le projecteur l'ignore et voyait les photos de téléphone couchées — et le grand côté ramené sous 1568 px. Appliquée aux pièces jointes, à see_image et aux captures. Dédup des appels d'outils : bash, bash_bg, bash_tail, see_image et les browser_* ne sont plus court-circuités sur un appel identique. Relancer la même commande après avoir modifié un fichier est légitime, et un outil qui porte une image dans un message à part renvoyait « [déjà fait] » SANS l'image — le modèle tournait en boucle. Mode code : le builder ne publie plus de lui-même (pas de commit/push/reset/ rebase ni de redémarrage de service sans demande explicite), reprise de la leçon d'AJEAN 0.15.4 ; l'inspection en lecture seule reste encouragée. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6CAgoLJzufeA8rZTSpSpY
This commit is contained in:
21 files changed
+2503
-19
No files matched your search
@@ -202,6 +202,9 @@ type Caps struct {
|
||||
// "verifier" (passe de vérification, seule autorisée à marquer un critère
|
||||
// passed), "explorer", "code-reviewer". Voir code_roles.go.
|
||||
Role string
|
||||
// ComputerUse = pilotage du navigateur du conteneur (outils browser_*).
|
||||
// Requiert aussi Agent (mêmes actions réelles que bash). Voir computer_use.go.
|
||||
ComputerUse bool
|
||||
}
|
||||
|
||||
// globalCaps reads the machine-wide config — the default when a request doesn't
|
||||
@@ -219,7 +222,7 @@ func globalCaps() Caps {
|
||||
if !agent {
|
||||
return Caps{Agent: false, Internet: false, Mem: MemOff}
|
||||
}
|
||||
return Caps{Agent: true, Internet: internetEnabled() && crawlReachable(), Mem: memMode()}
|
||||
return Caps{Agent: true, Internet: internetEnabled() && crawlReachable(), Mem: memMode(), ComputerUse: computerUseEnabled()}
|
||||
}
|
||||
|
||||
// InjectSkills prepends context system messages to msgs: the decisive-agent
|
||||
@@ -369,6 +372,14 @@ func EnabledTools(caps Caps) []Tool {
|
||||
if caps.Agent && (caps.Internet || caps.Code) && screenshotAvailable() {
|
||||
tools = append(tools, webScreenshotTool())
|
||||
}
|
||||
// Pilotage de navigateur (browser_*) : l'IA ouvre une page, lit ses éléments
|
||||
// interactifs NUMÉROTÉS et agit par numéro. Ce sont des actions réelles sur
|
||||
// le web (cliquer, taper, valider un formulaire) : même niveau de confiance
|
||||
// que bash, donc mode agent requis, plus son propre interrupteur (Réglages).
|
||||
// Voir computer_use.go.
|
||||
if caps.Agent && caps.ComputerUse {
|
||||
tools = append(tools, computerUseTools()...)
|
||||
}
|
||||
// Outils MCP : serveurs tiers configurés par le propriétaire de la machine.
|
||||
// Comme bash, ils exécutent du code arbitraire côté hôte → réservés au mode
|
||||
// agent. La découverte est paresseuse et cachée (voir mcp_client.go).
|
||||
@@ -458,6 +469,33 @@ func shownResult(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// dedupableTool indique si un appel RIGOUREUSEMENT identique (même outil, mêmes
|
||||
// arguments) doit être court-circuité au lieu d'être rejoué. Vrai pour les outils
|
||||
// où un ré-appel identique n'a pas de sens ou produit une fausse erreur (rejouer
|
||||
// une écriture déjà appliquée → « old introuvable »).
|
||||
//
|
||||
// FAUX pour bash : relancer la MÊME commande est un usage courant et légitime —
|
||||
// l'IA crée un fichier, le lance, le modifie, puis le RELANCE avec la même ligne ;
|
||||
// le résultat change parce que le fichier a changé. La dédup la bloquait par un
|
||||
// « [déjà fait] » exaspérant. bash a des effets de bord : on le laisse toujours
|
||||
// s'exécuter. Même raison pour bash_bg / bash_tail (suivre un job, c'est relire
|
||||
// la même sortie qui, elle, avance).
|
||||
//
|
||||
// FAUX aussi pour see_image et tout le pilotage de navigateur (browser_*) :
|
||||
// - leur résultat dépend de l'ÉTAT VIVANT de la page (un browser_snapshot sans
|
||||
// argument a forcément la même clé de dédup à chaque appel, alors que la page
|
||||
// a changé) — les dédupliquer fige l'IA sur un vieux cliché ;
|
||||
// - surtout, browser_screenshot et see_image portent leur IMAGE dans un message
|
||||
// à part (visionImg) que le chemin de dédup ne rejoue PAS : l'IA recevrait
|
||||
// « [déjà fait] » SANS l'image et tournerait en boucle.
|
||||
func dedupableTool(name string) bool {
|
||||
switch name {
|
||||
case "bash", "bash_bg", "bash_tail", "see_image":
|
||||
return false
|
||||
}
|
||||
return !strings.HasPrefix(name, "browser_")
|
||||
}
|
||||
|
||||
// repeatedCallResult construit ce qu'on renvoie quand le modèle redemande un
|
||||
// appel RIGOUREUSEMENT identique (même outil, mêmes arguments) dans le même tour.
|
||||
// L'appel n'est jamais rejoué — on répond depuis le résultat mémorisé.
|
||||
@@ -1228,7 +1266,7 @@ func runChat(ctx context.Context, messages []Message, temperature float64, caps
|
||||
// même écriture ; la rejouer produisait une fausse erreur (« old
|
||||
// introuvable », puisque le remplacement est déjà fait).
|
||||
callKey := tc.Function.Name + "\x00" + tc.Function.Arguments
|
||||
if prev, seen := doneCalls[callKey]; seen {
|
||||
if prev, seen := doneCalls[callKey]; seen && dedupableTool(tc.Function.Name) {
|
||||
repeatCount[callKey]++
|
||||
result = repeatedCallResult(prev, repeatCount[callKey])
|
||||
cb(StreamEvent{ToolUsed: &ToolUsedEvent{Name: tc.Function.Name, Label: label, Result: shownResult(result), Done: true}})
|
||||
@@ -1381,6 +1419,24 @@ func runChat(ctx context.Context, messages []Message, temperature float64, caps
|
||||
result = capWebOutput(toolWebGrep(args))
|
||||
case "web_screenshot":
|
||||
result = toolWebScreenshot(args, caps)
|
||||
case "browser_open":
|
||||
result = capCUOutput(toolCUOpen(args))
|
||||
case "browser_snapshot":
|
||||
result = capCUOutput(toolCUSnapshot())
|
||||
case "browser_find":
|
||||
result = capCUOutput(toolCUFind(args))
|
||||
case "browser_click":
|
||||
result = capCUOutput(toolCUClick(args))
|
||||
case "browser_click_xy":
|
||||
result = capCUOutput(toolCUClickXY(args))
|
||||
case "browser_type":
|
||||
result = capCUOutput(toolCUType(args))
|
||||
case "browser_key":
|
||||
result = capCUOutput(toolCUKey(args))
|
||||
case "browser_scroll":
|
||||
result = capCUOutput(toolCUScroll(args))
|
||||
case "browser_screenshot":
|
||||
result, visionImg = toolCUScreenshot()
|
||||
default:
|
||||
if isMCPTool(tc.Function.Name) {
|
||||
result = mcpCall(tc.Function.Name, args)
|
||||
|
||||
Reference in new issue
Block a user