From c9ed4cbd51fd91e9d3347609bdb476db0a718b0f Mon Sep 17 00:00:00 2001 From: Netanel Beniluz Date: Fri, 27 Jun 2025 11:09:59 +0300 Subject: [PATCH] fix: OpenAI/Anthropic tool use interop logic for gpt-4.1 and denial ordering - Ensures tool responses are visible for gpt-4.1 tool calls - Fixes bug where denying a tool use resulted in error 500 due to message role ordering Fixes: 1. GPT-4.1 tool use responses were not correctly serialized, causing responses not to appear to the user 2. Denied tool uses previously caused 500 errors because of incorrect message sequencing --- src/routes/messages/non-stream-translation.ts | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/routes/messages/non-stream-translation.ts b/src/routes/messages/non-stream-translation.ts index 021f283..d01437d 100644 --- a/src/routes/messages/non-stream-translation.ts +++ b/src/routes/messages/non-stream-translation.ts @@ -76,39 +76,49 @@ function handleSystemPrompt( } function handleUserMessage(message: AnthropicUserMessage): Array { - const newMessages: Array = [] + const msgs: Array = [] if (Array.isArray(message.content)) { const toolResultBlocks = message.content.filter( (block): block is AnthropicToolResultBlock => block.type === "tool_result", ) + const textBlocks = message.content.filter((block) => block.type === "text") const otherBlocks = message.content.filter( - (block) => block.type !== "tool_result", + (block) => block.type !== "tool_result" && block.type !== "text", ) - - if (otherBlocks.length > 0) { - newMessages.push({ - role: "user", - content: mapContent(otherBlocks), - }) - } - + // OpenAI order: tool results FIRST, then user text (if any) for (const block of toolResultBlocks) { - newMessages.push({ + msgs.push({ role: "tool", tool_call_id: block.tool_use_id, - content: block.content, + content: + typeof block.content === "string" ? + block.content + : JSON.stringify(block.content), }) } + if (textBlocks.length > 0 || otherBlocks.length > 0) { + const textContent = [ + ...textBlocks.map((b) => b.text), + ...otherBlocks.map((b) => JSON.stringify(b)), // fallback for custom blocks + ] + .join("\n\n") + .trim() + if (textContent.length > 0) { + msgs.push({ + role: "user", + content: textContent, + }) + } + } } else { - newMessages.push({ + msgs.push({ role: "user", content: mapContent(message.content), }) } - - return newMessages + return msgs } function handleAssistantMessage(