( anthropicMessages: Anthropic.Messages.MessageParam[], options?: ConvertToOpenAiMessagesOptions, )
| 320 | } |
| 321 | |
| 322 | export function convertToOpenAiMessages( |
| 323 | anthropicMessages: Anthropic.Messages.MessageParam[], |
| 324 | options?: ConvertToOpenAiMessagesOptions, |
| 325 | ): OpenAI.Chat.ChatCompletionMessageParam[] { |
| 326 | const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [] |
| 327 | |
| 328 | const mapReasoningDetails = (details: unknown): any[] | undefined => { |
| 329 | if (!Array.isArray(details)) { |
| 330 | return undefined |
| 331 | } |
| 332 | |
| 333 | return details.map((detail: any) => { |
| 334 | // Strip `id` from openai-responses-v1 blocks because OpenAI's Responses API |
| 335 | // requires `store: true` to persist reasoning blocks. Since we manage |
| 336 | // conversation state client-side, we don't use `store: true`, and sending |
| 337 | // back the `id` field causes a 404 error. |
| 338 | if (detail?.format === "openai-responses-v1" && detail?.id) { |
| 339 | const { id, ...rest } = detail |
| 340 | return rest |
| 341 | } |
| 342 | return detail |
| 343 | }) |
| 344 | } |
| 345 | |
| 346 | // Use provided normalization function or identity function |
| 347 | const normalizeId = options?.normalizeToolCallId ?? ((id: string) => id) |
| 348 | |
| 349 | for (const anthropicMessage of anthropicMessages) { |
| 350 | if (typeof anthropicMessage.content === "string") { |
| 351 | // Some upstream transforms (e.g. [`Task.buildCleanConversationHistory()`](src/core/task/Task.ts:4048)) |
| 352 | // will convert a single text block into a string for compactness. |
| 353 | // If a message also contains reasoning_details (Gemini 3 / xAI / o-series, etc.), |
| 354 | // we must preserve it here as well. |
| 355 | const messageWithDetails = anthropicMessage as AssistantMessageWithReasoning |
| 356 | const baseMessage: OpenAI.Chat.ChatCompletionMessageParam & ReasoningPassthroughFields = { |
| 357 | role: anthropicMessage.role, |
| 358 | content: anthropicMessage.content, |
| 359 | } |
| 360 | |
| 361 | if (anthropicMessage.role === "assistant") { |
| 362 | const mapped = mapReasoningDetails(messageWithDetails.reasoning_details) |
| 363 | if (mapped) { |
| 364 | baseMessage.reasoning_details = mapped |
| 365 | } |
| 366 | // Pass through reasoning_content for DeepSeek / Z.ai thinking mode. |
| 367 | if (typeof messageWithDetails.reasoning_content === "string" && messageWithDetails.reasoning_content) { |
| 368 | baseMessage.reasoning_content = messageWithDetails.reasoning_content |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | openAiMessages.push(baseMessage) |
| 373 | } else { |
| 374 | // image_url.url is base64 encoded image data |
| 375 | // ensure it contains the content-type of the image: data:image/png;base64, |
| 376 | /* |
| 377 | { role: "user", content: "" | { type: "text", text: string } | { type: "image_url", image_url: { url: string } } }, |
| 378 | // content required unless tool_calls is present |
| 379 | { role: "assistant", content?: "" | null, tool_calls?: [{ id: "", function: { name: "", arguments: "" }, type: "function" }] }, |
no test coverage detected