From 1c632d151df8fb4d96368652d7738d3af3f9f0e9 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Wed, 20 May 2026 11:59:31 +0800 Subject: [PATCH] fix(translator): skip empty text parts in Claude request conversion - Updated `ConvertClaudeRequestToGemini` to ignore empty `text` entries during processing. - Added unit tests to ensure empty `text` parts are skipped correctly. Closes: #3485 --- .../gemini/claude/gemini_claude_request.go | 6 ++++- .../claude/gemini_claude_request_test.go | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/internal/translator/gemini/claude/gemini_claude_request.go b/internal/translator/gemini/claude/gemini_claude_request.go index 3beadea18..128dac6e0 100644 --- a/internal/translator/gemini/claude/gemini_claude_request.go +++ b/internal/translator/gemini/claude/gemini_claude_request.go @@ -81,8 +81,12 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) contentsResult.ForEach(func(_, contentResult gjson.Result) bool { switch contentResult.Get("type").String() { case "text": + text := contentResult.Get("text").String() + if text == "" { + return true + } part := []byte(`{"text":""}`) - part, _ = sjson.SetBytes(part, "text", contentResult.Get("text").String()) + part, _ = sjson.SetBytes(part, "text", text) contentJSON, _ = sjson.SetRawBytes(contentJSON, "parts.-1", part) case "tool_use": diff --git a/internal/translator/gemini/claude/gemini_claude_request_test.go b/internal/translator/gemini/claude/gemini_claude_request_test.go index 0fd515e59..01bed5f17 100644 --- a/internal/translator/gemini/claude/gemini_claude_request_test.go +++ b/internal/translator/gemini/claude/gemini_claude_request_test.go @@ -106,3 +106,29 @@ func TestConvertClaudeRequestToGemini_StripsClaudeCodeAttribution(t *testing.T) t.Fatalf("Claude Code attribution block was forwarded: %s", gjson.GetBytes(output, "system_instruction.parts").Raw) } } + +func TestConvertClaudeRequestToGemini_SkipsEmptyTextParts(t *testing.T) { + inputJSON := []byte(`{ + "model": "claude-3-5-sonnet", + "messages": [ + { + "role": "assistant", + "content": [ + {"type": "text", "text": ""}, + {"type": "text", "text": "hello"}, + {"type": "text", "text": ""} + ] + } + ] + }`) + + output := ConvertClaudeRequestToGemini("gemini-3-flash-preview", inputJSON, false) + + parts := gjson.GetBytes(output, "contents.0.parts").Array() + if len(parts) != 1 { + t.Fatalf("Expected 1 part after skipping empty text, got %d: %s", len(parts), output) + } + if got := parts[0].Get("text").String(); got != "hello" { + t.Fatalf("Expected part text 'hello', got '%s'", got) + } +}