From 035dcf29292c72656a8fb6855ec28b332cb48b4a Mon Sep 17 00:00:00 2001 From: C1-BA-B1-F3 Date: Thu, 25 Jun 2026 09:39:13 +0800 Subject: [PATCH] fix: include assistant tool call content (closes #263) --- src/model/providers/openai/request.ts | 2 +- tests/model/providers/openai/request.test.ts | 48 ++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/model/providers/openai/request.test.ts diff --git a/src/model/providers/openai/request.ts b/src/model/providers/openai/request.ts index 57b12541..0f1ce106 100644 --- a/src/model/providers/openai/request.ts +++ b/src/model/providers/openai/request.ts @@ -139,7 +139,7 @@ function toOpenAIMessages(message: CanonicalMessage, messageIndex: number): Open role: message.role, content: normalContent.length > 0 ? toOpenAIContent(normalContent) - : (message.role === "assistant" && thinkingBlocks.length > 0 ? "" : undefined), + : (message.role === "assistant" && (assistantToolCalls.length > 0 || thinkingBlocks.length > 0) ? "" : undefined), tool_calls: assistantToolCalls.length > 0 ? assistantToolCalls : undefined, }; // DeepSeek V4 requires reasoning_content to be passed back on assistant diff --git a/tests/model/providers/openai/request.test.ts b/tests/model/providers/openai/request.test.ts new file mode 100644 index 00000000..19d3a607 --- /dev/null +++ b/tests/model/providers/openai/request.test.ts @@ -0,0 +1,48 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { DEFAULT_MODEL_CAPABILITIES } from "../../../../src/model/protocol/capabilities.js"; +import { DEFAULT_MULTIMODAL_CONSTRAINTS } from "../../../../src/model/protocol/multimodal.js"; +import { buildOpenAIRequest } from "../../../../src/model/providers/openai/request.js"; +import type { ModelDefinition } from "../../../../src/model/protocol/canonical.js"; + +const model: ModelDefinition = { + id: "test-model", + capabilities: DEFAULT_MODEL_CAPABILITIES, + multimodal: DEFAULT_MULTIMODAL_CONSTRAINTS, +}; + +test("OpenAI assistant tool-call messages include empty content when no text is present", () => { + const body = buildOpenAIRequest({ + provider: "openai", + model: "test-model", + stream: true, + messages: [ + { + role: "assistant", + content: [ + { + type: "tool_call", + id: "call_123", + name: "lookup", + input: { q: "pilotdeck" }, + }, + ], + }, + { + role: "user", + content: [ + { + type: "tool_result", + toolCallId: "call_123", + content: [{ type: "text", text: "ok" }], + }, + ], + }, + ], + }, model); + + assert.equal(body.messages[0]?.role, "assistant"); + assert.equal(body.messages[0]?.content, ""); + assert.equal(Array.isArray(body.messages[0]?.tool_calls), true); +});