feat(translator): drop apply_patch custom tool in OpenAI responses

- Added logic in `ConvertOpenAIResponsesRequestToClaude` to exclude `apply_patch` custom tools.
- Introduced `isOpenAIResponsesApplyPatchCustomTool` helper function to identify and filter the tool.
- Added `TestConvertOpenAIResponsesRequestToClaude_DropsApplyPatchCustomTool` to validate the behavior.

Closes: #3243
This commit is contained in:
Luis Pater
2026-06-19 03:43:26 +08:00
parent 1d0551a991
commit c020e2d03f
2 changed files with 41 additions and 0 deletions

View File

@@ -522,6 +522,9 @@ func convertResponsesToolToClaudeTools(tool gjson.Result, toolNameMap map[string
return [][]byte{tJSON}
}
default:
if isOpenAIResponsesApplyPatchCustomTool(toolType, tool) {
return nil
}
if isUnsupportedOpenAIBuiltinToolType(toolType) {
return nil
}
@@ -532,6 +535,10 @@ func convertResponsesToolToClaudeTools(tool gjson.Result, toolNameMap map[string
return nil
}
func isOpenAIResponsesApplyPatchCustomTool(toolType string, tool gjson.Result) bool {
return toolType == "custom" && strings.TrimSpace(tool.Get("name").String()) == "apply_patch"
}
func convertResponsesNamespaceToolToClaude(tool gjson.Result, toolNameMap map[string]string) [][]byte {
namespaceName := strings.TrimSpace(tool.Get("name").String())
children := tool.Get("tools")

View File

@@ -202,6 +202,40 @@ func TestConvertOpenAIResponsesRequestToClaude_KeepsToolUseAdjacentToToolResult(
}
}
func TestConvertOpenAIResponsesRequestToClaude_DropsApplyPatchCustomTool(t *testing.T) {
raw := []byte(`{
"model":"claude-test",
"input":[{"role":"user","content":[{"type":"input_text","text":"hi"}]}],
"tools":[
{
"type":"custom",
"name":"apply_patch",
"description":"Use the apply_patch tool to edit files.",
"format":{"type":"grammar","syntax":"lark","definition":"start: patch"}
},
{
"type":"function",
"name":"exec_command",
"description":"Runs a command.",
"parameters":{"type":"object","properties":{"cmd":{"type":"string"}},"required":["cmd"]}
}
]
}`)
out := ConvertOpenAIResponsesRequestToClaude("claude-test", raw, false)
root := gjson.ParseBytes(out)
if got := root.Get("tools.#").Int(); got != 1 {
t.Fatalf("tools count = %d, want 1. Output: %s", got, string(out))
}
if got := root.Get("tools.0.name").String(); got != "exec_command" {
t.Fatalf("tools.0.name = %q, want exec_command. Output: %s", got, string(out))
}
if got := root.Get("tools.#(name==\"apply_patch\")").Raw; got != "" {
t.Fatalf("apply_patch custom tool should be dropped. Output: %s", string(out))
}
}
func testClaudeResponsesThinkingSignature(t *testing.T) (string, string) {
t.Helper()
channelBlock := []byte{}