@ai-sdk/openai-compatible 3.0.0-beta.5 → 3.0.0-beta.57

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +432 -8
  2. package/README.md +2 -0
  3. package/dist/index.d.ts +69 -8
  4. package/dist/index.js +548 -428
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +20 -2
  7. package/dist/internal/index.js +91 -91
  8. package/dist/internal/index.js.map +1 -1
  9. package/docs/index.mdx +1 -0
  10. package/package.json +16 -17
  11. package/src/chat/convert-openai-compatible-chat-usage.ts +1 -1
  12. package/src/chat/convert-to-openai-compatible-chat-messages.ts +94 -73
  13. package/src/chat/map-openai-compatible-finish-reason.ts +1 -1
  14. package/src/chat/openai-compatible-api-types.ts +3 -5
  15. package/src/chat/openai-compatible-chat-language-model.ts +205 -191
  16. package/src/chat/openai-compatible-metadata-extractor.ts +1 -1
  17. package/src/chat/openai-compatible-prepare-tools.ts +2 -3
  18. package/src/completion/convert-openai-compatible-completion-usage.ts +1 -1
  19. package/src/completion/convert-to-openai-compatible-completion-prompt.ts +1 -2
  20. package/src/completion/map-openai-compatible-finish-reason.ts +1 -1
  21. package/src/completion/openai-compatible-completion-language-model.ts +52 -17
  22. package/src/embedding/openai-compatible-embedding-model.ts +36 -10
  23. package/src/image/openai-compatible-image-model.ts +35 -13
  24. package/src/index.ts +3 -3
  25. package/src/openai-compatible-error.ts +1 -2
  26. package/src/openai-compatible-provider.ts +18 -5
  27. package/src/utils/to-camel-case.ts +43 -0
  28. package/dist/index.d.mts +0 -290
  29. package/dist/index.mjs +0 -1742
  30. package/dist/index.mjs.map +0 -1
  31. package/dist/internal/index.d.mts +0 -193
  32. package/dist/internal/index.mjs +0 -340
  33. package/dist/internal/index.mjs.map +0 -1
  34. /package/src/chat/{openai-compatible-chat-options.ts → openai-compatible-chat-language-model-options.ts} +0 -0
  35. /package/src/completion/{openai-compatible-completion-options.ts → openai-compatible-completion-language-model-options.ts} +0 -0
  36. /package/src/embedding/{openai-compatible-embedding-options.ts → openai-compatible-embedding-model-options.ts} +0 -0
@@ -1,12 +1,14 @@
1
1
  import {
2
- LanguageModelV4Prompt,
3
- SharedV4ProviderMetadata,
4
2
  UnsupportedFunctionalityError,
3
+ type LanguageModelV4Prompt,
4
+ type SharedV4ProviderMetadata,
5
5
  } from '@ai-sdk/provider';
6
- import { OpenAICompatibleChatPrompt } from './openai-compatible-api-types';
6
+ import type { OpenAICompatibleChatPrompt } from './openai-compatible-api-types';
7
7
  import {
8
8
  convertBase64ToUint8Array,
9
9
  convertToBase64,
10
+ getTopLevelMediaType,
11
+ resolveFullMediaType,
10
12
  } from '@ai-sdk/provider-utils';
11
13
 
12
14
  function getOpenAIMetadata(message: {
@@ -58,86 +60,105 @@ export function convertToOpenAICompatibleChatMessages(
58
60
  return { type: 'text', text: part.text, ...partMetadata };
59
61
  }
60
62
  case 'file': {
61
- if (part.mediaType.startsWith('image/')) {
62
- const mediaType =
63
- part.mediaType === 'image/*'
64
- ? 'image/jpeg'
65
- : part.mediaType;
66
-
67
- return {
68
- type: 'image_url',
69
- image_url: {
70
- url:
71
- part.data instanceof URL
72
- ? part.data.toString()
73
- : `data:${mediaType};base64,${convertToBase64(part.data)}`,
74
- },
75
- ...partMetadata,
76
- };
77
- }
78
-
79
- if (part.mediaType.startsWith('audio/')) {
80
- if (part.data instanceof URL) {
63
+ switch (part.data.type) {
64
+ case 'reference': {
81
65
  throw new UnsupportedFunctionalityError({
82
- functionality: 'audio file parts with URLs',
66
+ functionality: 'file parts with provider references',
83
67
  });
84
68
  }
85
-
86
- const format = getAudioFormat(part.mediaType);
87
- if (format === null) {
69
+ case 'text': {
88
70
  throw new UnsupportedFunctionalityError({
89
- functionality: `audio media type ${part.mediaType}`,
71
+ functionality: 'text file parts',
90
72
  });
91
73
  }
74
+ case 'url':
75
+ case 'data': {
76
+ const topLevel = getTopLevelMediaType(part.mediaType);
92
77
 
93
- return {
94
- type: 'input_audio',
95
- input_audio: {
96
- data: convertToBase64(part.data),
97
- format,
98
- },
99
- ...partMetadata,
100
- };
101
- }
78
+ if (topLevel === 'image') {
79
+ return {
80
+ type: 'image_url',
81
+ image_url: {
82
+ url:
83
+ part.data.type === 'url'
84
+ ? part.data.url.toString()
85
+ : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
86
+ },
87
+ ...partMetadata,
88
+ };
89
+ }
102
90
 
103
- if (part.mediaType === 'application/pdf') {
104
- if (part.data instanceof URL) {
105
- throw new UnsupportedFunctionalityError({
106
- functionality: 'PDF file parts with URLs',
107
- });
108
- }
91
+ if (topLevel === 'audio') {
92
+ if (part.data.type === 'url') {
93
+ throw new UnsupportedFunctionalityError({
94
+ functionality: 'audio file parts with URLs',
95
+ });
96
+ }
109
97
 
110
- return {
111
- type: 'file',
112
- file: {
113
- filename: part.filename ?? 'document.pdf',
114
- file_data: `data:application/pdf;base64,${convertToBase64(part.data)}`,
115
- },
116
- ...partMetadata,
117
- };
118
- }
98
+ const fullMediaType = resolveFullMediaType({ part });
99
+ const format = getAudioFormat(fullMediaType);
100
+ if (format === null) {
101
+ throw new UnsupportedFunctionalityError({
102
+ functionality: `audio media type ${fullMediaType}`,
103
+ });
104
+ }
119
105
 
120
- if (part.mediaType.startsWith('text/')) {
121
- const textContent =
122
- part.data instanceof URL
123
- ? part.data.toString()
124
- : typeof part.data === 'string'
125
- ? new TextDecoder().decode(
126
- convertBase64ToUint8Array(part.data),
127
- )
128
- : new TextDecoder().decode(part.data);
106
+ return {
107
+ type: 'input_audio',
108
+ input_audio: {
109
+ data: convertToBase64(part.data.data),
110
+ format,
111
+ },
112
+ ...partMetadata,
113
+ };
114
+ }
129
115
 
130
- return {
131
- type: 'text',
132
- text: textContent,
133
- ...partMetadata,
134
- };
135
- }
116
+ if (topLevel === 'application') {
117
+ if (part.data.type === 'url') {
118
+ throw new UnsupportedFunctionalityError({
119
+ functionality: 'PDF file parts with URLs',
120
+ });
121
+ }
122
+
123
+ const fullMediaType = resolveFullMediaType({ part });
124
+ if (fullMediaType !== 'application/pdf') {
125
+ throw new UnsupportedFunctionalityError({
126
+ functionality: `file part media type ${fullMediaType}`,
127
+ });
128
+ }
129
+
130
+ return {
131
+ type: 'file',
132
+ file: {
133
+ filename: part.filename ?? 'document.pdf',
134
+ file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`,
135
+ },
136
+ ...partMetadata,
137
+ };
138
+ }
139
+
140
+ if (topLevel === 'text') {
141
+ const textContent =
142
+ part.data.type === 'url'
143
+ ? part.data.url.toString()
144
+ : typeof part.data.data === 'string'
145
+ ? new TextDecoder().decode(
146
+ convertBase64ToUint8Array(part.data.data),
147
+ )
148
+ : new TextDecoder().decode(part.data.data);
136
149
 
137
- // Unsupported type
138
- throw new UnsupportedFunctionalityError({
139
- functionality: `file part media type ${part.mediaType}`,
140
- });
150
+ return {
151
+ type: 'text',
152
+ text: textContent,
153
+ ...partMetadata,
154
+ };
155
+ }
156
+
157
+ throw new UnsupportedFunctionalityError({
158
+ functionality: `file part media type ${part.mediaType}`,
159
+ });
160
+ }
161
+ }
141
162
  }
142
163
  }
143
164
  }),
@@ -202,7 +223,7 @@ export function convertToOpenAICompatibleChatMessages(
202
223
 
203
224
  messages.push({
204
225
  role: 'assistant',
205
- content: text,
226
+ content: toolCalls.length > 0 ? text || null : text,
206
227
  ...(reasoning.length > 0 ? { reasoning_content: reasoning } : {}),
207
228
  tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
208
229
  ...metadata,
@@ -226,7 +247,7 @@ export function convertToOpenAICompatibleChatMessages(
226
247
  contentValue = output.value;
227
248
  break;
228
249
  case 'execution-denied':
229
- contentValue = output.reason ?? 'Tool execution denied.';
250
+ contentValue = output.reason ?? 'Tool call execution denied.';
230
251
  break;
231
252
  case 'content':
232
253
  case 'json':
@@ -1,4 +1,4 @@
1
- import { LanguageModelV4FinishReason } from '@ai-sdk/provider';
1
+ import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';
2
2
 
3
3
  export function mapOpenAICompatibleFinishReason(
4
4
  finishReason: string | null | undefined,
@@ -1,4 +1,4 @@
1
- import { JSONValue } from '@ai-sdk/provider';
1
+ import type { JSONValue } from '@ai-sdk/provider';
2
2
 
3
3
  export type OpenAICompatibleChatPrompt = Array<OpenAICompatibleMessage>;
4
4
 
@@ -20,8 +20,7 @@ export interface OpenAICompatibleSystemMessage extends JsonRecord {
20
20
  content: string;
21
21
  }
22
22
 
23
- export interface OpenAICompatibleUserMessage
24
- extends JsonRecord<OpenAICompatibleContentPart> {
23
+ export interface OpenAICompatibleUserMessage extends JsonRecord<OpenAICompatibleContentPart> {
25
24
  role: 'user';
26
25
  content: string | Array<OpenAICompatibleContentPart>;
27
26
  }
@@ -54,8 +53,7 @@ export interface OpenAICompatibleContentPartFile extends JsonRecord {
54
53
  file: { filename: string; file_data: string };
55
54
  }
56
55
 
57
- export interface OpenAICompatibleAssistantMessage
58
- extends JsonRecord<OpenAICompatibleMessageToolCall> {
56
+ export interface OpenAICompatibleAssistantMessage extends JsonRecord<OpenAICompatibleMessageToolCall> {
59
57
  role: 'assistant';
60
58
  content?: string | null;
61
59
  reasoning_content?: string;