diff --git a/src/protocol/body.ts b/src/protocol/body.ts index 5228c79..d3e7a0b 100644 --- a/src/protocol/body.ts +++ b/src/protocol/body.ts @@ -42,7 +42,8 @@ type ResponsesInputItem = // Tool results carry a content array; the Responses API wants a string. Join // the text parts; non-text content is not representable here and is dropped // with a marker so the model is not misled into thinking it went missing -// silently. +// silently. The wire has no error flag, so a failed result is wrapped in +// `` as Interchange's Chat Completions adapter does. function toolResultText( block: Extract, ): string { @@ -51,7 +52,8 @@ function toolResultText( if (c.type === "text") parts.push(c.text); else parts.push(`[unsupported ${c.type} content omitted]`); } - return parts.join(""); + const text = parts.join("\n"); + return block.isError ? `\n${text}\n` : text; } // Grounded on the OpenAI Responses API document input shape: a base64 diff --git a/src/responses.test.ts b/src/responses.test.ts index 52f90ca..85ce2e5 100644 --- a/src/responses.test.ts +++ b/src/responses.test.ts @@ -526,6 +526,42 @@ describe("Responses request builder — tool-item dedupe", () => { }); }); +describe("Responses request builder — tool-result encoding", () => { + // The wire has no error flag; without the `` wrapper the model + // cannot tell a failed tool from a successful one. + test.each([ + { name: "success", isError: false, output: "a\nb" }, + { name: "error", isError: true, output: "\na\nb\n" }, + ])("encodes a $name result like the Chat adapter", ({ isError, output }) => { + const adapter = createOpenAIResponsesAdapter(source, {}); + const request = adapter.buildRequest( + [ + { + role: "user", + timestamp: 0, + content: [ + { + type: "tool_result", + callId: "call_1", + isError, + content: [ + { type: "text", text: "a" }, + { type: "text", text: "b" }, + ], + }, + ], + }, + ], + "model", + {}, + ); + const outputs = inputItemsOf(request).filter( + (item) => item["type"] === "function_call_output", + ); + expect(outputs[0]?.["output"]).toBe(output); + }); +}); + describe("Responses request builder — un-replayable reasoning", () => { const history = (model: string, signature: string): ConversationTurn[] => [ { role: "user", timestamp: 0, content: [{ type: "text", text: "hi" }] },