From 4b1a404fcb2cc91e98300cd8243e9d311b509b19 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sun, 15 Mar 2026 02:18:28 +0800 Subject: [PATCH] Fixed: #1936 feat(translator): add image type handling in ConvertClaudeRequestToGemini --- .../gemini/claude/gemini_claude_request.go | 15 ++++++++ .../claude/gemini_claude_request_test.go | 38 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/internal/translator/gemini/claude/gemini_claude_request.go b/internal/translator/gemini/claude/gemini_claude_request.go index b13955bb..137008b0 100644 --- a/internal/translator/gemini/claude/gemini_claude_request.go +++ b/internal/translator/gemini/claude/gemini_claude_request.go @@ -114,6 +114,21 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) part, _ = sjson.Set(part, "functionResponse.name", funcName) part, _ = sjson.Set(part, "functionResponse.response.result", responseData) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) + + case "image": + source := contentResult.Get("source") + if source.Get("type").String() != "base64" { + return true + } + mimeType := source.Get("media_type").String() + data := source.Get("data").String() + if mimeType == "" || data == "" { + return true + } + part := `{"inline_data":{"mime_type":"","data":""}}` + part, _ = sjson.Set(part, "inline_data.mime_type", mimeType) + part, _ = sjson.Set(part, "inline_data.data", data) + contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) } return true }) diff --git a/internal/translator/gemini/claude/gemini_claude_request_test.go b/internal/translator/gemini/claude/gemini_claude_request_test.go index e242c42c..10ad2d3a 100644 --- a/internal/translator/gemini/claude/gemini_claude_request_test.go +++ b/internal/translator/gemini/claude/gemini_claude_request_test.go @@ -40,3 +40,41 @@ func TestConvertClaudeRequestToGemini_ToolChoice_SpecificTool(t *testing.T) { t.Fatalf("Expected allowedFunctionNames ['json'], got %s", gjson.GetBytes(output, "toolConfig.functionCallingConfig.allowedFunctionNames").Raw) } } + +func TestConvertClaudeRequestToGemini_ImageContent(t *testing.T) { + inputJSON := []byte(`{ + "model": "gemini-3-flash-preview", + "messages": [ + { + "role": "user", + "content": [ + {"type": "text", "text": "describe this image"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "aGVsbG8=" + } + } + ] + } + ] + }`) + + output := ConvertClaudeRequestToGemini("gemini-3-flash-preview", inputJSON, false) + + parts := gjson.GetBytes(output, "contents.0.parts").Array() + if len(parts) != 2 { + t.Fatalf("Expected 2 parts, got %d", len(parts)) + } + if got := parts[0].Get("text").String(); got != "describe this image" { + t.Fatalf("Expected first part text 'describe this image', got '%s'", got) + } + if got := parts[1].Get("inline_data.mime_type").String(); got != "image/png" { + t.Fatalf("Expected image mime type 'image/png', got '%s'", got) + } + if got := parts[1].Get("inline_data.data").String(); got != "aGVsbG8=" { + t.Fatalf("Expected image data 'aGVsbG8=', got '%s'", got) + } +}