@ai-sdk/openai-compatible 3.0.60 → 3.0.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/chat/openai-compatible-api-types.ts","../../src/chat/convert-to-openai-compatible-chat-messages.ts","../../src/chat/convert-openai-compatible-chat-usage.ts","../../src/chat/map-openai-compatible-finish-reason.ts","../../src/chat/get-response-metadata.ts","../../src/chat/openai-compatible-prepare-tools.ts","../../src/openai-compatible-error.ts","../../src/chat/openai-compatible-metadata-extractor.ts","../../src/chat/openai-compatible-chat-language-model.ts"],"mappings":";;;;KAEY,6BAA6B,MAAM;KAEnC,0BACR,gCACA,8BACA,mCACA;KAIC,WAAW,aAAa,eAE3B,YAAY,cAAc,IAAI;UAGf,sCAAsC;EACrD;EACA;;UAGe,oCAAoC,WAAW;EAC9D;EACA,kBAAkB,MAAM;;KAGd,8BACR,kCACA,mCACA,mCACA,wCACA;UAEa,wCAAwC;EACvD;EACA;;UAGe,yCAAyC;EACxD;EACA;IAAa;;;UAIE,yCAAyC;EACxD;EACA;IAAa;;;UAIE,8CAA8C;EAC7D;EACA;IAAe;IAAc;;;UAId,wCAAwC;EACvD;EACA;IAAQ;IAAkB;;;UAGX,yCAAyC,WAAW;EACnE;EACA;EACA;EACA,aAAa,MAAM;;UAGJ,wCAAwC;EACvD;EACA;EACA;IACE;IACA;;;;;;EAMF;IACE;MACE;;;;UAKW,oCAAoC;EACnD;EACA;EACA;;;;wBC3Dc,sCACd,QAAQ,yBACN;EAAmC;IACpC;;;wBC/Ba,iCACd;EAEM;EACA;EACA;IACE;;EAEF;IACE;;uBAKP;;;wBCfa,gCACd,0CACC;;;wBCFa,sBACd,IACA,OACA;EAEA;EACA;EACA;+BACD;;;wBCLe,eACd,OACA;EAEA,OAAO;EACP,aAAa;;EAEb,mBAEI;IACE;IACA;MACE;MACA;MACA;MACA;;;EAGR;IACM;IAAkB;MAAY;;;EAKpC,cAAc;;;;KCXJ,uBAAuB;EACjC,aAAa,QAAQ;EACrB,iBAAiB,OAAO;EACxB,eAAe,UAAU,UAAU,QAAQ;;;;;;;;;KCdjC;;;;;;;;;EASV,oBACE;IAEA;QACI,QAAQ;;;;;;;;EASd;;;;;;;IAOE,aAAa;;;;;;;;IASb,iBAAiB;;;;;KCgBT;EACV;EACA,gBAAgB;EAChB,MAAM;IAAW;IAAiB;;EAClC,QAAQ;EACR;EACA,iBAAiB;EACjB,oBAAoB;;;;EAKpB;;;;EAKA,sBAAsB;;;;;;EAOtB,wBAAwB,MAAM,wBAAwB;;;;;EAMtD,gBACE,OAAO,EAAE,aAAa,sCACnB;;cAqqBD,kCAAgC,EAAA,YAAA,EAAA,YAAA,EAAA;;;;;;;;;;;;GAkB1B,EAAA,KAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/chat/openai-compatible-api-types.ts","../../src/chat/convert-to-openai-compatible-chat-messages.ts","../../src/chat/convert-openai-compatible-chat-usage.ts","../../src/chat/map-openai-compatible-finish-reason.ts","../../src/chat/get-response-metadata.ts","../../src/chat/openai-compatible-prepare-tools.ts","../../src/openai-compatible-error.ts","../../src/chat/openai-compatible-metadata-extractor.ts","../../src/chat/openai-compatible-chat-language-model.ts"],"mappings":";;;;KAEY,6BAA6B,MAAM;KAEnC,0BACR,gCACA,8BACA,mCACA;KAIC,WAAW,aAAa,eAE3B,YAAY,cAAc,IAAI;UAGf,sCAAsC;EACrD;EACA;;UAGe,oCAAoC,WAAW;EAC9D;EACA,kBAAkB,MAAM;;KAGd,8BACR,kCACA,mCACA,mCACA,wCACA;UAEa,wCAAwC;EACvD;EACA;;UAGe,yCAAyC;EACxD;EACA;IAAa;;;UAIE,yCAAyC;EACxD;EACA;IAAa;;;UAIE,8CAA8C;EAC7D;EACA;IAAe;IAAc;;;UAId,wCAAwC;EACvD;EACA;IAAQ;IAAkB;;;UAGX,yCAAyC,WAAW;EACnE;EACA;EACA;EACA,aAAa,MAAM;;UAGJ,wCAAwC;EACvD;EACA;EACA;IACE;IACA;;;;;;EAMF;IACE;MACE;;;;UAKW,oCAAoC;EACnD;EACA,kBAAkB,MAAM;EACxB;;;;wBCwFc,sCACd,QAAQ,yBAEN,oBACA;EAEA;EACA;IAED;;;wBCxLa,iCACd;EAEM;EACA;EACA;IACE;;EAEF;IACE;;uBAKP;;;wBCfa,gCACd,0CACC;;;wBCFa,sBACd,IACA,OACA;EAEA;EACA;EACA;+BACD;;;wBCLe,eACd,OACA;EAEA,OAAO;EACP,aAAa;;EAEb,mBAEI;IACE;IACA;MACE;MACA;MACA;MACA;;;EAGR;IACM;IAAkB;MAAY;;;EAKpC,cAAc;;;;KCXJ,uBAAuB;EACjC,aAAa,QAAQ;EACrB,iBAAiB,OAAO;EACxB,eAAe,UAAU,UAAU,QAAQ;;;;;;;;;KCdjC;;;;;;;;;EASV,oBACE;IAEA;QACI,QAAQ;;;;;;;;EASd;;;;;;;IAOE,aAAa;;;;;;;;IASb,iBAAiB;;;;;KCgBT;EACV;EACA,gBAAgB;EAChB,MAAM;IAAW;IAAiB;;EAClC,QAAQ;EACR;EACA,iBAAiB;EACjB,oBAAoB;;;;EAKpB;;;;EAKA;;;;EAKA,sBAAsB;;;;;;EAOtB,wBAAwB,MAAM,wBAAwB;;;;;EAMtD,gBACE,OAAO,EAAE,aAAa,sCACnB;;cAuqBD,kCAAgC,EAAA,YAAA,EAAA,YAAA,EAAA;;;;;;;;;;;;GAkB1B,EAAA,KAAA"}
@@ -12,7 +12,72 @@ function getAudioFormat(mediaType) {
12
12
  default: return null;
13
13
  }
14
14
  }
15
- function convertToOpenAICompatibleChatMessages(prompt, { providerOptionsKey = "google" } = {}) {
15
+ function convertToOpenAICompatibleContentPart(part) {
16
+ const partMetadata = getOpenAIMetadata(part);
17
+ switch (part.type) {
18
+ case "text": return {
19
+ type: "text",
20
+ text: part.text,
21
+ ...partMetadata
22
+ };
23
+ case "file": switch (part.data.type) {
24
+ case "reference": throw new UnsupportedFunctionalityError({ functionality: "file parts with provider references" });
25
+ case "text": throw new UnsupportedFunctionalityError({ functionality: "text file parts" });
26
+ case "url":
27
+ case "data": {
28
+ const topLevel = getTopLevelMediaType(part.mediaType);
29
+ if (topLevel === "image") return {
30
+ type: "image_url",
31
+ image_url: { url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}` },
32
+ ...partMetadata
33
+ };
34
+ if (topLevel === "video") return {
35
+ type: "video_url",
36
+ video_url: { url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}` },
37
+ ...partMetadata
38
+ };
39
+ if (topLevel === "audio") {
40
+ if (part.data.type === "url") throw new UnsupportedFunctionalityError({ functionality: "audio file parts with URLs" });
41
+ const fullMediaType = resolveFullMediaType({ part });
42
+ const format = getAudioFormat(fullMediaType);
43
+ if (format === null) throw new UnsupportedFunctionalityError({ functionality: `audio media type ${fullMediaType}` });
44
+ return {
45
+ type: "input_audio",
46
+ input_audio: {
47
+ data: convertToBase64(part.data.data),
48
+ format
49
+ },
50
+ ...partMetadata
51
+ };
52
+ }
53
+ if (topLevel === "application") {
54
+ if (part.data.type === "url") throw new UnsupportedFunctionalityError({ functionality: "PDF file parts with URLs" });
55
+ const fullMediaType = resolveFullMediaType({ part });
56
+ if (fullMediaType !== "application/pdf") throw new UnsupportedFunctionalityError({ functionality: `file part media type ${fullMediaType}` });
57
+ return {
58
+ type: "file",
59
+ file: {
60
+ filename: part.filename ?? "document.pdf",
61
+ file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`
62
+ },
63
+ ...partMetadata
64
+ };
65
+ }
66
+ if (topLevel === "text") return {
67
+ type: "text",
68
+ text: part.data.type === "url" ? part.data.url.toString() : typeof part.data.data === "string" ? new TextDecoder().decode(convertBase64ToUint8Array(part.data.data)) : new TextDecoder().decode(part.data.data),
69
+ ...partMetadata
70
+ };
71
+ throw new UnsupportedFunctionalityError({ functionality: `file part media type ${part.mediaType}` });
72
+ }
73
+ }
74
+ }
75
+ }
76
+ function convertToolContentPart(part) {
77
+ if (part.type === "custom") throw new UnsupportedFunctionalityError({ functionality: "custom tool content parts" });
78
+ return convertToOpenAICompatibleContentPart(part);
79
+ }
80
+ function convertToOpenAICompatibleChatMessages(prompt, { providerOptionsKey = "google", supportsMultiPartToolContent = false } = {}) {
16
81
  const messages = [];
17
82
  for (const { role, content, ...message } of prompt) {
18
83
  const metadata = getOpenAIMetadata({ ...message });
@@ -35,67 +100,7 @@ function convertToOpenAICompatibleChatMessages(prompt, { providerOptionsKey = "g
35
100
  }
36
101
  messages.push({
37
102
  role: "user",
38
- content: content.map((part) => {
39
- const partMetadata = getOpenAIMetadata(part);
40
- switch (part.type) {
41
- case "text": return {
42
- type: "text",
43
- text: part.text,
44
- ...partMetadata
45
- };
46
- case "file": switch (part.data.type) {
47
- case "reference": throw new UnsupportedFunctionalityError({ functionality: "file parts with provider references" });
48
- case "text": throw new UnsupportedFunctionalityError({ functionality: "text file parts" });
49
- case "url":
50
- case "data": {
51
- const topLevel = getTopLevelMediaType(part.mediaType);
52
- if (topLevel === "image") return {
53
- type: "image_url",
54
- image_url: { url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}` },
55
- ...partMetadata
56
- };
57
- if (topLevel === "video") return {
58
- type: "video_url",
59
- video_url: { url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}` },
60
- ...partMetadata
61
- };
62
- if (topLevel === "audio") {
63
- if (part.data.type === "url") throw new UnsupportedFunctionalityError({ functionality: "audio file parts with URLs" });
64
- const fullMediaType = resolveFullMediaType({ part });
65
- const format = getAudioFormat(fullMediaType);
66
- if (format === null) throw new UnsupportedFunctionalityError({ functionality: `audio media type ${fullMediaType}` });
67
- return {
68
- type: "input_audio",
69
- input_audio: {
70
- data: convertToBase64(part.data.data),
71
- format
72
- },
73
- ...partMetadata
74
- };
75
- }
76
- if (topLevel === "application") {
77
- if (part.data.type === "url") throw new UnsupportedFunctionalityError({ functionality: "PDF file parts with URLs" });
78
- const fullMediaType = resolveFullMediaType({ part });
79
- if (fullMediaType !== "application/pdf") throw new UnsupportedFunctionalityError({ functionality: `file part media type ${fullMediaType}` });
80
- return {
81
- type: "file",
82
- file: {
83
- filename: part.filename ?? "document.pdf",
84
- file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`
85
- },
86
- ...partMetadata
87
- };
88
- }
89
- if (topLevel === "text") return {
90
- type: "text",
91
- text: part.data.type === "url" ? part.data.url.toString() : typeof part.data.data === "string" ? new TextDecoder().decode(convertBase64ToUint8Array(part.data.data)) : new TextDecoder().decode(part.data.data),
92
- ...partMetadata
93
- };
94
- throw new UnsupportedFunctionalityError({ functionality: `file part media type ${part.mediaType}` });
95
- }
96
- }
97
- }
98
- }),
103
+ content: content.map(convertToOpenAICompatibleContentPart),
99
104
  ...metadata
100
105
  });
101
106
  break;
@@ -150,9 +155,11 @@ function convertToOpenAICompatibleChatMessages(prompt, { providerOptionsKey = "g
150
155
  case "execution-denied":
151
156
  contentValue = output.reason ?? "Tool call execution denied.";
152
157
  break;
153
- case "content":
154
158
  case "json":
155
- case "error-json": contentValue = JSON.stringify(output.value);
159
+ case "error-json":
160
+ contentValue = JSON.stringify(output.value);
161
+ break;
162
+ case "content": contentValue = supportsMultiPartToolContent ? output.value.map(convertToolContentPart) : JSON.stringify(output.value);
156
163
  }
157
164
  const toolResponseMetadata = getOpenAIMetadata(toolResponse);
158
165
  messages.push({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_exhaustiveCheck","_exhaustiveCheck"],"sources":["../../src/chat/convert-to-openai-compatible-chat-messages.ts","../../src/chat/convert-openai-compatible-chat-usage.ts","../../src/chat/map-openai-compatible-finish-reason.ts","../../src/chat/get-response-metadata.ts","../../src/chat/openai-compatible-prepare-tools.ts"],"sourcesContent":["import {\n UnsupportedFunctionalityError,\n type LanguageModelV4Prompt,\n type SharedV4ProviderMetadata,\n} from '@ai-sdk/provider';\nimport type { OpenAICompatibleChatPrompt } from './openai-compatible-api-types';\nimport {\n convertBase64ToUint8Array,\n convertToBase64,\n getTopLevelMediaType,\n resolveFullMediaType,\n} from '@ai-sdk/provider-utils';\n\nfunction getOpenAIMetadata(message: {\n providerOptions?: SharedV4ProviderMetadata;\n}) {\n return message?.providerOptions?.openaiCompatible ?? {};\n}\n\nfunction getAudioFormat(mediaType: string): 'wav' | 'mp3' | null {\n switch (mediaType) {\n case 'audio/wav':\n return 'wav';\n case 'audio/mp3':\n case 'audio/mpeg':\n return 'mp3';\n default:\n return null;\n }\n}\n\nexport function convertToOpenAICompatibleChatMessages(\n prompt: LanguageModelV4Prompt,\n { providerOptionsKey = 'google' }: { providerOptionsKey?: string } = {},\n): OpenAICompatibleChatPrompt {\n const messages: OpenAICompatibleChatPrompt = [];\n for (const { role, content, ...message } of prompt) {\n const metadata = getOpenAIMetadata({ ...message });\n switch (role) {\n case 'system': {\n messages.push({ role: 'system', content, ...metadata });\n break;\n }\n\n case 'user': {\n if (content.length === 1 && content[0].type === 'text') {\n messages.push({\n role: 'user',\n content: content[0].text,\n ...getOpenAIMetadata(content[0]),\n });\n break;\n }\n\n messages.push({\n role: 'user',\n content: content.map(part => {\n const partMetadata = getOpenAIMetadata(part);\n switch (part.type) {\n case 'text': {\n return { type: 'text', text: part.text, ...partMetadata };\n }\n case 'file': {\n switch (part.data.type) {\n case 'reference': {\n throw new UnsupportedFunctionalityError({\n functionality: 'file parts with provider references',\n });\n }\n case 'text': {\n throw new UnsupportedFunctionalityError({\n functionality: 'text file parts',\n });\n }\n case 'url':\n case 'data': {\n const topLevel = getTopLevelMediaType(part.mediaType);\n\n if (topLevel === 'image') {\n return {\n type: 'image_url',\n image_url: {\n url:\n part.data.type === 'url'\n ? part.data.url.toString()\n : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'video') {\n return {\n type: 'video_url',\n video_url: {\n url:\n part.data.type === 'url'\n ? part.data.url.toString()\n : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'audio') {\n if (part.data.type === 'url') {\n throw new UnsupportedFunctionalityError({\n functionality: 'audio file parts with URLs',\n });\n }\n\n const fullMediaType = resolveFullMediaType({ part });\n const format = getAudioFormat(fullMediaType);\n if (format === null) {\n throw new UnsupportedFunctionalityError({\n functionality: `audio media type ${fullMediaType}`,\n });\n }\n\n return {\n type: 'input_audio',\n input_audio: {\n data: convertToBase64(part.data.data),\n format,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'application') {\n if (part.data.type === 'url') {\n throw new UnsupportedFunctionalityError({\n functionality: 'PDF file parts with URLs',\n });\n }\n\n const fullMediaType = resolveFullMediaType({ part });\n if (fullMediaType !== 'application/pdf') {\n throw new UnsupportedFunctionalityError({\n functionality: `file part media type ${fullMediaType}`,\n });\n }\n\n return {\n type: 'file',\n file: {\n filename: part.filename ?? 'document.pdf',\n file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'text') {\n const textContent =\n part.data.type === 'url'\n ? part.data.url.toString()\n : typeof part.data.data === 'string'\n ? new TextDecoder().decode(\n convertBase64ToUint8Array(part.data.data),\n )\n : new TextDecoder().decode(part.data.data);\n\n return {\n type: 'text',\n text: textContent,\n ...partMetadata,\n };\n }\n\n throw new UnsupportedFunctionalityError({\n functionality: `file part media type ${part.mediaType}`,\n });\n }\n }\n }\n }\n }),\n ...metadata,\n });\n\n break;\n }\n\n case 'assistant': {\n let text = '';\n let reasoning = '';\n const toolCalls: Array<{\n id: string;\n type: 'function';\n function: { name: string; arguments: string };\n extra_content?: {\n google?: {\n thought_signature?: string;\n };\n };\n }> = [];\n\n for (const part of content) {\n const partMetadata = getOpenAIMetadata(part);\n switch (part.type) {\n case 'text': {\n text += part.text;\n break;\n }\n case 'reasoning': {\n reasoning += part.text;\n break;\n }\n case 'tool-call': {\n // TODO: thoughtSignature should be abstracted once we add support for other providers\n const thoughtSignature =\n part.providerOptions?.[providerOptionsKey]?.thoughtSignature ??\n part.providerOptions?.google?.thoughtSignature;\n toolCalls.push({\n id: part.toolCallId,\n type: 'function',\n function: {\n name: part.toolName,\n arguments: JSON.stringify(part.input),\n },\n ...partMetadata,\n // Include extra_content for Google Gemini thought signatures\n ...(thoughtSignature\n ? {\n extra_content: {\n google: {\n thought_signature: String(thoughtSignature),\n },\n },\n }\n : {}),\n });\n break;\n }\n }\n }\n\n messages.push({\n role: 'assistant',\n content: toolCalls.length > 0 ? text || null : text,\n ...(reasoning.length > 0 ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n ...metadata,\n });\n\n break;\n }\n\n case 'tool': {\n for (const toolResponse of content) {\n if (toolResponse.type === 'tool-approval-response') {\n continue;\n }\n\n const output = toolResponse.output;\n\n let contentValue: string;\n switch (output.type) {\n case 'text':\n case 'error-text':\n contentValue = output.value;\n break;\n case 'execution-denied':\n contentValue = output.reason ?? 'Tool call execution denied.';\n break;\n case 'content':\n case 'json':\n case 'error-json':\n contentValue = JSON.stringify(output.value);\n break;\n }\n\n const toolResponseMetadata = getOpenAIMetadata(toolResponse);\n messages.push({\n role: 'tool',\n tool_call_id: toolResponse.toolCallId,\n content: contentValue,\n ...toolResponseMetadata,\n });\n }\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = role;\n throw new Error(`Unsupported role: ${_exhaustiveCheck}`);\n }\n }\n }\n\n return messages;\n}\n","import type { LanguageModelV4Usage } from '@ai-sdk/provider';\nimport { createNullLanguageModelUsage } from '@ai-sdk/provider-utils';\n\nexport function convertOpenAICompatibleChatUsage(\n usage:\n | {\n prompt_tokens?: number | null;\n completion_tokens?: number | null;\n prompt_tokens_details?: {\n cached_tokens?: number | null;\n } | null;\n completion_tokens_details?: {\n reasoning_tokens?: number | null;\n } | null;\n }\n | undefined\n | null,\n): LanguageModelV4Usage {\n if (usage == null) {\n return createNullLanguageModelUsage();\n }\n\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;\n const reasoningTokens =\n usage.completion_tokens_details?.reasoning_tokens ?? 0;\n\n return {\n inputTokens: {\n total: promptTokens,\n noCache: promptTokens - cacheReadTokens,\n cacheRead: cacheReadTokens,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: completionTokens,\n text: Math.max(0, completionTokens - reasoningTokens),\n reasoning: reasoningTokens,\n },\n raw: usage,\n };\n}\n","import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';\n\nexport function mapOpenAICompatibleFinishReason(\n finishReason: string | null | undefined,\n): LanguageModelV4FinishReason['unified'] {\n switch (finishReason) {\n case 'stop':\n return 'stop';\n case 'length':\n return 'length';\n case 'content_filter':\n return 'content-filter';\n case 'function_call':\n case 'tool_calls':\n return 'tool-calls';\n default:\n return 'other';\n }\n}\n","import { createLanguageModelResponseMetadata } from '@ai-sdk/provider-utils';\n\nexport function getResponseMetadata({\n id,\n model,\n created,\n}: {\n id?: string | null;\n model?: string | null;\n created?: number | null;\n}) {\n return createLanguageModelResponseMetadata({\n id,\n model,\n // Some OpenAI-compatible providers use 0 as a placeholder timestamp.\n created: created || undefined,\n });\n}\n","import {\n UnsupportedFunctionalityError,\n type LanguageModelV4CallOptions,\n type SharedV4Warning,\n} from '@ai-sdk/provider';\nexport function prepareTools({\n tools,\n toolChoice,\n}: {\n tools: LanguageModelV4CallOptions['tools'];\n toolChoice?: LanguageModelV4CallOptions['toolChoice'];\n}): {\n tools:\n | undefined\n | Array<{\n type: 'function';\n function: {\n name: string;\n description: string | undefined;\n parameters: unknown;\n strict?: boolean;\n };\n }>;\n toolChoice:\n | { type: 'function'; function: { name: string } }\n | 'auto'\n | 'none'\n | 'required'\n | undefined;\n toolWarnings: SharedV4Warning[];\n} {\n // when the tools array is empty, change it to undefined to prevent errors:\n tools = tools?.length ? tools : undefined;\n\n const toolWarnings: SharedV4Warning[] = [];\n\n if (tools == null) {\n return { tools: undefined, toolChoice: undefined, toolWarnings };\n }\n\n const openaiCompatTools: Array<{\n type: 'function';\n function: {\n name: string;\n description: string | undefined;\n parameters: unknown;\n strict?: boolean;\n };\n }> = [];\n\n for (const tool of tools) {\n if (tool.type === 'provider') {\n toolWarnings.push({\n type: 'unsupported',\n feature: `provider-defined tool ${tool.id}`,\n });\n } else {\n openaiCompatTools.push({\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema,\n ...(tool.strict != null ? { strict: tool.strict } : {}),\n },\n });\n }\n }\n\n if (toolChoice == null) {\n return { tools: openaiCompatTools, toolChoice: undefined, toolWarnings };\n }\n\n const type = toolChoice.type;\n\n switch (type) {\n case 'auto':\n case 'none':\n case 'required':\n return { tools: openaiCompatTools, toolChoice: type, toolWarnings };\n case 'tool':\n return {\n tools: openaiCompatTools,\n toolChoice: {\n type: 'function',\n function: { name: toolChoice.toolName },\n },\n toolWarnings,\n };\n default: {\n const _exhaustiveCheck: never = type;\n throw new UnsupportedFunctionalityError({\n functionality: `tool choice type: ${_exhaustiveCheck}`,\n });\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["_exhaustiveCheck","_exhaustiveCheck"],"sources":["../../src/chat/convert-to-openai-compatible-chat-messages.ts","../../src/chat/convert-openai-compatible-chat-usage.ts","../../src/chat/map-openai-compatible-finish-reason.ts","../../src/chat/get-response-metadata.ts","../../src/chat/openai-compatible-prepare-tools.ts"],"sourcesContent":["import {\n UnsupportedFunctionalityError,\n type LanguageModelV4FilePart,\n type LanguageModelV4Prompt,\n type LanguageModelV4TextPart,\n type LanguageModelV4ToolResultOutput,\n type SharedV4ProviderMetadata,\n} from '@ai-sdk/provider';\nimport type {\n OpenAICompatibleChatPrompt,\n OpenAICompatibleContentPart,\n} from './openai-compatible-api-types';\nimport {\n convertBase64ToUint8Array,\n convertToBase64,\n getTopLevelMediaType,\n resolveFullMediaType,\n} from '@ai-sdk/provider-utils';\n\nfunction getOpenAIMetadata(message: {\n providerOptions?: SharedV4ProviderMetadata;\n}) {\n return message?.providerOptions?.openaiCompatible ?? {};\n}\n\nfunction getAudioFormat(mediaType: string): 'wav' | 'mp3' | null {\n switch (mediaType) {\n case 'audio/wav':\n return 'wav';\n case 'audio/mp3':\n case 'audio/mpeg':\n return 'mp3';\n default:\n return null;\n }\n}\n\nfunction convertToOpenAICompatibleContentPart(\n part: LanguageModelV4TextPart | LanguageModelV4FilePart,\n): OpenAICompatibleContentPart {\n const partMetadata = getOpenAIMetadata(part);\n\n switch (part.type) {\n case 'text': {\n return { type: 'text', text: part.text, ...partMetadata };\n }\n case 'file': {\n switch (part.data.type) {\n case 'reference': {\n throw new UnsupportedFunctionalityError({\n functionality: 'file parts with provider references',\n });\n }\n case 'text': {\n throw new UnsupportedFunctionalityError({\n functionality: 'text file parts',\n });\n }\n case 'url':\n case 'data': {\n const topLevel = getTopLevelMediaType(part.mediaType);\n\n if (topLevel === 'image') {\n return {\n type: 'image_url',\n image_url: {\n url:\n part.data.type === 'url'\n ? part.data.url.toString()\n : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'video') {\n return {\n type: 'video_url',\n video_url: {\n url:\n part.data.type === 'url'\n ? part.data.url.toString()\n : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'audio') {\n if (part.data.type === 'url') {\n throw new UnsupportedFunctionalityError({\n functionality: 'audio file parts with URLs',\n });\n }\n\n const fullMediaType = resolveFullMediaType({ part });\n const format = getAudioFormat(fullMediaType);\n if (format === null) {\n throw new UnsupportedFunctionalityError({\n functionality: `audio media type ${fullMediaType}`,\n });\n }\n\n return {\n type: 'input_audio',\n input_audio: {\n data: convertToBase64(part.data.data),\n format,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'application') {\n if (part.data.type === 'url') {\n throw new UnsupportedFunctionalityError({\n functionality: 'PDF file parts with URLs',\n });\n }\n\n const fullMediaType = resolveFullMediaType({ part });\n if (fullMediaType !== 'application/pdf') {\n throw new UnsupportedFunctionalityError({\n functionality: `file part media type ${fullMediaType}`,\n });\n }\n\n return {\n type: 'file',\n file: {\n filename: part.filename ?? 'document.pdf',\n file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`,\n },\n ...partMetadata,\n };\n }\n\n if (topLevel === 'text') {\n const textContent =\n part.data.type === 'url'\n ? part.data.url.toString()\n : typeof part.data.data === 'string'\n ? new TextDecoder().decode(\n convertBase64ToUint8Array(part.data.data),\n )\n : new TextDecoder().decode(part.data.data);\n\n return {\n type: 'text',\n text: textContent,\n ...partMetadata,\n };\n }\n\n throw new UnsupportedFunctionalityError({\n functionality: `file part media type ${part.mediaType}`,\n });\n }\n }\n }\n }\n}\n\nfunction convertToolContentPart(\n part: Extract<\n LanguageModelV4ToolResultOutput,\n { type: 'content' }\n >['value'][number],\n): OpenAICompatibleContentPart {\n if (part.type === 'custom') {\n throw new UnsupportedFunctionalityError({\n functionality: 'custom tool content parts',\n });\n }\n\n return convertToOpenAICompatibleContentPart(part);\n}\n\nexport function convertToOpenAICompatibleChatMessages(\n prompt: LanguageModelV4Prompt,\n {\n providerOptionsKey = 'google',\n supportsMultiPartToolContent = false,\n }: {\n providerOptionsKey?: string;\n supportsMultiPartToolContent?: boolean;\n } = {},\n): OpenAICompatibleChatPrompt {\n const messages: OpenAICompatibleChatPrompt = [];\n for (const { role, content, ...message } of prompt) {\n const metadata = getOpenAIMetadata({ ...message });\n switch (role) {\n case 'system': {\n messages.push({ role: 'system', content, ...metadata });\n break;\n }\n\n case 'user': {\n if (content.length === 1 && content[0].type === 'text') {\n messages.push({\n role: 'user',\n content: content[0].text,\n ...getOpenAIMetadata(content[0]),\n });\n break;\n }\n\n messages.push({\n role: 'user',\n content: content.map(convertToOpenAICompatibleContentPart),\n ...metadata,\n });\n\n break;\n }\n\n case 'assistant': {\n let text = '';\n let reasoning = '';\n const toolCalls: Array<{\n id: string;\n type: 'function';\n function: { name: string; arguments: string };\n extra_content?: {\n google?: {\n thought_signature?: string;\n };\n };\n }> = [];\n\n for (const part of content) {\n const partMetadata = getOpenAIMetadata(part);\n switch (part.type) {\n case 'text': {\n text += part.text;\n break;\n }\n case 'reasoning': {\n reasoning += part.text;\n break;\n }\n case 'tool-call': {\n // TODO: thoughtSignature should be abstracted once we add support for other providers\n const thoughtSignature =\n part.providerOptions?.[providerOptionsKey]?.thoughtSignature ??\n part.providerOptions?.google?.thoughtSignature;\n toolCalls.push({\n id: part.toolCallId,\n type: 'function',\n function: {\n name: part.toolName,\n arguments: JSON.stringify(part.input),\n },\n ...partMetadata,\n // Include extra_content for Google Gemini thought signatures\n ...(thoughtSignature\n ? {\n extra_content: {\n google: {\n thought_signature: String(thoughtSignature),\n },\n },\n }\n : {}),\n });\n break;\n }\n }\n }\n\n messages.push({\n role: 'assistant',\n content: toolCalls.length > 0 ? text || null : text,\n ...(reasoning.length > 0 ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n ...metadata,\n });\n\n break;\n }\n\n case 'tool': {\n for (const toolResponse of content) {\n if (toolResponse.type === 'tool-approval-response') {\n continue;\n }\n\n const output = toolResponse.output;\n\n let contentValue: string | Array<OpenAICompatibleContentPart>;\n switch (output.type) {\n case 'text':\n case 'error-text':\n contentValue = output.value;\n break;\n case 'execution-denied':\n contentValue = output.reason ?? 'Tool call execution denied.';\n break;\n case 'json':\n case 'error-json':\n contentValue = JSON.stringify(output.value);\n break;\n case 'content':\n contentValue = supportsMultiPartToolContent\n ? output.value.map(convertToolContentPart)\n : JSON.stringify(output.value);\n break;\n }\n\n const toolResponseMetadata = getOpenAIMetadata(toolResponse);\n messages.push({\n role: 'tool',\n tool_call_id: toolResponse.toolCallId,\n content: contentValue,\n ...toolResponseMetadata,\n });\n }\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = role;\n throw new Error(`Unsupported role: ${_exhaustiveCheck}`);\n }\n }\n }\n\n return messages;\n}\n","import type { LanguageModelV4Usage } from '@ai-sdk/provider';\nimport { createNullLanguageModelUsage } from '@ai-sdk/provider-utils';\n\nexport function convertOpenAICompatibleChatUsage(\n usage:\n | {\n prompt_tokens?: number | null;\n completion_tokens?: number | null;\n prompt_tokens_details?: {\n cached_tokens?: number | null;\n } | null;\n completion_tokens_details?: {\n reasoning_tokens?: number | null;\n } | null;\n }\n | undefined\n | null,\n): LanguageModelV4Usage {\n if (usage == null) {\n return createNullLanguageModelUsage();\n }\n\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;\n const reasoningTokens =\n usage.completion_tokens_details?.reasoning_tokens ?? 0;\n\n return {\n inputTokens: {\n total: promptTokens,\n noCache: promptTokens - cacheReadTokens,\n cacheRead: cacheReadTokens,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: completionTokens,\n text: Math.max(0, completionTokens - reasoningTokens),\n reasoning: reasoningTokens,\n },\n raw: usage,\n };\n}\n","import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';\n\nexport function mapOpenAICompatibleFinishReason(\n finishReason: string | null | undefined,\n): LanguageModelV4FinishReason['unified'] {\n switch (finishReason) {\n case 'stop':\n return 'stop';\n case 'length':\n return 'length';\n case 'content_filter':\n return 'content-filter';\n case 'function_call':\n case 'tool_calls':\n return 'tool-calls';\n default:\n return 'other';\n }\n}\n","import { createLanguageModelResponseMetadata } from '@ai-sdk/provider-utils';\n\nexport function getResponseMetadata({\n id,\n model,\n created,\n}: {\n id?: string | null;\n model?: string | null;\n created?: number | null;\n}) {\n return createLanguageModelResponseMetadata({\n id,\n model,\n // Some OpenAI-compatible providers use 0 as a placeholder timestamp.\n created: created || undefined,\n });\n}\n","import {\n UnsupportedFunctionalityError,\n type LanguageModelV4CallOptions,\n type SharedV4Warning,\n} from '@ai-sdk/provider';\nexport function prepareTools({\n tools,\n toolChoice,\n}: {\n tools: LanguageModelV4CallOptions['tools'];\n toolChoice?: LanguageModelV4CallOptions['toolChoice'];\n}): {\n tools:\n | undefined\n | Array<{\n type: 'function';\n function: {\n name: string;\n description: string | undefined;\n parameters: unknown;\n strict?: boolean;\n };\n }>;\n toolChoice:\n | { type: 'function'; function: { name: string } }\n | 'auto'\n | 'none'\n | 'required'\n | undefined;\n toolWarnings: SharedV4Warning[];\n} {\n // when the tools array is empty, change it to undefined to prevent errors:\n tools = tools?.length ? tools : undefined;\n\n const toolWarnings: SharedV4Warning[] = [];\n\n if (tools == null) {\n return { tools: undefined, toolChoice: undefined, toolWarnings };\n }\n\n const openaiCompatTools: Array<{\n type: 'function';\n function: {\n name: string;\n description: string | undefined;\n parameters: unknown;\n strict?: boolean;\n };\n }> = [];\n\n for (const tool of tools) {\n if (tool.type === 'provider') {\n toolWarnings.push({\n type: 'unsupported',\n feature: `provider-defined tool ${tool.id}`,\n });\n } else {\n openaiCompatTools.push({\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema,\n ...(tool.strict != null ? { strict: tool.strict } : {}),\n },\n });\n }\n }\n\n if (toolChoice == null) {\n return { tools: openaiCompatTools, toolChoice: undefined, toolWarnings };\n }\n\n const type = toolChoice.type;\n\n switch (type) {\n case 'auto':\n case 'none':\n case 'required':\n return { tools: openaiCompatTools, toolChoice: type, toolWarnings };\n case 'tool':\n return {\n tools: openaiCompatTools,\n toolChoice: {\n type: 'function',\n function: { name: toolChoice.toolName },\n },\n toolWarnings,\n };\n default: {\n const _exhaustiveCheck: never = type;\n throw new UnsupportedFunctionalityError({\n functionality: `tool choice type: ${_exhaustiveCheck}`,\n });\n }\n }\n}\n"],"mappings":"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"}
package/docs/index.mdx CHANGED
@@ -78,6 +78,14 @@ You can use the following optional settings to customize the provider instance:
78
78
 
79
79
  Set to true if the provider supports structured outputs. Only relevant for `provider()`, `provider.chatModel()`, and `provider.languageModel()`.
80
80
 
81
+ - **supportsMultiPartToolContent** _boolean_
82
+
83
+ Set to true if the provider supports multi-part content arrays in tool
84
+ results. When enabled, tool outputs with `type: 'content'` can contain text,
85
+ images, video, audio, and PDF content parts. Defaults to `false`. Only
86
+ relevant for `provider()`, `provider.chatModel()`, and
87
+ `provider.languageModel()`.
88
+
81
89
  - **supportedUrls** _() =&gt; Record&lt;string, RegExp[]&gt;_
82
90
 
83
91
  Defines URLs that chat models can access directly, grouped by media type.
@@ -121,6 +129,22 @@ Chat models created with this provider support the following capabilities:
121
129
  - **System messages** - Support for system prompts
122
130
  - **Multi-modal inputs** - Support for images and other content types (provider-dependent)
123
131
 
132
+ Some providers extend the OpenAI Chat Completions API with multi-part tool
133
+ result content. You can opt into this behavior when creating the provider:
134
+
135
+ ```ts
136
+ const provider = createOpenAICompatible({
137
+ name: 'providerName',
138
+ apiKey: process.env.PROVIDER_API_KEY,
139
+ baseURL: 'https://api.provider.com/v1',
140
+ supportsMultiPartToolContent: true,
141
+ });
142
+ ```
143
+
144
+ Only enable `supportsMultiPartToolContent` when the provider accepts content
145
+ arrays for messages with the `tool` role. The default stringifies multi-part
146
+ tool results for compatibility with the OpenAI Chat Completions API.
147
+
124
148
  ### Example
125
149
 
126
150
  You can use provider language models to generate text with the `generateText` function:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai-compatible",
3
- "version": "3.0.60",
3
+ "version": "3.0.62",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,8 +35,8 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.20",
39
- "@ai-sdk/provider-utils": "5.0.52"
38
+ "@ai-sdk/provider": "4.0.21",
39
+ "@ai-sdk/provider-utils": "5.0.53"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.3",
@@ -1,9 +1,15 @@
1
1
  import {
2
2
  UnsupportedFunctionalityError,
3
+ type LanguageModelV4FilePart,
3
4
  type LanguageModelV4Prompt,
5
+ type LanguageModelV4TextPart,
6
+ type LanguageModelV4ToolResultOutput,
4
7
  type SharedV4ProviderMetadata,
5
8
  } from '@ai-sdk/provider';
6
- import type { OpenAICompatibleChatPrompt } from './openai-compatible-api-types';
9
+ import type {
10
+ OpenAICompatibleChatPrompt,
11
+ OpenAICompatibleContentPart,
12
+ } from './openai-compatible-api-types';
7
13
  import {
8
14
  convertBase64ToUint8Array,
9
15
  convertToBase64,
@@ -29,9 +35,156 @@ function getAudioFormat(mediaType: string): 'wav' | 'mp3' | null {
29
35
  }
30
36
  }
31
37
 
38
+ function convertToOpenAICompatibleContentPart(
39
+ part: LanguageModelV4TextPart | LanguageModelV4FilePart,
40
+ ): OpenAICompatibleContentPart {
41
+ const partMetadata = getOpenAIMetadata(part);
42
+
43
+ switch (part.type) {
44
+ case 'text': {
45
+ return { type: 'text', text: part.text, ...partMetadata };
46
+ }
47
+ case 'file': {
48
+ switch (part.data.type) {
49
+ case 'reference': {
50
+ throw new UnsupportedFunctionalityError({
51
+ functionality: 'file parts with provider references',
52
+ });
53
+ }
54
+ case 'text': {
55
+ throw new UnsupportedFunctionalityError({
56
+ functionality: 'text file parts',
57
+ });
58
+ }
59
+ case 'url':
60
+ case 'data': {
61
+ const topLevel = getTopLevelMediaType(part.mediaType);
62
+
63
+ if (topLevel === 'image') {
64
+ return {
65
+ type: 'image_url',
66
+ image_url: {
67
+ url:
68
+ part.data.type === 'url'
69
+ ? part.data.url.toString()
70
+ : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
71
+ },
72
+ ...partMetadata,
73
+ };
74
+ }
75
+
76
+ if (topLevel === 'video') {
77
+ return {
78
+ type: 'video_url',
79
+ video_url: {
80
+ url:
81
+ part.data.type === 'url'
82
+ ? part.data.url.toString()
83
+ : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
84
+ },
85
+ ...partMetadata,
86
+ };
87
+ }
88
+
89
+ if (topLevel === 'audio') {
90
+ if (part.data.type === 'url') {
91
+ throw new UnsupportedFunctionalityError({
92
+ functionality: 'audio file parts with URLs',
93
+ });
94
+ }
95
+
96
+ const fullMediaType = resolveFullMediaType({ part });
97
+ const format = getAudioFormat(fullMediaType);
98
+ if (format === null) {
99
+ throw new UnsupportedFunctionalityError({
100
+ functionality: `audio media type ${fullMediaType}`,
101
+ });
102
+ }
103
+
104
+ return {
105
+ type: 'input_audio',
106
+ input_audio: {
107
+ data: convertToBase64(part.data.data),
108
+ format,
109
+ },
110
+ ...partMetadata,
111
+ };
112
+ }
113
+
114
+ if (topLevel === 'application') {
115
+ if (part.data.type === 'url') {
116
+ throw new UnsupportedFunctionalityError({
117
+ functionality: 'PDF file parts with URLs',
118
+ });
119
+ }
120
+
121
+ const fullMediaType = resolveFullMediaType({ part });
122
+ if (fullMediaType !== 'application/pdf') {
123
+ throw new UnsupportedFunctionalityError({
124
+ functionality: `file part media type ${fullMediaType}`,
125
+ });
126
+ }
127
+
128
+ return {
129
+ type: 'file',
130
+ file: {
131
+ filename: part.filename ?? 'document.pdf',
132
+ file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`,
133
+ },
134
+ ...partMetadata,
135
+ };
136
+ }
137
+
138
+ if (topLevel === 'text') {
139
+ const textContent =
140
+ part.data.type === 'url'
141
+ ? part.data.url.toString()
142
+ : typeof part.data.data === 'string'
143
+ ? new TextDecoder().decode(
144
+ convertBase64ToUint8Array(part.data.data),
145
+ )
146
+ : new TextDecoder().decode(part.data.data);
147
+
148
+ return {
149
+ type: 'text',
150
+ text: textContent,
151
+ ...partMetadata,
152
+ };
153
+ }
154
+
155
+ throw new UnsupportedFunctionalityError({
156
+ functionality: `file part media type ${part.mediaType}`,
157
+ });
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ function convertToolContentPart(
165
+ part: Extract<
166
+ LanguageModelV4ToolResultOutput,
167
+ { type: 'content' }
168
+ >['value'][number],
169
+ ): OpenAICompatibleContentPart {
170
+ if (part.type === 'custom') {
171
+ throw new UnsupportedFunctionalityError({
172
+ functionality: 'custom tool content parts',
173
+ });
174
+ }
175
+
176
+ return convertToOpenAICompatibleContentPart(part);
177
+ }
178
+
32
179
  export function convertToOpenAICompatibleChatMessages(
33
180
  prompt: LanguageModelV4Prompt,
34
- { providerOptionsKey = 'google' }: { providerOptionsKey?: string } = {},
181
+ {
182
+ providerOptionsKey = 'google',
183
+ supportsMultiPartToolContent = false,
184
+ }: {
185
+ providerOptionsKey?: string;
186
+ supportsMultiPartToolContent?: boolean;
187
+ } = {},
35
188
  ): OpenAICompatibleChatPrompt {
36
189
  const messages: OpenAICompatibleChatPrompt = [];
37
190
  for (const { role, content, ...message } of prompt) {
@@ -54,128 +207,7 @@ export function convertToOpenAICompatibleChatMessages(
54
207
 
55
208
  messages.push({
56
209
  role: 'user',
57
- content: content.map(part => {
58
- const partMetadata = getOpenAIMetadata(part);
59
- switch (part.type) {
60
- case 'text': {
61
- return { type: 'text', text: part.text, ...partMetadata };
62
- }
63
- case 'file': {
64
- switch (part.data.type) {
65
- case 'reference': {
66
- throw new UnsupportedFunctionalityError({
67
- functionality: 'file parts with provider references',
68
- });
69
- }
70
- case 'text': {
71
- throw new UnsupportedFunctionalityError({
72
- functionality: 'text file parts',
73
- });
74
- }
75
- case 'url':
76
- case 'data': {
77
- const topLevel = getTopLevelMediaType(part.mediaType);
78
-
79
- if (topLevel === 'image') {
80
- return {
81
- type: 'image_url',
82
- image_url: {
83
- url:
84
- part.data.type === 'url'
85
- ? part.data.url.toString()
86
- : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
87
- },
88
- ...partMetadata,
89
- };
90
- }
91
-
92
- if (topLevel === 'video') {
93
- return {
94
- type: 'video_url',
95
- video_url: {
96
- url:
97
- part.data.type === 'url'
98
- ? part.data.url.toString()
99
- : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
100
- },
101
- ...partMetadata,
102
- };
103
- }
104
-
105
- if (topLevel === 'audio') {
106
- if (part.data.type === 'url') {
107
- throw new UnsupportedFunctionalityError({
108
- functionality: 'audio file parts with URLs',
109
- });
110
- }
111
-
112
- const fullMediaType = resolveFullMediaType({ part });
113
- const format = getAudioFormat(fullMediaType);
114
- if (format === null) {
115
- throw new UnsupportedFunctionalityError({
116
- functionality: `audio media type ${fullMediaType}`,
117
- });
118
- }
119
-
120
- return {
121
- type: 'input_audio',
122
- input_audio: {
123
- data: convertToBase64(part.data.data),
124
- format,
125
- },
126
- ...partMetadata,
127
- };
128
- }
129
-
130
- if (topLevel === 'application') {
131
- if (part.data.type === 'url') {
132
- throw new UnsupportedFunctionalityError({
133
- functionality: 'PDF file parts with URLs',
134
- });
135
- }
136
-
137
- const fullMediaType = resolveFullMediaType({ part });
138
- if (fullMediaType !== 'application/pdf') {
139
- throw new UnsupportedFunctionalityError({
140
- functionality: `file part media type ${fullMediaType}`,
141
- });
142
- }
143
-
144
- return {
145
- type: 'file',
146
- file: {
147
- filename: part.filename ?? 'document.pdf',
148
- file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`,
149
- },
150
- ...partMetadata,
151
- };
152
- }
153
-
154
- if (topLevel === 'text') {
155
- const textContent =
156
- part.data.type === 'url'
157
- ? part.data.url.toString()
158
- : typeof part.data.data === 'string'
159
- ? new TextDecoder().decode(
160
- convertBase64ToUint8Array(part.data.data),
161
- )
162
- : new TextDecoder().decode(part.data.data);
163
-
164
- return {
165
- type: 'text',
166
- text: textContent,
167
- ...partMetadata,
168
- };
169
- }
170
-
171
- throw new UnsupportedFunctionalityError({
172
- functionality: `file part media type ${part.mediaType}`,
173
- });
174
- }
175
- }
176
- }
177
- }
178
- }),
210
+ content: content.map(convertToOpenAICompatibleContentPart),
179
211
  ...metadata,
180
212
  });
181
213
 
@@ -255,7 +287,7 @@ export function convertToOpenAICompatibleChatMessages(
255
287
 
256
288
  const output = toolResponse.output;
257
289
 
258
- let contentValue: string;
290
+ let contentValue: string | Array<OpenAICompatibleContentPart>;
259
291
  switch (output.type) {
260
292
  case 'text':
261
293
  case 'error-text':
@@ -264,11 +296,15 @@ export function convertToOpenAICompatibleChatMessages(
264
296
  case 'execution-denied':
265
297
  contentValue = output.reason ?? 'Tool call execution denied.';
266
298
  break;
267
- case 'content':
268
299
  case 'json':
269
300
  case 'error-json':
270
301
  contentValue = JSON.stringify(output.value);
271
302
  break;
303
+ case 'content':
304
+ contentValue = supportsMultiPartToolContent
305
+ ? output.value.map(convertToolContentPart)
306
+ : JSON.stringify(output.value);
307
+ break;
272
308
  }
273
309
 
274
310
  const toolResponseMetadata = getOpenAIMetadata(toolResponse);
@@ -87,6 +87,6 @@ export interface OpenAICompatibleMessageToolCall extends JsonRecord {
87
87
 
88
88
  export interface OpenAICompatibleToolMessage extends JsonRecord {
89
89
  role: 'tool';
90
- content: string;
90
+ content: string | Array<OpenAICompatibleContentPart>;
91
91
  tool_call_id: string;
92
92
  }