From 06a5a1a7aa31acfefeedf0d25422a7646c9994a6 Mon Sep 17 00:00:00 2001 From: Grass Zhang Date: Sun, 1 Feb 2026 11:30:44 +0000 Subject: [PATCH] fix: handle array content format in OpenAI messages The OpenAI Chat Completions API allows message content to be either a plain string or an array of content parts (e.g. [{type: "text", text: "..."}]). When an upstream client sends array content, the previous code passed it directly to template literals, resulting in "[object Object]" being sent to Claude CLI. Add extractContent() helper that normalises both string and array formats into a single string, and update the OpenAIChatMessage type to reflect the actual API spec. --- src/adapter/openai-to-cli.ts | 39 ++++++++++++++++++++++++++++++++---- src/types/openai.ts | 12 ++++++++++- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/adapter/openai-to-cli.ts b/src/adapter/openai-to-cli.ts index c8ecaa1..41af8a3 100644 --- a/src/adapter/openai-to-cli.ts +++ b/src/adapter/openai-to-cli.ts @@ -2,7 +2,10 @@ * Converts OpenAI chat request format to Claude CLI input */ -import type { OpenAIChatRequest } from "../types/openai.js"; +import type { + OpenAIChatRequest, + OpenAIContentPart, +} from "../types/openai.js"; export type ClaudeModel = "opus" | "sonnet" | "haiku"; @@ -46,6 +49,32 @@ export function extractModel(model: string): ClaudeModel { return "opus"; } +/** + * Extract text from message content. + * + * OpenAI API allows content to be either a plain string or an array of + * content parts (e.g. [{type: "text", text: "..."}]). This function + * normalises both forms into a single string. + */ +export function extractContent( + content: string | OpenAIContentPart[], +): string { + if (typeof content === "string") return content; + + if (Array.isArray(content)) { + return content + .map((part) => { + if (typeof part === "string") return part; + if (part && typeof part === "object") return part.text ?? ""; + return ""; + }) + .filter(Boolean) + .join("\n"); + } + + return String(content ?? ""); +} + /** * Convert OpenAI messages array to a single prompt string for Claude CLI * @@ -56,20 +85,22 @@ export function messagesToPrompt(messages: OpenAIChatRequest["messages"]): strin const parts: string[] = []; for (const msg of messages) { + const text = extractContent(msg.content); + switch (msg.role) { case "system": // System messages become context instructions - parts.push(`\n${msg.content}\n\n`); + parts.push(`\n${text}\n\n`); break; case "user": // User messages are the main prompt - parts.push(msg.content); + parts.push(text); break; case "assistant": // Previous assistant responses for context - parts.push(`\n${msg.content}\n\n`); + parts.push(`\n${text}\n\n`); break; } } diff --git a/src/types/openai.ts b/src/types/openai.ts index c116658..706ef62 100644 --- a/src/types/openai.ts +++ b/src/types/openai.ts @@ -3,9 +3,19 @@ * Used for Clawdbot integration */ +/** + * A single content part in a multi-part message. + * See: https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages + */ +export interface OpenAIContentPart { + type: "text" | "image_url"; + text?: string; + image_url?: { url: string; detail?: string }; +} + export interface OpenAIChatMessage { role: "system" | "user" | "assistant"; - content: string; + content: string | OpenAIContentPart[]; } export interface OpenAIChatRequest {