From dc8d3201e1e78a4961d213bace2a111574504735 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Sat, 22 Nov 2025 10:36:52 +0800 Subject: [PATCH] feat(translator): support image size and googleSearch tools --- .../antigravity_openai_request.go | 32 +++++++++++--- .../gemini-cli_openai_request.go | 32 +++++++++++--- .../chat-completions/gemini_openai_request.go | 43 ++++++++++++++++--- 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/internal/translator/antigravity/openai/chat-completions/antigravity_openai_request.go b/internal/translator/antigravity/openai/chat-completions/antigravity_openai_request.go index 98a9d2e0..fd8e0071 100644 --- a/internal/translator/antigravity/openai/chat-completions/antigravity_openai_request.go +++ b/internal/translator/antigravity/openai/chat-completions/antigravity_openai_request.go @@ -131,6 +131,9 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _ if ar := imgCfg.Get("aspect_ratio"); ar.Exists() && ar.Type == gjson.String { out, _ = sjson.SetBytes(out, "request.generationConfig.imageConfig.aspectRatio", ar.Str) } + if size := imgCfg.Get("image_size"); size.Exists() && size.Type == gjson.String { + out, _ = sjson.SetBytes(out, "request.generationConfig.imageConfig.imageSize", size.Str) + } } // messages -> systemInstruction + contents @@ -281,11 +284,12 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _ } } - // tools -> request.tools[0].functionDeclarations + // tools -> request.tools[0].functionDeclarations + request.tools[0].googleSearch passthrough tools := gjson.GetBytes(rawJSON, "tools") if tools.IsArray() && len(tools.Array()) > 0 { - out, _ = sjson.SetRawBytes(out, "request.tools", []byte(`[{"functionDeclarations":[]}]`)) - fdPath := "request.tools.0.functionDeclarations" + toolNode := []byte(`{}`) + hasTool := false + hasFunction := false for _, t := range tools.Array() { if t.Get("type").String() == "function" { fn := t.Get("function") @@ -323,14 +327,32 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _ } } fnRaw, _ = sjson.Delete(fnRaw, "strict") - tmp, errSet := sjson.SetRawBytes(out, fdPath+".-1", []byte(fnRaw)) + if !hasFunction { + toolNode, _ = sjson.SetRawBytes(toolNode, "functionDeclarations", []byte("[]")) + } + tmp, errSet := sjson.SetRawBytes(toolNode, "functionDeclarations.-1", []byte(fnRaw)) if errSet != nil { log.Warnf("Failed to append tool declaration for '%s': %v", fn.Get("name").String(), errSet) continue } - out = tmp + toolNode = tmp + hasFunction = true + hasTool = true } } + if gs := t.Get("google_search"); gs.Exists() { + var errSet error + toolNode, errSet = sjson.SetRawBytes(toolNode, "googleSearch", []byte(gs.Raw)) + if errSet != nil { + log.Warnf("Failed to set googleSearch tool: %v", errSet) + continue + } + hasTool = true + } + } + if hasTool { + out, _ = sjson.SetRawBytes(out, "request.tools", []byte("[]")) + out, _ = sjson.SetRawBytes(out, "request.tools.0", toolNode) } } diff --git a/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go b/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go index 99b50366..d14f1119 100644 --- a/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go +++ b/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go @@ -131,6 +131,9 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo if ar := imgCfg.Get("aspect_ratio"); ar.Exists() && ar.Type == gjson.String { out, _ = sjson.SetBytes(out, "request.generationConfig.imageConfig.aspectRatio", ar.Str) } + if size := imgCfg.Get("image_size"); size.Exists() && size.Type == gjson.String { + out, _ = sjson.SetBytes(out, "request.generationConfig.imageConfig.imageSize", size.Str) + } } // messages -> systemInstruction + contents @@ -281,11 +284,12 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo } } - // tools -> request.tools[0].functionDeclarations + // tools -> request.tools[0].functionDeclarations + request.tools[0].googleSearch passthrough tools := gjson.GetBytes(rawJSON, "tools") if tools.IsArray() && len(tools.Array()) > 0 { - out, _ = sjson.SetRawBytes(out, "request.tools", []byte(`[{"functionDeclarations":[]}]`)) - fdPath := "request.tools.0.functionDeclarations" + toolNode := []byte(`{}`) + hasTool := false + hasFunction := false for _, t := range tools.Array() { if t.Get("type").String() == "function" { fn := t.Get("function") @@ -323,14 +327,32 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo } } fnRaw, _ = sjson.Delete(fnRaw, "strict") - tmp, errSet := sjson.SetRawBytes(out, fdPath+".-1", []byte(fnRaw)) + if !hasFunction { + toolNode, _ = sjson.SetRawBytes(toolNode, "functionDeclarations", []byte("[]")) + } + tmp, errSet := sjson.SetRawBytes(toolNode, "functionDeclarations.-1", []byte(fnRaw)) if errSet != nil { log.Warnf("Failed to append tool declaration for '%s': %v", fn.Get("name").String(), errSet) continue } - out = tmp + toolNode = tmp + hasFunction = true + hasTool = true } } + if gs := t.Get("google_search"); gs.Exists() { + var errSet error + toolNode, errSet = sjson.SetRawBytes(toolNode, "googleSearch", []byte(gs.Raw)) + if errSet != nil { + log.Warnf("Failed to set googleSearch tool: %v", errSet) + continue + } + hasTool = true + } + } + if hasTool { + out, _ = sjson.SetRawBytes(out, "request.tools", []byte("[]")) + out, _ = sjson.SetRawBytes(out, "request.tools.0", toolNode) } } diff --git a/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go b/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go index 055c02b0..0df8987f 100644 --- a/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go +++ b/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go @@ -122,6 +122,9 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool) if ar := imgCfg.Get("aspect_ratio"); ar.Exists() && ar.Type == gjson.String { out, _ = sjson.SetBytes(out, "generationConfig.imageConfig.aspectRatio", ar.Str) } + if size := imgCfg.Get("image_size"); size.Exists() && size.Type == gjson.String { + out, _ = sjson.SetBytes(out, "generationConfig.imageConfig.imageSize", size.Str) + } } // messages -> systemInstruction + contents @@ -297,11 +300,12 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool) } } - // tools -> tools[0].functionDeclarations + // tools -> tools[0].functionDeclarations + tools[0].googleSearch passthrough tools := gjson.GetBytes(rawJSON, "tools") if tools.IsArray() && len(tools.Array()) > 0 { - out, _ = sjson.SetRawBytes(out, "tools", []byte(`[{"functionDeclarations":[]}]`)) - fdPath := "tools.0.functionDeclarations" + toolNode := []byte(`{}`) + hasTool := false + hasFunction := false for _, t := range tools.Array() { if t.Get("type").String() == "function" { fn := t.Get("function") @@ -311,6 +315,17 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool) renamed, errRename := util.RenameKey(fnRaw, "parameters", "parametersJsonSchema") if errRename != nil { log.Warnf("Failed to rename parameters for tool '%s': %v", fn.Get("name").String(), errRename) + var errSet error + fnRaw, errSet = sjson.Set(fnRaw, "parametersJsonSchema.type", "object") + if errSet != nil { + log.Warnf("Failed to set default schema type for tool '%s': %v", fn.Get("name").String(), errSet) + continue + } + fnRaw, errSet = sjson.Set(fnRaw, "parametersJsonSchema.properties", map[string]interface{}{}) + if errSet != nil { + log.Warnf("Failed to set default schema properties for tool '%s': %v", fn.Get("name").String(), errSet) + continue + } } else { fnRaw = renamed } @@ -328,14 +343,32 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool) } } fnRaw, _ = sjson.Delete(fnRaw, "strict") - tmp, errSet := sjson.SetRawBytes(out, fdPath+".-1", []byte(fnRaw)) + if !hasFunction { + toolNode, _ = sjson.SetRawBytes(toolNode, "functionDeclarations", []byte("[]")) + } + tmp, errSet := sjson.SetRawBytes(toolNode, "functionDeclarations.-1", []byte(fnRaw)) if errSet != nil { log.Warnf("Failed to append tool declaration for '%s': %v", fn.Get("name").String(), errSet) continue } - out = tmp + toolNode = tmp + hasFunction = true + hasTool = true } } + if gs := t.Get("google_search"); gs.Exists() { + var errSet error + toolNode, errSet = sjson.SetRawBytes(toolNode, "googleSearch", []byte(gs.Raw)) + if errSet != nil { + log.Warnf("Failed to set googleSearch tool: %v", errSet) + continue + } + hasTool = true + } + } + if hasTool { + out, _ = sjson.SetRawBytes(out, "tools", []byte("[]")) + out, _ = sjson.SetRawBytes(out, "tools.0", toolNode) } }