win 002066e700 chore(wip): 保存订制改动以便合并上游
- windsurf: client/pool/local_ls/tool_emulation/tool_names/models 调整
- handler: admin account_data / failover_loop / gateway_handler
- repository: scheduler_cache 及测试
- service: windsurf_chat_service / windsurf_gateway_service
- deploy: compose 合并为单文件(含 windsurf-ls profile),Dockerfile.ls
- cmd: 新增 dump_ls_models / dump_preamble / test_windsurf_tools 辅助工具
2026-04-24 11:14:36 +08:00

101 lines
2.5 KiB
Go

package windsurf
import "strings"
var canonicalToolAliases = map[string]string{
"read": "read",
"read_file": "read",
"readfile": "read",
"write": "write",
"write_file": "write",
"writefile": "write",
"edit": "edit",
"apply_patch": "edit",
"applypatch": "edit",
"bash": "bash",
"execute_bash": "bash",
"executebash": "bash",
"exec_bash": "bash",
"execbash": "bash",
"glob": "glob",
"list_files": "glob",
"listfiles": "glob",
"grep": "grep",
"search_files": "grep",
"searchfiles": "grep",
"webfetch": "webfetch",
"web_fetch": "webfetch",
"fetch": "webfetch",
}
// NormalizeToolName canonicalizes known tool aliases while preserving unknown tool names.
//
// Callers use different conventions:
// - OpenCode / Codex: lowercase or snake_case ("read_file", "search_files")
// → canonicalize via aliases map.
// - Claude Code: PascalCase ("Read", "Bash", "Glob", "Edit", "TodoWrite", …)
// → must pass through untouched; lowercasing them breaks round-trip because
// Claude Code only recognizes its exact PascalCase names.
//
// Heuristic: if the input starts with an uppercase letter, treat it as the
// caller's authoritative name and do NOT canonicalize.
func NormalizeToolName(name string) string {
trimmed := strings.TrimSpace(name)
if trimmed == "" {
return ""
}
if c := trimmed[0]; c >= 'A' && c <= 'Z' {
return trimmed
}
if canonical, ok := canonicalToolAliases[strings.ToLower(trimmed)]; ok {
return canonical
}
return trimmed
}
func normalizeOpenAITool(tool OpenAITool) OpenAITool {
if tool.Type != "function" {
return tool
}
tool.Function.Name = NormalizeToolName(tool.Function.Name)
return tool
}
func canonicalizeOpenAITools(tools []OpenAITool) []OpenAITool {
if len(tools) == 0 {
return nil
}
out := make([]OpenAITool, 0, len(tools))
seen := make(map[string]int, len(tools))
for _, tool := range tools {
normalized := normalizeOpenAITool(tool)
if normalized.Type != "function" {
out = append(out, normalized)
continue
}
name := strings.TrimSpace(normalized.Function.Name)
if name == "" {
continue
}
key := strings.ToLower(name)
if idx, ok := seen[key]; ok {
if out[idx].Function.Description == "" {
out[idx].Function.Description = normalized.Function.Description
}
if len(out[idx].Function.Parameters) == 0 {
out[idx].Function.Parameters = normalized.Function.Parameters
}
continue
}
seen[key] = len(out)
out = append(out, normalized)
}
return out
}