mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-18 04:10:51 +08:00
feat(translator): sanitize tool/function names for upstream provider compatibility
Implemented SanitizeFunctionName utility to ensure Claude tool names meet Gemini/Upstream strict naming conventions (alphanumeric, starts with letter/underscore, max 64 chars). Applied sanitization to tool definitions and usage in all relevant translators. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
@@ -185,7 +185,7 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
||||
// Antigravity API validates signatures, so dummy values are rejected.
|
||||
// The TypeScript plugin removes unsigned thinking blocks instead of injecting dummies.
|
||||
|
||||
functionName := contentResult.Get("name").String()
|
||||
functionName := util.SanitizeFunctionName(contentResult.Get("name").String())
|
||||
argsResult := contentResult.Get("input")
|
||||
functionID := contentResult.Get("id").String()
|
||||
|
||||
@@ -225,10 +225,10 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
||||
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_result" {
|
||||
toolCallID := contentResult.Get("tool_use_id").String()
|
||||
if toolCallID != "" {
|
||||
funcName := toolCallID
|
||||
funcName := util.SanitizeFunctionName(toolCallID)
|
||||
toolCallIDs := strings.Split(toolCallID, "-")
|
||||
if len(toolCallIDs) > 1 {
|
||||
funcName = strings.Join(toolCallIDs[0:len(toolCallIDs)-2], "-")
|
||||
funcName = util.SanitizeFunctionName(strings.Join(toolCallIDs[0:len(toolCallIDs)-2], "-"))
|
||||
}
|
||||
functionResponseResult := contentResult.Get("content")
|
||||
|
||||
@@ -337,6 +337,12 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
||||
inputSchema := util.CleanJSONSchemaForAntigravity(inputSchemaResult.Raw)
|
||||
tool, _ := sjson.Delete(toolResult.Raw, "input_schema")
|
||||
tool, _ = sjson.SetRaw(tool, "parametersJsonSchema", inputSchema)
|
||||
|
||||
// Sanitize tool name
|
||||
if name := gjson.Get(tool, "name"); name.Exists() {
|
||||
tool, _ = sjson.Set(tool, "name", util.SanitizeFunctionName(name.String()))
|
||||
}
|
||||
|
||||
for toolKey := range gjson.Parse(tool).Map() {
|
||||
if util.InArray(allowedToolKeys, toolKey) {
|
||||
continue
|
||||
|
||||
@@ -264,21 +264,7 @@ func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool)
|
||||
|
||||
// shortenNameIfNeeded applies a simple shortening rule for a single name.
|
||||
func shortenNameIfNeeded(name string) string {
|
||||
const limit = 64
|
||||
if len(name) <= limit {
|
||||
return name
|
||||
}
|
||||
if strings.HasPrefix(name, "mcp__") {
|
||||
idx := strings.LastIndex(name, "__")
|
||||
if idx > 0 {
|
||||
cand := "mcp__" + name[idx+2:]
|
||||
if len(cand) > limit {
|
||||
return cand[:limit]
|
||||
}
|
||||
return cand
|
||||
}
|
||||
}
|
||||
return name[:limit]
|
||||
return util.SanitizeFunctionName(name)
|
||||
}
|
||||
|
||||
// buildShortNameMap ensures uniqueness of shortened names within a request.
|
||||
@@ -288,20 +274,7 @@ func buildShortNameMap(names []string) map[string]string {
|
||||
m := map[string]string{}
|
||||
|
||||
baseCandidate := func(n string) string {
|
||||
if len(n) <= limit {
|
||||
return n
|
||||
}
|
||||
if strings.HasPrefix(n, "mcp__") {
|
||||
idx := strings.LastIndex(n, "__")
|
||||
if idx > 0 {
|
||||
cand := "mcp__" + n[idx+2:]
|
||||
if len(cand) > limit {
|
||||
cand = cand[:limit]
|
||||
}
|
||||
return cand
|
||||
}
|
||||
}
|
||||
return n[:limit]
|
||||
return util.SanitizeFunctionName(n)
|
||||
}
|
||||
|
||||
makeUnique := func(cand string) string {
|
||||
|
||||
@@ -91,7 +91,7 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) []
|
||||
contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part)
|
||||
|
||||
case "tool_use":
|
||||
functionName := contentResult.Get("name").String()
|
||||
functionName := util.SanitizeFunctionName(contentResult.Get("name").String())
|
||||
functionArgs := contentResult.Get("input").String()
|
||||
argsResult := gjson.Parse(functionArgs)
|
||||
if argsResult.IsObject() && gjson.Valid(functionArgs) {
|
||||
@@ -107,10 +107,10 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) []
|
||||
if toolCallID == "" {
|
||||
return true
|
||||
}
|
||||
funcName := toolCallID
|
||||
funcName := util.SanitizeFunctionName(toolCallID)
|
||||
toolCallIDs := strings.Split(toolCallID, "-")
|
||||
if len(toolCallIDs) > 1 {
|
||||
funcName = strings.Join(toolCallIDs[0:len(toolCallIDs)-1], "-")
|
||||
funcName = util.SanitizeFunctionName(strings.Join(toolCallIDs[0:len(toolCallIDs)-1], "-"))
|
||||
}
|
||||
responseData := contentResult.Get("content").Raw
|
||||
part := `{"functionResponse":{"name":"","response":{"result":""}}}`
|
||||
@@ -144,6 +144,12 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) []
|
||||
tool, _ = sjson.Delete(tool, "input_examples")
|
||||
tool, _ = sjson.Delete(tool, "type")
|
||||
tool, _ = sjson.Delete(tool, "cache_control")
|
||||
|
||||
// Sanitize tool name
|
||||
if name := gjson.Get(tool, "name"); name.Exists() {
|
||||
tool, _ = sjson.Set(tool, "name", util.SanitizeFunctionName(name.String()))
|
||||
}
|
||||
|
||||
if gjson.Valid(tool) && gjson.Parse(tool).IsObject() {
|
||||
if !hasTools {
|
||||
out, _ = sjson.SetRaw(out, "request.tools", `[{"functionDeclarations":[]}]`)
|
||||
|
||||
@@ -84,7 +84,7 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
||||
contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part)
|
||||
|
||||
case "tool_use":
|
||||
functionName := contentResult.Get("name").String()
|
||||
functionName := util.SanitizeFunctionName(contentResult.Get("name").String())
|
||||
functionArgs := contentResult.Get("input").String()
|
||||
argsResult := gjson.Parse(functionArgs)
|
||||
if argsResult.IsObject() && gjson.Valid(functionArgs) {
|
||||
@@ -100,10 +100,10 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
||||
if toolCallID == "" {
|
||||
return true
|
||||
}
|
||||
funcName := toolCallID
|
||||
funcName := util.SanitizeFunctionName(toolCallID)
|
||||
toolCallIDs := strings.Split(toolCallID, "-")
|
||||
if len(toolCallIDs) > 1 {
|
||||
funcName = strings.Join(toolCallIDs[0:len(toolCallIDs)-1], "-")
|
||||
funcName = util.SanitizeFunctionName(strings.Join(toolCallIDs[0:len(toolCallIDs)-1], "-"))
|
||||
}
|
||||
responseData := contentResult.Get("content").Raw
|
||||
part := `{"functionResponse":{"name":"","response":{"result":""}}}`
|
||||
@@ -137,6 +137,12 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
||||
tool, _ = sjson.Delete(tool, "input_examples")
|
||||
tool, _ = sjson.Delete(tool, "type")
|
||||
tool, _ = sjson.Delete(tool, "cache_control")
|
||||
|
||||
// Sanitize tool name
|
||||
if name := gjson.Get(tool, "name"); name.Exists() {
|
||||
tool, _ = sjson.Set(tool, "name", util.SanitizeFunctionName(name.String()))
|
||||
}
|
||||
|
||||
if gjson.Valid(tool) && gjson.Parse(tool).IsObject() {
|
||||
if !hasTools {
|
||||
out, _ = sjson.SetRaw(out, "tools", `[{"functionDeclarations":[]}]`)
|
||||
|
||||
Reference in New Issue
Block a user