@ai-sdk/openai-compatible 3.0.58 → 3.0.60

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,359 +1,267 @@
1
- // src/chat/convert-to-openai-compatible-chat-messages.ts
2
- import {
3
- UnsupportedFunctionalityError
4
- } from "@ai-sdk/provider";
5
- import {
6
- convertBase64ToUint8Array,
7
- convertToBase64,
8
- getTopLevelMediaType,
9
- resolveFullMediaType
10
- } from "@ai-sdk/provider-utils";
1
+ import { UnsupportedFunctionalityError } from "@ai-sdk/provider";
2
+ import { convertBase64ToUint8Array, convertToBase64, createLanguageModelResponseMetadata, createNullLanguageModelUsage, getTopLevelMediaType, resolveFullMediaType } from "@ai-sdk/provider-utils";
3
+ //#region src/chat/convert-to-openai-compatible-chat-messages.ts
11
4
  function getOpenAIMetadata(message) {
12
- return message?.providerOptions?.openaiCompatible ?? {};
5
+ return message?.providerOptions?.openaiCompatible ?? {};
13
6
  }
14
7
  function getAudioFormat(mediaType) {
15
- switch (mediaType) {
16
- case "audio/wav":
17
- return "wav";
18
- case "audio/mp3":
19
- case "audio/mpeg":
20
- return "mp3";
21
- default:
22
- return null;
23
- }
8
+ switch (mediaType) {
9
+ case "audio/wav": return "wav";
10
+ case "audio/mp3":
11
+ case "audio/mpeg": return "mp3";
12
+ default: return null;
13
+ }
24
14
  }
25
15
  function convertToOpenAICompatibleChatMessages(prompt, { providerOptionsKey = "google" } = {}) {
26
- const messages = [];
27
- for (const { role, content, ...message } of prompt) {
28
- const metadata = getOpenAIMetadata({ ...message });
29
- switch (role) {
30
- case "system": {
31
- messages.push({ role: "system", content, ...metadata });
32
- break;
33
- }
34
- case "user": {
35
- if (content.length === 1 && content[0].type === "text") {
36
- messages.push({
37
- role: "user",
38
- content: content[0].text,
39
- ...getOpenAIMetadata(content[0])
40
- });
41
- break;
42
- }
43
- messages.push({
44
- role: "user",
45
- content: content.map((part) => {
46
- const partMetadata = getOpenAIMetadata(part);
47
- switch (part.type) {
48
- case "text": {
49
- return { type: "text", text: part.text, ...partMetadata };
50
- }
51
- case "file": {
52
- switch (part.data.type) {
53
- case "reference": {
54
- throw new UnsupportedFunctionalityError({
55
- functionality: "file parts with provider references"
56
- });
57
- }
58
- case "text": {
59
- throw new UnsupportedFunctionalityError({
60
- functionality: "text file parts"
61
- });
62
- }
63
- case "url":
64
- case "data": {
65
- const topLevel = getTopLevelMediaType(part.mediaType);
66
- if (topLevel === "image") {
67
- return {
68
- type: "image_url",
69
- image_url: {
70
- url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`
71
- },
72
- ...partMetadata
73
- };
74
- }
75
- if (topLevel === "video") {
76
- return {
77
- type: "video_url",
78
- video_url: {
79
- url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`
80
- },
81
- ...partMetadata
82
- };
83
- }
84
- if (topLevel === "audio") {
85
- if (part.data.type === "url") {
86
- throw new UnsupportedFunctionalityError({
87
- functionality: "audio file parts with URLs"
88
- });
89
- }
90
- const fullMediaType = resolveFullMediaType({ part });
91
- const format = getAudioFormat(fullMediaType);
92
- if (format === null) {
93
- throw new UnsupportedFunctionalityError({
94
- functionality: `audio media type ${fullMediaType}`
95
- });
96
- }
97
- return {
98
- type: "input_audio",
99
- input_audio: {
100
- data: convertToBase64(part.data.data),
101
- format
102
- },
103
- ...partMetadata
104
- };
105
- }
106
- if (topLevel === "application") {
107
- if (part.data.type === "url") {
108
- throw new UnsupportedFunctionalityError({
109
- functionality: "PDF file parts with URLs"
110
- });
111
- }
112
- const fullMediaType = resolveFullMediaType({ part });
113
- if (fullMediaType !== "application/pdf") {
114
- throw new UnsupportedFunctionalityError({
115
- functionality: `file part media type ${fullMediaType}`
116
- });
117
- }
118
- return {
119
- type: "file",
120
- file: {
121
- filename: part.filename ?? "document.pdf",
122
- file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`
123
- },
124
- ...partMetadata
125
- };
126
- }
127
- if (topLevel === "text") {
128
- const textContent = part.data.type === "url" ? part.data.url.toString() : typeof part.data.data === "string" ? new TextDecoder().decode(
129
- convertBase64ToUint8Array(part.data.data)
130
- ) : new TextDecoder().decode(part.data.data);
131
- return {
132
- type: "text",
133
- text: textContent,
134
- ...partMetadata
135
- };
136
- }
137
- throw new UnsupportedFunctionalityError({
138
- functionality: `file part media type ${part.mediaType}`
139
- });
140
- }
141
- }
142
- }
143
- }
144
- }),
145
- ...metadata
146
- });
147
- break;
148
- }
149
- case "assistant": {
150
- let text = "";
151
- let reasoning = "";
152
- const toolCalls = [];
153
- for (const part of content) {
154
- const partMetadata = getOpenAIMetadata(part);
155
- switch (part.type) {
156
- case "text": {
157
- text += part.text;
158
- break;
159
- }
160
- case "reasoning": {
161
- reasoning += part.text;
162
- break;
163
- }
164
- case "tool-call": {
165
- const thoughtSignature = part.providerOptions?.[providerOptionsKey]?.thoughtSignature ?? part.providerOptions?.google?.thoughtSignature;
166
- toolCalls.push({
167
- id: part.toolCallId,
168
- type: "function",
169
- function: {
170
- name: part.toolName,
171
- arguments: JSON.stringify(part.input)
172
- },
173
- ...partMetadata,
174
- // Include extra_content for Google Gemini thought signatures
175
- ...thoughtSignature ? {
176
- extra_content: {
177
- google: {
178
- thought_signature: String(thoughtSignature)
179
- }
180
- }
181
- } : {}
182
- });
183
- break;
184
- }
185
- }
186
- }
187
- messages.push({
188
- role: "assistant",
189
- content: toolCalls.length > 0 ? text || null : text,
190
- ...reasoning.length > 0 ? { reasoning_content: reasoning } : {},
191
- tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
192
- ...metadata
193
- });
194
- break;
195
- }
196
- case "tool": {
197
- for (const toolResponse of content) {
198
- if (toolResponse.type === "tool-approval-response") {
199
- continue;
200
- }
201
- const output = toolResponse.output;
202
- let contentValue;
203
- switch (output.type) {
204
- case "text":
205
- case "error-text":
206
- contentValue = output.value;
207
- break;
208
- case "execution-denied":
209
- contentValue = output.reason ?? "Tool call execution denied.";
210
- break;
211
- case "content":
212
- case "json":
213
- case "error-json":
214
- contentValue = JSON.stringify(output.value);
215
- break;
216
- }
217
- const toolResponseMetadata = getOpenAIMetadata(toolResponse);
218
- messages.push({
219
- role: "tool",
220
- tool_call_id: toolResponse.toolCallId,
221
- content: contentValue,
222
- ...toolResponseMetadata
223
- });
224
- }
225
- break;
226
- }
227
- default: {
228
- const _exhaustiveCheck = role;
229
- throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
230
- }
231
- }
232
- }
233
- return messages;
16
+ const messages = [];
17
+ for (const { role, content, ...message } of prompt) {
18
+ const metadata = getOpenAIMetadata({ ...message });
19
+ switch (role) {
20
+ case "system":
21
+ messages.push({
22
+ role: "system",
23
+ content,
24
+ ...metadata
25
+ });
26
+ break;
27
+ case "user":
28
+ if (content.length === 1 && content[0].type === "text") {
29
+ messages.push({
30
+ role: "user",
31
+ content: content[0].text,
32
+ ...getOpenAIMetadata(content[0])
33
+ });
34
+ break;
35
+ }
36
+ messages.push({
37
+ 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
+ }),
99
+ ...metadata
100
+ });
101
+ break;
102
+ case "assistant": {
103
+ let text = "";
104
+ let reasoning = "";
105
+ const toolCalls = [];
106
+ for (const part of content) {
107
+ const partMetadata = getOpenAIMetadata(part);
108
+ switch (part.type) {
109
+ case "text":
110
+ text += part.text;
111
+ break;
112
+ case "reasoning":
113
+ reasoning += part.text;
114
+ break;
115
+ case "tool-call": {
116
+ const thoughtSignature = part.providerOptions?.[providerOptionsKey]?.thoughtSignature ?? part.providerOptions?.google?.thoughtSignature;
117
+ toolCalls.push({
118
+ id: part.toolCallId,
119
+ type: "function",
120
+ function: {
121
+ name: part.toolName,
122
+ arguments: JSON.stringify(part.input)
123
+ },
124
+ ...partMetadata,
125
+ ...thoughtSignature ? { extra_content: { google: { thought_signature: String(thoughtSignature) } } } : {}
126
+ });
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ messages.push({
132
+ role: "assistant",
133
+ content: toolCalls.length > 0 ? text || null : text,
134
+ ...reasoning.length > 0 ? { reasoning_content: reasoning } : {},
135
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
136
+ ...metadata
137
+ });
138
+ break;
139
+ }
140
+ case "tool":
141
+ for (const toolResponse of content) {
142
+ if (toolResponse.type === "tool-approval-response") continue;
143
+ const output = toolResponse.output;
144
+ let contentValue;
145
+ switch (output.type) {
146
+ case "text":
147
+ case "error-text":
148
+ contentValue = output.value;
149
+ break;
150
+ case "execution-denied":
151
+ contentValue = output.reason ?? "Tool call execution denied.";
152
+ break;
153
+ case "content":
154
+ case "json":
155
+ case "error-json": contentValue = JSON.stringify(output.value);
156
+ }
157
+ const toolResponseMetadata = getOpenAIMetadata(toolResponse);
158
+ messages.push({
159
+ role: "tool",
160
+ tool_call_id: toolResponse.toolCallId,
161
+ content: contentValue,
162
+ ...toolResponseMetadata
163
+ });
164
+ }
165
+ break;
166
+ default: throw new Error(`Unsupported role: ${role}`);
167
+ }
168
+ }
169
+ return messages;
234
170
  }
235
-
236
- // src/chat/convert-openai-compatible-chat-usage.ts
237
- import { createNullLanguageModelUsage } from "@ai-sdk/provider-utils";
171
+ //#endregion
172
+ //#region src/chat/convert-openai-compatible-chat-usage.ts
238
173
  function convertOpenAICompatibleChatUsage(usage) {
239
- if (usage == null) {
240
- return createNullLanguageModelUsage();
241
- }
242
- const promptTokens = usage.prompt_tokens ?? 0;
243
- const completionTokens = usage.completion_tokens ?? 0;
244
- const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
245
- const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? 0;
246
- return {
247
- inputTokens: {
248
- total: promptTokens,
249
- noCache: promptTokens - cacheReadTokens,
250
- cacheRead: cacheReadTokens,
251
- cacheWrite: void 0
252
- },
253
- outputTokens: {
254
- total: completionTokens,
255
- text: Math.max(0, completionTokens - reasoningTokens),
256
- reasoning: reasoningTokens
257
- },
258
- raw: usage
259
- };
174
+ if (usage == null) return createNullLanguageModelUsage();
175
+ const promptTokens = usage.prompt_tokens ?? 0;
176
+ const completionTokens = usage.completion_tokens ?? 0;
177
+ const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
178
+ const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? 0;
179
+ return {
180
+ inputTokens: {
181
+ total: promptTokens,
182
+ noCache: promptTokens - cacheReadTokens,
183
+ cacheRead: cacheReadTokens,
184
+ cacheWrite: void 0
185
+ },
186
+ outputTokens: {
187
+ total: completionTokens,
188
+ text: Math.max(0, completionTokens - reasoningTokens),
189
+ reasoning: reasoningTokens
190
+ },
191
+ raw: usage
192
+ };
260
193
  }
261
-
262
- // src/chat/map-openai-compatible-finish-reason.ts
194
+ //#endregion
195
+ //#region src/chat/map-openai-compatible-finish-reason.ts
263
196
  function mapOpenAICompatibleFinishReason(finishReason) {
264
- switch (finishReason) {
265
- case "stop":
266
- return "stop";
267
- case "length":
268
- return "length";
269
- case "content_filter":
270
- return "content-filter";
271
- case "function_call":
272
- case "tool_calls":
273
- return "tool-calls";
274
- default:
275
- return "other";
276
- }
197
+ switch (finishReason) {
198
+ case "stop": return "stop";
199
+ case "length": return "length";
200
+ case "content_filter": return "content-filter";
201
+ case "function_call":
202
+ case "tool_calls": return "tool-calls";
203
+ default: return "other";
204
+ }
277
205
  }
278
-
279
- // src/chat/get-response-metadata.ts
280
- import { createLanguageModelResponseMetadata } from "@ai-sdk/provider-utils";
281
- function getResponseMetadata({
282
- id,
283
- model,
284
- created
285
- }) {
286
- return createLanguageModelResponseMetadata({
287
- id,
288
- model,
289
- // Some OpenAI-compatible providers use 0 as a placeholder timestamp.
290
- created: created || void 0
291
- });
206
+ //#endregion
207
+ //#region src/chat/get-response-metadata.ts
208
+ function getResponseMetadata({ id, model, created }) {
209
+ return createLanguageModelResponseMetadata({
210
+ id,
211
+ model,
212
+ created: created || void 0
213
+ });
292
214
  }
293
-
294
- // src/chat/openai-compatible-prepare-tools.ts
295
- import {
296
- UnsupportedFunctionalityError as UnsupportedFunctionalityError2
297
- } from "@ai-sdk/provider";
298
- function prepareTools({
299
- tools,
300
- toolChoice
301
- }) {
302
- tools = tools?.length ? tools : void 0;
303
- const toolWarnings = [];
304
- if (tools == null) {
305
- return { tools: void 0, toolChoice: void 0, toolWarnings };
306
- }
307
- const openaiCompatTools = [];
308
- for (const tool of tools) {
309
- if (tool.type === "provider") {
310
- toolWarnings.push({
311
- type: "unsupported",
312
- feature: `provider-defined tool ${tool.id}`
313
- });
314
- } else {
315
- openaiCompatTools.push({
316
- type: "function",
317
- function: {
318
- name: tool.name,
319
- description: tool.description,
320
- parameters: tool.inputSchema,
321
- ...tool.strict != null ? { strict: tool.strict } : {}
322
- }
323
- });
324
- }
325
- }
326
- if (toolChoice == null) {
327
- return { tools: openaiCompatTools, toolChoice: void 0, toolWarnings };
328
- }
329
- const type = toolChoice.type;
330
- switch (type) {
331
- case "auto":
332
- case "none":
333
- case "required":
334
- return { tools: openaiCompatTools, toolChoice: type, toolWarnings };
335
- case "tool":
336
- return {
337
- tools: openaiCompatTools,
338
- toolChoice: {
339
- type: "function",
340
- function: { name: toolChoice.toolName }
341
- },
342
- toolWarnings
343
- };
344
- default: {
345
- const _exhaustiveCheck = type;
346
- throw new UnsupportedFunctionalityError2({
347
- functionality: `tool choice type: ${_exhaustiveCheck}`
348
- });
349
- }
350
- }
215
+ //#endregion
216
+ //#region src/chat/openai-compatible-prepare-tools.ts
217
+ function prepareTools({ tools, toolChoice }) {
218
+ tools = tools?.length ? tools : void 0;
219
+ const toolWarnings = [];
220
+ if (tools == null) return {
221
+ tools: void 0,
222
+ toolChoice: void 0,
223
+ toolWarnings
224
+ };
225
+ const openaiCompatTools = [];
226
+ for (const tool of tools) if (tool.type === "provider") toolWarnings.push({
227
+ type: "unsupported",
228
+ feature: `provider-defined tool ${tool.id}`
229
+ });
230
+ else openaiCompatTools.push({
231
+ type: "function",
232
+ function: {
233
+ name: tool.name,
234
+ description: tool.description,
235
+ parameters: tool.inputSchema,
236
+ ...tool.strict != null ? { strict: tool.strict } : {}
237
+ }
238
+ });
239
+ if (toolChoice == null) return {
240
+ tools: openaiCompatTools,
241
+ toolChoice: void 0,
242
+ toolWarnings
243
+ };
244
+ const type = toolChoice.type;
245
+ switch (type) {
246
+ case "auto":
247
+ case "none":
248
+ case "required": return {
249
+ tools: openaiCompatTools,
250
+ toolChoice: type,
251
+ toolWarnings
252
+ };
253
+ case "tool": return {
254
+ tools: openaiCompatTools,
255
+ toolChoice: {
256
+ type: "function",
257
+ function: { name: toolChoice.toolName }
258
+ },
259
+ toolWarnings
260
+ };
261
+ default: throw new UnsupportedFunctionalityError({ functionality: `tool choice type: ${type}` });
262
+ }
351
263
  }
352
- export {
353
- convertOpenAICompatibleChatUsage,
354
- convertToOpenAICompatibleChatMessages,
355
- getResponseMetadata,
356
- mapOpenAICompatibleFinishReason,
357
- prepareTools
358
- };
264
+ //#endregion
265
+ export { convertOpenAICompatibleChatUsage, convertToOpenAICompatibleChatMessages, getResponseMetadata, mapOpenAICompatibleFinishReason, prepareTools };
266
+
359
267
  //# sourceMappingURL=index.js.map