From ce6120ce7b006f0cf306e7f97d4cdd93305e995a Mon Sep 17 00:00:00 2001 From: Sahrul Ramadhan Hardiansyah Date: Fri, 3 Jul 2026 10:53:31 +0700 Subject: [PATCH] fix(translator): strict Anthropic content block compliance (#2225) Filter empty text blocks from thoughtSignature-only parts, preserve tool_calls when functionResponse and functionCall coexist in the same content, and skip empty regular text parts before they reach Claude. Co-authored-by: Cursor --- .../request/antigravity-to-openai.js | 22 ++++++++++++++++--- tests/translator/bugs-antigravity.test.js | 7 +++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/open-sse/translator/request/antigravity-to-openai.js b/open-sse/translator/request/antigravity-to-openai.js index b1dbd7bf..374bb54a 100644 --- a/open-sse/translator/request/antigravity-to-openai.js +++ b/open-sse/translator/request/antigravity-to-openai.js @@ -138,12 +138,14 @@ function convertContent(content) { // Text with thoughtSignature = regular text after thinking if (part.thoughtSignature && part.text !== undefined) { - textParts.push({ type: OPENAI_BLOCK.TEXT, text: part.text }); + if (part.text) { + textParts.push({ type: OPENAI_BLOCK.TEXT, text: part.text }); + } continue; } // Regular text - if (part.text !== undefined) { + if (part.text !== undefined && part.text !== "") { textParts.push({ type: OPENAI_BLOCK.TEXT, text: part.text }); } @@ -180,8 +182,22 @@ function convertContent(content) { } } - // Content with only functionResponses → return array of tool messages + // Content with functionResponses — return array of tool result messages, + // plus an assistant message for any co-located tool calls / text. if (toolResults.length > 0) { + if (toolCalls.length > 0 || textParts.length > 0 || reasoningContent) { + const assistantMsg = { role: ROLE.ASSISTANT }; + if (textParts.length > 0) { + assistantMsg.content = collapseTextParts(textParts); + } + if (reasoningContent) { + assistantMsg.reasoning_content = reasoningContent; + } + if (toolCalls.length > 0) { + assistantMsg.tool_calls = toolCalls; + } + return [...toolResults, assistantMsg]; + } return toolResults; } diff --git a/tests/translator/bugs-antigravity.test.js b/tests/translator/bugs-antigravity.test.js index a594161e..47c36942 100644 --- a/tests/translator/bugs-antigravity.test.js +++ b/tests/translator/bugs-antigravity.test.js @@ -9,10 +9,9 @@ const AG2O = (req) => translateRequest(FORMATS.ANTIGRAVITY, FORMATS.OPENAI, "m", { request: req }, true, null, null); describe("Antigravity → OpenAI", () => { - // antigravity-to-openai.js:177-189 — content with BOTH functionResponse and functionCall/text - // returns toolResults early → drops the tool calls / text. - // KNOWN BUG - it.fails("functionResponse + functionCall in same content keeps both", () => { + // antigravity-to-openai.js — content with BOTH functionResponse and functionCall/text + // previously returned toolResults early → dropped tool calls / text (fixed in #2225) + it("functionResponse + functionCall in same content keeps both", () => { const out = AG2O({ contents: [{ role: "model",