@ai-sdk/openai 4.0.0-beta.6 → 4.0.0-beta.74
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 +644 -24
- package/README.md +2 -0
- package/dist/index.d.ts +240 -44
- package/dist/index.js +3345 -1683
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +390 -36
- package/dist/internal/index.js +2707 -1706
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +413 -39
- package/package.json +17 -18
- package/src/chat/convert-openai-chat-usage.ts +1 -1
- package/src/chat/convert-to-openai-chat-messages.ts +96 -68
- package/src/chat/map-openai-finish-reason.ts +1 -1
- package/src/chat/openai-chat-api.ts +6 -2
- package/src/chat/{openai-chat-options.ts → openai-chat-language-model-options.ts} +11 -1
- package/src/chat/openai-chat-language-model.ts +82 -148
- package/src/chat/openai-chat-prepare-tools.ts +3 -3
- package/src/completion/convert-openai-completion-usage.ts +1 -1
- package/src/completion/convert-to-openai-completion-prompt.ts +1 -2
- package/src/completion/map-openai-finish-reason.ts +1 -1
- package/src/completion/openai-completion-api.ts +5 -2
- package/src/completion/{openai-completion-options.ts → openai-completion-language-model-options.ts} +5 -1
- package/src/completion/openai-completion-language-model.ts +53 -17
- package/src/embedding/{openai-embedding-options.ts → openai-embedding-model-options.ts} +5 -1
- package/src/embedding/openai-embedding-model.ts +22 -5
- package/src/files/openai-files-api.ts +17 -0
- package/src/files/openai-files-options.ts +22 -0
- package/src/files/openai-files.ts +100 -0
- package/src/image/openai-image-model-options.ts +123 -0
- package/src/image/openai-image-model.ts +62 -83
- package/src/index.ts +15 -6
- package/src/internal/index.ts +7 -6
- package/src/openai-config.ts +7 -7
- package/src/openai-language-model-capabilities.ts +5 -4
- package/src/openai-provider.ts +80 -9
- package/src/openai-stream-error.ts +181 -0
- package/src/openai-tools.ts +12 -1
- package/src/realtime/index.ts +2 -0
- package/src/realtime/openai-realtime-event-mapper.ts +436 -0
- package/src/realtime/openai-realtime-model-options.ts +3 -0
- package/src/realtime/openai-realtime-model.ts +111 -0
- package/src/responses/convert-openai-responses-usage.ts +1 -1
- package/src/responses/convert-to-openai-responses-input.ts +345 -90
- package/src/responses/map-openai-responses-finish-reason.ts +1 -1
- package/src/responses/openai-responses-api.ts +186 -17
- package/src/responses/{openai-responses-options.ts → openai-responses-language-model-options.ts} +55 -1
- package/src/responses/openai-responses-language-model.ts +330 -52
- package/src/responses/openai-responses-prepare-tools.ts +129 -18
- package/src/responses/openai-responses-provider-metadata.ts +12 -2
- package/src/skills/openai-skills-api.ts +31 -0
- package/src/skills/openai-skills.ts +83 -0
- package/src/speech/{openai-speech-options.ts → openai-speech-model-options.ts} +5 -1
- package/src/speech/openai-speech-model.ts +23 -7
- package/src/tool/apply-patch.ts +33 -32
- package/src/tool/code-interpreter.ts +40 -41
- package/src/tool/custom.ts +2 -8
- package/src/tool/file-search.ts +3 -3
- package/src/tool/image-generation.ts +2 -2
- package/src/tool/local-shell.ts +2 -2
- package/src/tool/mcp.ts +3 -3
- package/src/tool/shell.ts +9 -4
- package/src/tool/tool-search.ts +98 -0
- package/src/tool/web-search-preview.ts +2 -2
- package/src/tool/web-search.ts +10 -2
- package/src/transcription/{openai-transcription-options.ts → openai-transcription-model-options.ts} +5 -1
- package/src/transcription/openai-transcription-model.ts +35 -13
- package/dist/index.d.mts +0 -1107
- package/dist/index.mjs +0 -6509
- package/dist/index.mjs.map +0 -1
- package/dist/internal/index.d.mts +0 -1137
- package/dist/internal/index.mjs +0 -6322
- package/dist/internal/index.mjs.map +0 -1
- package/src/image/openai-image-options.ts +0 -31
package/src/openai-provider.ts
CHANGED
|
@@ -1,33 +1,40 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
2
|
EmbeddingModelV4,
|
|
3
|
+
FilesV4,
|
|
3
4
|
ImageModelV4,
|
|
4
5
|
LanguageModelV4,
|
|
5
6
|
ProviderV4,
|
|
7
|
+
Experimental_RealtimeFactoryV4 as RealtimeFactoryV4,
|
|
8
|
+
Experimental_RealtimeFactoryV4GetTokenOptions as RealtimeFactoryV4GetTokenOptions,
|
|
6
9
|
SpeechModelV4,
|
|
10
|
+
SkillsV4,
|
|
7
11
|
TranscriptionModelV4,
|
|
8
12
|
} from '@ai-sdk/provider';
|
|
9
13
|
import {
|
|
10
|
-
FetchFunction,
|
|
11
14
|
loadApiKey,
|
|
12
15
|
loadOptionalSetting,
|
|
13
16
|
withoutTrailingSlash,
|
|
14
17
|
withUserAgentSuffix,
|
|
18
|
+
type FetchFunction,
|
|
15
19
|
} from '@ai-sdk/provider-utils';
|
|
16
20
|
import { OpenAIChatLanguageModel } from './chat/openai-chat-language-model';
|
|
17
|
-
import { OpenAIChatModelId } from './chat/openai-chat-options';
|
|
21
|
+
import type { OpenAIChatModelId } from './chat/openai-chat-language-model-options';
|
|
18
22
|
import { OpenAICompletionLanguageModel } from './completion/openai-completion-language-model';
|
|
19
|
-
import { OpenAICompletionModelId } from './completion/openai-completion-options';
|
|
23
|
+
import type { OpenAICompletionModelId } from './completion/openai-completion-language-model-options';
|
|
20
24
|
import { OpenAIEmbeddingModel } from './embedding/openai-embedding-model';
|
|
21
|
-
import {
|
|
25
|
+
import { OpenAIFiles } from './files/openai-files';
|
|
26
|
+
import type { OpenAIEmbeddingModelId } from './embedding/openai-embedding-model-options';
|
|
22
27
|
import { OpenAIImageModel } from './image/openai-image-model';
|
|
23
|
-
import { OpenAIImageModelId } from './image/openai-image-options';
|
|
28
|
+
import type { OpenAIImageModelId } from './image/openai-image-model-options';
|
|
24
29
|
import { openaiTools } from './openai-tools';
|
|
30
|
+
import { OpenAIRealtimeModel } from './realtime/openai-realtime-model';
|
|
25
31
|
import { OpenAIResponsesLanguageModel } from './responses/openai-responses-language-model';
|
|
26
|
-
import { OpenAIResponsesModelId } from './responses/openai-responses-options';
|
|
32
|
+
import type { OpenAIResponsesModelId } from './responses/openai-responses-language-model-options';
|
|
27
33
|
import { OpenAISpeechModel } from './speech/openai-speech-model';
|
|
28
|
-
import { OpenAISpeechModelId } from './speech/openai-speech-options';
|
|
34
|
+
import type { OpenAISpeechModelId } from './speech/openai-speech-model-options';
|
|
29
35
|
import { OpenAITranscriptionModel } from './transcription/openai-transcription-model';
|
|
30
|
-
import { OpenAITranscriptionModelId } from './transcription/openai-transcription-options';
|
|
36
|
+
import type { OpenAITranscriptionModelId } from './transcription/openai-transcription-model-options';
|
|
37
|
+
import { OpenAISkills } from './skills/openai-skills';
|
|
31
38
|
import { VERSION } from './version';
|
|
32
39
|
|
|
33
40
|
export interface OpenAIProvider extends ProviderV4 {
|
|
@@ -93,6 +100,22 @@ export interface OpenAIProvider extends ProviderV4 {
|
|
|
93
100
|
*/
|
|
94
101
|
speech(modelId: OpenAISpeechModelId): SpeechModelV4;
|
|
95
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Creates an experimental realtime model for bidirectional audio/text
|
|
105
|
+
* communication over WebSocket.
|
|
106
|
+
*/
|
|
107
|
+
experimental_realtime: RealtimeFactoryV4;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns a FilesV4 interface for uploading files to OpenAI.
|
|
111
|
+
*/
|
|
112
|
+
files(): FilesV4;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Returns a SkillsV4 interface for uploading skills to OpenAI.
|
|
116
|
+
*/
|
|
117
|
+
skills(): SkillsV4;
|
|
118
|
+
|
|
96
119
|
/**
|
|
97
120
|
* OpenAI-specific tools.
|
|
98
121
|
*/
|
|
@@ -216,6 +239,22 @@ export function createOpenAI(
|
|
|
216
239
|
fetch: options.fetch,
|
|
217
240
|
});
|
|
218
241
|
|
|
242
|
+
const createFiles = () =>
|
|
243
|
+
new OpenAIFiles({
|
|
244
|
+
provider: `${providerName}.files`,
|
|
245
|
+
baseURL,
|
|
246
|
+
headers: getHeaders,
|
|
247
|
+
fetch: options.fetch,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const createSkills = () =>
|
|
251
|
+
new OpenAISkills({
|
|
252
|
+
provider: `${providerName}.skills`,
|
|
253
|
+
url: ({ path }) => `${baseURL}${path}`,
|
|
254
|
+
headers: getHeaders,
|
|
255
|
+
fetch: options.fetch,
|
|
256
|
+
});
|
|
257
|
+
|
|
219
258
|
const createLanguageModel = (modelId: OpenAIResponsesModelId) => {
|
|
220
259
|
if (new.target) {
|
|
221
260
|
throw new Error(
|
|
@@ -232,10 +271,38 @@ export function createOpenAI(
|
|
|
232
271
|
url: ({ path }) => `${baseURL}${path}`,
|
|
233
272
|
headers: getHeaders,
|
|
234
273
|
fetch: options.fetch,
|
|
274
|
+
// Soft-deprecated. TODO: remove in v8
|
|
235
275
|
fileIdPrefixes: ['file-'],
|
|
236
276
|
});
|
|
237
277
|
};
|
|
238
278
|
|
|
279
|
+
const createRealtimeModel = (modelId: string) =>
|
|
280
|
+
new OpenAIRealtimeModel(modelId, {
|
|
281
|
+
provider: `${providerName}.realtime`,
|
|
282
|
+
baseURL,
|
|
283
|
+
headers: getHeaders,
|
|
284
|
+
fetch: options.fetch,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const experimentalRealtimeFactory = Object.assign(
|
|
288
|
+
(modelId: string) => createRealtimeModel(modelId),
|
|
289
|
+
{
|
|
290
|
+
getToken: async (tokenOptions: RealtimeFactoryV4GetTokenOptions) => {
|
|
291
|
+
const model = createRealtimeModel(tokenOptions.model);
|
|
292
|
+
const secret = await model.doCreateClientSecret({
|
|
293
|
+
sessionConfig: tokenOptions.sessionConfig,
|
|
294
|
+
expiresAfterSeconds: tokenOptions.expiresAfterSeconds,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
token: secret.token,
|
|
299
|
+
url: secret.url,
|
|
300
|
+
expiresAt: secret.expiresAt,
|
|
301
|
+
};
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
) as RealtimeFactoryV4;
|
|
305
|
+
|
|
239
306
|
const provider = function (modelId: OpenAIResponsesModelId) {
|
|
240
307
|
return createLanguageModel(modelId);
|
|
241
308
|
};
|
|
@@ -258,6 +325,10 @@ export function createOpenAI(
|
|
|
258
325
|
|
|
259
326
|
provider.speech = createSpeechModel;
|
|
260
327
|
provider.speechModel = createSpeechModel;
|
|
328
|
+
provider.files = createFiles;
|
|
329
|
+
provider.skills = createSkills;
|
|
330
|
+
|
|
331
|
+
provider.experimental_realtime = experimentalRealtimeFactory;
|
|
261
332
|
|
|
262
333
|
provider.tools = openaiTools;
|
|
263
334
|
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { APICallError } from '@ai-sdk/provider';
|
|
2
|
+
import type { ParseResult } from '@ai-sdk/provider-utils';
|
|
3
|
+
|
|
4
|
+
type StreamError = {
|
|
5
|
+
message: string;
|
|
6
|
+
code?: string | number | null;
|
|
7
|
+
type?: string | null;
|
|
8
|
+
frame: unknown;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export async function throwIfOpenAIStreamErrorBeforeOutput<T>({
|
|
12
|
+
stream,
|
|
13
|
+
getError,
|
|
14
|
+
isOutputChunk,
|
|
15
|
+
url,
|
|
16
|
+
requestBodyValues,
|
|
17
|
+
responseHeaders,
|
|
18
|
+
}: {
|
|
19
|
+
stream: ReadableStream<ParseResult<T>>;
|
|
20
|
+
getError: (chunk: T) => unknown | undefined;
|
|
21
|
+
isOutputChunk: (chunk: T) => boolean;
|
|
22
|
+
url: string;
|
|
23
|
+
requestBodyValues: unknown;
|
|
24
|
+
responseHeaders?: Record<string, string>;
|
|
25
|
+
}): Promise<ReadableStream<ParseResult<T>>> {
|
|
26
|
+
const [streamForEarlyError, streamForConsumer] = stream.tee();
|
|
27
|
+
const reader = streamForEarlyError.getReader();
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
while (true) {
|
|
31
|
+
const result = await reader.read();
|
|
32
|
+
|
|
33
|
+
if (result.done) {
|
|
34
|
+
return streamForConsumer;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const chunk = result.value;
|
|
38
|
+
|
|
39
|
+
if (!chunk.success) {
|
|
40
|
+
return streamForConsumer;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const errorFrame = getError(chunk.value);
|
|
44
|
+
|
|
45
|
+
if (errorFrame != null) {
|
|
46
|
+
streamForConsumer.cancel().catch(() => {});
|
|
47
|
+
throw createOpenAIStreamError({
|
|
48
|
+
frame: errorFrame,
|
|
49
|
+
url,
|
|
50
|
+
requestBodyValues,
|
|
51
|
+
responseHeaders,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (isOutputChunk(chunk.value)) {
|
|
56
|
+
return streamForConsumer;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
reader.cancel().catch(() => {});
|
|
61
|
+
reader.releaseLock();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createOpenAIStreamError({
|
|
66
|
+
frame,
|
|
67
|
+
url,
|
|
68
|
+
requestBodyValues,
|
|
69
|
+
responseHeaders,
|
|
70
|
+
}: {
|
|
71
|
+
frame: unknown;
|
|
72
|
+
url: string;
|
|
73
|
+
requestBodyValues: unknown;
|
|
74
|
+
responseHeaders?: Record<string, string>;
|
|
75
|
+
}): APICallError {
|
|
76
|
+
const streamError = parseStreamError(frame);
|
|
77
|
+
return new APICallError({
|
|
78
|
+
message:
|
|
79
|
+
streamError?.message ??
|
|
80
|
+
'OpenAI stream failed before any output was generated',
|
|
81
|
+
url,
|
|
82
|
+
requestBodyValues,
|
|
83
|
+
statusCode: streamError == null ? 500 : getStatusCode(streamError),
|
|
84
|
+
responseHeaders,
|
|
85
|
+
responseBody: JSON.stringify(frame),
|
|
86
|
+
data: frame,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function parseStreamError(frame: unknown): StreamError | undefined {
|
|
91
|
+
const value = asRecord(frame);
|
|
92
|
+
|
|
93
|
+
if (value == null) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (value.type === 'response.failed') {
|
|
98
|
+
const response = asRecord(value.response);
|
|
99
|
+
const responseError = asRecord(response?.error);
|
|
100
|
+
|
|
101
|
+
return typeof responseError?.message === 'string'
|
|
102
|
+
? {
|
|
103
|
+
message: responseError.message,
|
|
104
|
+
code: getStringOrNumber(responseError.code),
|
|
105
|
+
type: 'response.failed',
|
|
106
|
+
frame,
|
|
107
|
+
}
|
|
108
|
+
: undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const error = asRecord(value.error) ?? value;
|
|
112
|
+
|
|
113
|
+
return typeof error.message === 'string' &&
|
|
114
|
+
(asRecord(value.error) != null ||
|
|
115
|
+
typeof error.type === 'string' ||
|
|
116
|
+
'code' in error ||
|
|
117
|
+
'param' in error)
|
|
118
|
+
? {
|
|
119
|
+
message: error.message,
|
|
120
|
+
code: getStringOrNumber(error.code),
|
|
121
|
+
type: typeof error.type === 'string' ? error.type : undefined,
|
|
122
|
+
frame,
|
|
123
|
+
}
|
|
124
|
+
: undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function getStatusCode(error: StreamError): number {
|
|
128
|
+
if (typeof error.code === 'number' && isHttpErrorStatusCode(error.code)) {
|
|
129
|
+
return error.code;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (typeof error.code === 'string' && /^\d{3}$/.test(error.code)) {
|
|
133
|
+
const numericCode = Number(error.code);
|
|
134
|
+
if (isHttpErrorStatusCode(numericCode)) {
|
|
135
|
+
return numericCode;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const discriminator = [error.code, error.type]
|
|
140
|
+
.filter(value => typeof value === 'string' || typeof value === 'number')
|
|
141
|
+
.join(' ')
|
|
142
|
+
.toLowerCase();
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
['insufficient_quota', 'rate_limit'].some(term =>
|
|
146
|
+
discriminator.includes(term),
|
|
147
|
+
)
|
|
148
|
+
) {
|
|
149
|
+
return 429;
|
|
150
|
+
}
|
|
151
|
+
if (discriminator.includes('authentication')) return 401;
|
|
152
|
+
if (discriminator.includes('permission')) return 403;
|
|
153
|
+
if (discriminator.includes('not_found')) return 404;
|
|
154
|
+
if (
|
|
155
|
+
['invalid', 'bad_request', 'context_length'].some(term =>
|
|
156
|
+
discriminator.includes(term),
|
|
157
|
+
)
|
|
158
|
+
) {
|
|
159
|
+
return 400;
|
|
160
|
+
}
|
|
161
|
+
if (discriminator.includes('overload')) return 503;
|
|
162
|
+
if (discriminator.includes('timeout')) return 504;
|
|
163
|
+
|
|
164
|
+
return 500;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
168
|
+
return typeof value === 'object' && value != null
|
|
169
|
+
? (value as Record<string, unknown>)
|
|
170
|
+
: undefined;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getStringOrNumber(value: unknown): string | number | undefined {
|
|
174
|
+
return typeof value === 'string' || typeof value === 'number'
|
|
175
|
+
? value
|
|
176
|
+
: undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isHttpErrorStatusCode(value: number): boolean {
|
|
180
|
+
return Number.isInteger(value) && value >= 400 && value <= 599;
|
|
181
|
+
}
|
package/src/openai-tools.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { fileSearch } from './tool/file-search';
|
|
|
5
5
|
import { imageGeneration } from './tool/image-generation';
|
|
6
6
|
import { localShell } from './tool/local-shell';
|
|
7
7
|
import { shell } from './tool/shell';
|
|
8
|
+
import { toolSearch } from './tool/tool-search';
|
|
8
9
|
import { webSearch } from './tool/web-search';
|
|
9
10
|
import { webSearchPreview } from './tool/web-search-preview';
|
|
10
11
|
import { mcp } from './tool/mcp';
|
|
@@ -24,7 +25,6 @@ export const openaiTools = {
|
|
|
24
25
|
* Lark syntax). The model returns a `custom_tool_call` output item whose
|
|
25
26
|
* `input` field is a string matching the specified grammar.
|
|
26
27
|
*
|
|
27
|
-
* @param name - The name of the custom tool.
|
|
28
28
|
* @param description - An optional description of the tool.
|
|
29
29
|
* @param format - The output format constraint (grammar type, syntax, and definition).
|
|
30
30
|
*/
|
|
@@ -123,4 +123,15 @@ export const openaiTools = {
|
|
|
123
123
|
* @param serverUrl - URL for the MCP server.
|
|
124
124
|
*/
|
|
125
125
|
mcp,
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Tool search allows the model to dynamically search for and load deferred
|
|
129
|
+
* tools into the model's context as needed. This helps reduce overall token
|
|
130
|
+
* usage, cost, and latency by only loading tools when the model needs them.
|
|
131
|
+
*
|
|
132
|
+
* To use tool search, mark functions or namespaces with `defer_loading: true`
|
|
133
|
+
* in the tools array. The model will use tool search to load these tools
|
|
134
|
+
* when it determines they are needed.
|
|
135
|
+
*/
|
|
136
|
+
toolSearch,
|
|
126
137
|
};
|