From e7862099c6717c06429b50c0e7076cb5daf1287a Mon Sep 17 00:00:00 2001 From: warelik Date: Fri, 19 Jun 2026 08:49:19 +0300 Subject: [PATCH] fix(combo): flatten tool history for anthropic style messages Anthropic format places tool_use and tool_result blocks in the content array of assistant and user messages respectively. This patch updates flattenToolHistory to recognize and flatten them into prose for panel calls. --- open-sse/services/combo.js | 23 +++++++++++++++++++++++ tests/unit/combo-fusion.test.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/open-sse/services/combo.js b/open-sse/services/combo.js index 459266ec04..9216ab2fcf 100644 --- a/open-sse/services/combo.js +++ b/open-sse/services/combo.js @@ -31,6 +31,29 @@ function flattenToolHistory(messages) { const base = extractTextContent(rest.content) || (typeof rest.content === "string" ? rest.content : ""); return { ...rest, content: `${base}${base ? "\n" : ""}${TOOL_CALL_PREFIX}${names}]` }; } + if (Array.isArray(msg.content)) { + const hasToolUse = msg.content.some((c) => c.type === "tool_use"); + const hasToolResult = msg.content.some((c) => c.type === "tool_result"); + if (hasToolUse || hasToolResult) { + const textParts = []; + const toolNames = []; + const toolResults = []; + for (const block of msg.content) { + if (block.type === "text" && block.text) textParts.push(block.text); + if (block.type === "tool_use") toolNames.push(block.name || "tool"); + if (block.type === "tool_result") toolResults.push(extractTextContent(block.content) || String(block.content ?? "")); + } + const { ...rest } = msg; + let newContent = textParts.join("\n"); + if (toolNames.length > 0) { + newContent = `${newContent}${newContent ? "\n" : ""}${TOOL_CALL_PREFIX}${toolNames.join(", ")}]`; + } + if (toolResults.length > 0) { + newContent = `${newContent}${newContent ? "\n" : ""}${TOOL_RESULT_PREFIX}${toolResults.join("\n")}]`; + } + return { ...rest, content: newContent }; + } + } return msg; }); } diff --git a/tests/unit/combo-fusion.test.js b/tests/unit/combo-fusion.test.js index 1a47843db2..52949cb00b 100644 --- a/tests/unit/combo-fusion.test.js +++ b/tests/unit/combo-fusion.test.js @@ -182,4 +182,35 @@ describe("fusion combo", () => { expect(judgeBody.messages[1].tool_calls).toBeDefined(); expect(judgeBody.messages[2].role).toBe("tool"); }); + + it("flattens Anthropic-style tool_use and tool_result blocks in arrays", async () => { + const handleSingleModel = vi.fn(async () => okResponse("ans")); + await handleFusionChat({ + body: { + messages: [ + { role: "user", content: "do it" }, + { role: "assistant", content: [{ type: "text", text: "ok" }, { type: "tool_use", id: "t1", name: "run" }] }, + { role: "user", content: [{ type: "tool_result", tool_use_id: "t1", content: "done" }] } + ], + tools: [{ name: "run", description: "d" }] + }, + models: ["p/a", "p/b"], + handleSingleModel, + log, + judgeModel: "p/judge" + }); + + const panelCalls = handleSingleModel.mock.calls.filter(([,, isPanel]) => isPanel === true); + expect(panelCalls.length).toBe(2); + const panelBody = panelCalls[0][0]; + + expect(panelBody.tools).toBeUndefined(); + expect(panelBody.messages.length).toBe(3); + + // Flattened tool_use + expect(panelBody.messages[1].content).toBe("ok\n[Called tools: run]"); + + // Flattened tool_result + expect(panelBody.messages[2].content).toBe("[Tool result: done]"); + }); });