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 <cursoragent@cursor.com>
This commit is contained in:
committed by
decolua
parent
7afaecd617
commit
ce6120ce7b
@@ -138,12 +138,14 @@ function convertContent(content) {
|
|||||||
|
|
||||||
// Text with thoughtSignature = regular text after thinking
|
// Text with thoughtSignature = regular text after thinking
|
||||||
if (part.thoughtSignature && part.text !== undefined) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular text
|
// Regular text
|
||||||
if (part.text !== undefined) {
|
if (part.text !== undefined && part.text !== "") {
|
||||||
textParts.push({ type: OPENAI_BLOCK.TEXT, text: 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 (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;
|
return toolResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ const AG2O = (req) =>
|
|||||||
translateRequest(FORMATS.ANTIGRAVITY, FORMATS.OPENAI, "m", { request: req }, true, null, null);
|
translateRequest(FORMATS.ANTIGRAVITY, FORMATS.OPENAI, "m", { request: req }, true, null, null);
|
||||||
|
|
||||||
describe("Antigravity → OpenAI", () => {
|
describe("Antigravity → OpenAI", () => {
|
||||||
// antigravity-to-openai.js:177-189 — content with BOTH functionResponse and functionCall/text
|
// antigravity-to-openai.js — content with BOTH functionResponse and functionCall/text
|
||||||
// returns toolResults early → drops the tool calls / text.
|
// previously returned toolResults early → dropped tool calls / text (fixed in #2225)
|
||||||
// KNOWN BUG
|
it("functionResponse + functionCall in same content keeps both", () => {
|
||||||
it.fails("functionResponse + functionCall in same content keeps both", () => {
|
|
||||||
const out = AG2O({
|
const out = AG2O({
|
||||||
contents: [{
|
contents: [{
|
||||||
role: "model",
|
role: "model",
|
||||||
|
|||||||
Reference in New Issue
Block a user