Files
Loki/internal/loki/code_alias.go
T
MichaelandClaude Opus 5.5 a745d2399e Agent : read_file, str_replace, old_string… traduits vers les vrais outils
Généralisation du transformSubAgentAliases d'OpenFox. Les petits modèles
ont appris d'autres agents : read_file, str_replace, run_command, ou path /
old_string au lieu de file / old. L'appel tombait sur « outil inconnu »
ou sur un argument manquant, et le tour se perdait en allers-retours.

Le nom et les arguments sont traduits vers l'outil réel quand la cible est
disponible dans ce tour, avant que l'appel soit rangé dans l'historique
(le modèle y relit l'appel tel qu'exécuté). Un sous-agent appelé comme un
outil (« explorer », « code_reviewer ») devient subagent{role, task}. Un
appel déjà correct, ou dont la cible n'est pas offerte, reste intact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:32:29 +02:00

103 lines
3.5 KiB
Go

package loki
// code_alias.go — réparation des noms d'outils et d'arguments hallucinés.
//
// Les petits modèles ont appris d'autres agents : ils appellent read_file,
// str_replace, run_command, ou passent path / old_string au lieu de file /
// old. L'appel tombait sur « outil inconnu » ou sur un argument manquant, et
// le tour se perdait en allers-retours. On traduit vers l'outil RÉEL quand la
// cible existe dans ce tour (généralisation du transformSubAgentAliases
// d'OpenFox : un outil nommé d'après un sous-agent devient subagent{role}).
import (
"encoding/json"
"strings"
)
// toolNameAliases : nom halluciné → outil de Loki.
var toolNameAliases = map[string]string{
"read_file": "read", "view": "read", "cat": "read", "open_file": "read",
"write_file": "write", "create_file": "write",
"edit_file": "edit", "str_replace": "edit", "replace_in_file": "edit", "str_replace_editor": "edit",
"run_command": "bash", "shell": "bash", "run_shell": "bash", "execute_command": "bash", "terminal": "bash",
"search": "grep", "grep_search": "grep", "search_files": "grep",
"find_files": "glob", "list_files": "glob", "file_search": "glob",
}
// toolArgAliases : par outil, argument halluciné → argument attendu.
var toolArgAliases = map[string]map[string]string{
"read": {"path": "file", "file_path": "file", "filename": "file", "filepath": "file"},
"write": {"path": "file", "file_path": "file", "filename": "file", "filepath": "file", "contents": "content", "text": "content"},
"edit": {"path": "file", "file_path": "file", "filename": "file", "filepath": "file",
"old_string": "old", "old_str": "old", "old_text": "old", "search": "old",
"new_string": "new", "new_str": "new", "new_text": "new", "replace": "new"},
"bash": {"cmd": "command"},
"bash_bg": {"cmd": "command"},
"grep": {"query": "pattern", "regex": "pattern"},
"glob": {"query": "pattern"},
}
// repairToolCall corrige en place le nom et les arguments d'un appel quand ils
// visent, sous un autre nom, un outil disponible dans ce tour. Renvoie true si
// l'appel a été modifié. Les arguments doivent déjà être du JSON valide.
func repairToolCall(tc *ToolCall, tools []Tool) bool {
have := make(map[string]bool, len(tools))
for _, t := range tools {
have[t.Function.Name] = true
}
name := tc.Function.Name
var args map[string]any
if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil || args == nil {
args = map[string]any{}
}
changed := false
if !have[name] {
lower := strings.ToLower(strings.TrimSpace(name))
if t := toolNameAliases[lower]; t != "" && have[t] {
name, changed = t, true
} else if have["subagent"] {
// Un sous-agent appelé comme un outil (« explorer », « code_reviewer »).
role := strings.ReplaceAll(lower, "_", "-")
for _, r := range subagentRoles {
if role == r {
if _, ok := args["role"]; !ok {
args["role"] = r
}
if _, ok := args["task"]; !ok {
for _, k := range []string{"prompt", "query", "question", "description"} {
if v, ok := args[k].(string); ok && v != "" {
args["task"] = v
delete(args, k)
break
}
}
}
name, changed = "subagent", true
break
}
}
}
}
for from, to := range toolArgAliases[name] {
v, ok := args[from]
if !ok {
continue
}
if _, exists := args[to]; !exists {
args[to] = v
changed = true
}
delete(args, from)
}
if !changed {
return false
}
b, err := json.Marshal(args)
if err != nil {
return false
}
tc.Function.Name = name
tc.Function.Arguments = string(b)
return true
}