@ai-sdk/openai-compatible 3.0.61 → 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.
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +77 -66
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +7 -2
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +71 -64
- package/dist/internal/index.js.map +1 -1
- package/docs/index.mdx +24 -0
- package/package.json +1 -1
- package/src/chat/convert-to-openai-compatible-chat-messages.ts +162 -126
- package/src/chat/openai-compatible-api-types.ts +1 -1
- package/src/chat/openai-compatible-chat-language-model.ts +7 -0
- package/src/openai-compatible-provider.ts +9 -0
|
@@ -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;
|
|
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"}
|
package/dist/internal/index.js
CHANGED
|
@@ -12,7 +12,72 @@ function getAudioFormat(mediaType) {
|
|
|
12
12
|
default: return null;
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
function
|
|
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(
|
|
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":
|
|
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** _() => Record<string, RegExp[]>_
|
|
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,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 {
|
|
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
|
-
{
|
|
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(
|
|
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
|
}
|