From 749c2e3f9cac8fead7b224f32d0e8be0f9c106b6 Mon Sep 17 00:00:00 2001 From: decolua Date: Mon, 29 Jun 2026 15:53:26 +0700 Subject: [PATCH] fix(translator): map mid-conversation system message to user in claude-to-openai MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Code chèn role:system cuối messages[], trước đây bị map thành assistant khiến hội thoại không kết thúc bằng user → provider OpenAI-compat (LiteLLM) dịch ngược Anthropic trả 400 "assistant message prefill". Map system -> user và wrap để giữ ngữ nghĩa instruction. Co-authored-by: Cursor --- open-sse/translator/request/claude-to-openai.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/open-sse/translator/request/claude-to-openai.js b/open-sse/translator/request/claude-to-openai.js index dc54bac5..c6e92ed6 100644 --- a/open-sse/translator/request/claude-to-openai.js +++ b/open-sse/translator/request/claude-to-openai.js @@ -129,8 +129,24 @@ function fixMissingToolResponsesOpenAI(messages) { } } +// Wrap mid-conversation system text so it ends as a user turn (avoids Anthropic prefill 400) +function systemReminderText(content) { + const parts = Array.isArray(content) + ? content.filter(c => c?.type === CLAUDE_BLOCK.TEXT).map(c => c.text || "") + : [typeof content === "string" ? content : ""]; + const text = parts.filter(Boolean).join("\n"); + if (!text.trim()) return ""; + return `\n${text}\n`; +} + // Convert single Claude message - returns single message or array of messages function convertClaudeMessage(msg) { + // Mid-conversation system message -> user (per Anthropic placement rules) + if (msg.role === ROLE.SYSTEM) { + const text = systemReminderText(msg.content); + return text ? { role: ROLE.USER, content: text } : null; + } + const role = msg.role === ROLE.USER || msg.role === ROLE.TOOL ? ROLE.USER : ROLE.ASSISTANT; // Simple string content