@ai-sdk/openai 4.0.0-beta.4 → 4.0.0-beta.41
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 +399 -22
- package/README.md +2 -0
- package/dist/index.d.ts +166 -49
- package/dist/index.js +2454 -1627
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +176 -53
- package/dist/internal/index.js +2220 -1648
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +292 -22
- package/package.json +13 -14
- package/src/chat/convert-openai-chat-usage.ts +2 -2
- package/src/chat/convert-to-openai-chat-messages.ts +99 -71
- package/src/chat/map-openai-finish-reason.ts +2 -2
- package/src/chat/openai-chat-api.ts +6 -2
- package/src/chat/openai-chat-language-model.ts +68 -164
- package/src/chat/openai-chat-options.ts +10 -1
- package/src/chat/openai-chat-prepare-tools.ts +7 -7
- package/src/completion/convert-openai-completion-usage.ts +2 -2
- package/src/completion/convert-to-openai-completion-prompt.ts +2 -3
- package/src/completion/map-openai-finish-reason.ts +2 -2
- package/src/completion/openai-completion-api.ts +5 -2
- package/src/completion/openai-completion-language-model.ts +46 -30
- package/src/completion/openai-completion-options.ts +5 -1
- package/src/embedding/openai-embedding-model.ts +25 -8
- package/src/embedding/openai-embedding-options.ts +5 -1
- 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.ts +31 -15
- package/src/image/openai-image-options.ts +3 -0
- package/src/index.ts +2 -0
- package/src/openai-config.ts +7 -7
- package/src/openai-language-model-capabilities.ts +3 -2
- package/src/openai-provider.ts +63 -30
- package/src/openai-tools.ts +12 -1
- package/src/responses/convert-openai-responses-usage.ts +2 -2
- package/src/responses/convert-to-openai-responses-input.ts +244 -77
- package/src/responses/map-openai-responses-finish-reason.ts +2 -2
- package/src/responses/openai-responses-api.ts +141 -3
- package/src/responses/openai-responses-language-model.ts +274 -61
- package/src/responses/openai-responses-options.ts +29 -3
- package/src/responses/openai-responses-prepare-tools.ts +48 -15
- 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-model.ts +28 -12
- package/src/speech/openai-speech-options.ts +5 -1
- 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 +2 -2
- package/src/transcription/openai-transcription-model.ts +30 -14
- package/src/transcription/openai-transcription-options.ts +5 -1
- package/dist/index.d.mts +0 -1107
- package/dist/index.mjs +0 -6508
- package/dist/index.mjs.map +0 -1
- package/dist/internal/index.d.mts +0 -1137
- package/dist/internal/index.mjs +0 -6321
- package/dist/internal/index.mjs.map +0 -1
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
|
-
SharedV3Warning,
|
|
3
|
-
LanguageModelV3Prompt,
|
|
4
2
|
UnsupportedFunctionalityError,
|
|
3
|
+
type SharedV4Warning,
|
|
4
|
+
type LanguageModelV4Prompt,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
|
-
import { OpenAIChatPrompt } from './openai-chat-prompt';
|
|
7
|
-
import {
|
|
6
|
+
import type { OpenAIChatPrompt } from './openai-chat-prompt';
|
|
7
|
+
import {
|
|
8
|
+
convertToBase64,
|
|
9
|
+
getTopLevelMediaType,
|
|
10
|
+
resolveFullMediaType,
|
|
11
|
+
resolveProviderReference,
|
|
12
|
+
} from '@ai-sdk/provider-utils';
|
|
13
|
+
|
|
14
|
+
function serializeToolCallArguments(input: unknown): string {
|
|
15
|
+
return JSON.stringify(input === undefined ? {} : input);
|
|
16
|
+
}
|
|
8
17
|
|
|
9
18
|
export function convertToOpenAIChatMessages({
|
|
10
19
|
prompt,
|
|
11
20
|
systemMessageMode = 'system',
|
|
12
21
|
}: {
|
|
13
|
-
prompt:
|
|
22
|
+
prompt: LanguageModelV4Prompt;
|
|
14
23
|
systemMessageMode?: 'system' | 'developer' | 'remove';
|
|
15
24
|
}): {
|
|
16
25
|
messages: OpenAIChatPrompt;
|
|
17
|
-
warnings: Array<
|
|
26
|
+
warnings: Array<SharedV4Warning>;
|
|
18
27
|
} {
|
|
19
28
|
const messages: OpenAIChatPrompt = [];
|
|
20
|
-
const warnings: Array<
|
|
29
|
+
const warnings: Array<SharedV4Warning> = [];
|
|
21
30
|
|
|
22
31
|
for (const { role, content } of prompt) {
|
|
23
32
|
switch (role) {
|
|
@@ -62,80 +71,99 @@ export function convertToOpenAIChatMessages({
|
|
|
62
71
|
return { type: 'text', text: part.text };
|
|
63
72
|
}
|
|
64
73
|
case 'file': {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// OpenAI specific extension: image detail
|
|
80
|
-
detail: part.providerOptions?.openai?.imageDetail,
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
} else if (part.mediaType.startsWith('audio/')) {
|
|
84
|
-
if (part.data instanceof URL) {
|
|
74
|
+
switch (part.data.type) {
|
|
75
|
+
case 'reference': {
|
|
76
|
+
return {
|
|
77
|
+
type: 'file',
|
|
78
|
+
file: {
|
|
79
|
+
file_id: resolveProviderReference({
|
|
80
|
+
reference: part.data.reference,
|
|
81
|
+
provider: 'openai',
|
|
82
|
+
}),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
case 'text': {
|
|
85
87
|
throw new UnsupportedFunctionalityError({
|
|
86
|
-
functionality: '
|
|
88
|
+
functionality: 'text file parts',
|
|
87
89
|
});
|
|
88
90
|
}
|
|
91
|
+
case 'url':
|
|
92
|
+
case 'data': {
|
|
93
|
+
const topLevel = getTopLevelMediaType(part.mediaType);
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
case 'audio/wav': {
|
|
95
|
+
if (topLevel === 'image') {
|
|
92
96
|
return {
|
|
93
|
-
type: '
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
type: 'image_url',
|
|
98
|
+
image_url: {
|
|
99
|
+
url:
|
|
100
|
+
part.data.type === 'url'
|
|
101
|
+
? part.data.url.toString()
|
|
102
|
+
: convertToBase64(part.data.data),
|
|
103
|
+
|
|
104
|
+
detail: part.providerOptions?.openai?.imageDetail,
|
|
97
105
|
},
|
|
98
106
|
};
|
|
107
|
+
} else if (topLevel === 'audio') {
|
|
108
|
+
if (part.data.type === 'url') {
|
|
109
|
+
throw new UnsupportedFunctionalityError({
|
|
110
|
+
functionality: 'audio file parts with URLs',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const fullMediaType = resolveFullMediaType({ part });
|
|
115
|
+
|
|
116
|
+
switch (fullMediaType) {
|
|
117
|
+
case 'audio/wav': {
|
|
118
|
+
return {
|
|
119
|
+
type: 'input_audio',
|
|
120
|
+
input_audio: {
|
|
121
|
+
data: convertToBase64(part.data.data),
|
|
122
|
+
format: 'wav',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
case 'audio/mp3':
|
|
127
|
+
case 'audio/mpeg': {
|
|
128
|
+
return {
|
|
129
|
+
type: 'input_audio',
|
|
130
|
+
input_audio: {
|
|
131
|
+
data: convertToBase64(part.data.data),
|
|
132
|
+
format: 'mp3',
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
default: {
|
|
138
|
+
throw new UnsupportedFunctionalityError({
|
|
139
|
+
functionality: `audio content parts with media type ${fullMediaType}`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
99
143
|
}
|
|
100
|
-
|
|
101
|
-
|
|
144
|
+
{
|
|
145
|
+
const fullMediaType = resolveFullMediaType({ part });
|
|
146
|
+
if (fullMediaType !== 'application/pdf') {
|
|
147
|
+
throw new UnsupportedFunctionalityError({
|
|
148
|
+
functionality: `file part media type ${fullMediaType}`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (part.data.type === 'url') {
|
|
153
|
+
throw new UnsupportedFunctionalityError({
|
|
154
|
+
functionality: 'PDF file parts with URLs',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
102
158
|
return {
|
|
103
|
-
type: '
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
159
|
+
type: 'file',
|
|
160
|
+
file: {
|
|
161
|
+
filename: part.filename ?? `part-${index}.pdf`,
|
|
162
|
+
file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`,
|
|
107
163
|
},
|
|
108
164
|
};
|
|
109
165
|
}
|
|
110
|
-
|
|
111
|
-
default: {
|
|
112
|
-
throw new UnsupportedFunctionalityError({
|
|
113
|
-
functionality: `audio content parts with media type ${part.mediaType}`,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
166
|
}
|
|
117
|
-
} else if (part.mediaType === 'application/pdf') {
|
|
118
|
-
if (part.data instanceof URL) {
|
|
119
|
-
throw new UnsupportedFunctionalityError({
|
|
120
|
-
functionality: 'PDF file parts with URLs',
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
type: 'file',
|
|
126
|
-
file:
|
|
127
|
-
typeof part.data === 'string' &&
|
|
128
|
-
part.data.startsWith('file-')
|
|
129
|
-
? { file_id: part.data }
|
|
130
|
-
: {
|
|
131
|
-
filename: part.filename ?? `part-${index}.pdf`,
|
|
132
|
-
file_data: `data:application/pdf;base64,${convertToBase64(part.data)}`,
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
} else {
|
|
136
|
-
throw new UnsupportedFunctionalityError({
|
|
137
|
-
functionality: `file part media type ${part.mediaType}`,
|
|
138
|
-
});
|
|
139
167
|
}
|
|
140
168
|
}
|
|
141
169
|
}
|
|
@@ -165,7 +193,7 @@ export function convertToOpenAIChatMessages({
|
|
|
165
193
|
type: 'function',
|
|
166
194
|
function: {
|
|
167
195
|
name: part.toolName,
|
|
168
|
-
arguments:
|
|
196
|
+
arguments: serializeToolCallArguments(part.input),
|
|
169
197
|
},
|
|
170
198
|
});
|
|
171
199
|
break;
|
|
@@ -175,7 +203,7 @@ export function convertToOpenAIChatMessages({
|
|
|
175
203
|
|
|
176
204
|
messages.push({
|
|
177
205
|
role: 'assistant',
|
|
178
|
-
content: text,
|
|
206
|
+
content: text || null,
|
|
179
207
|
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
180
208
|
});
|
|
181
209
|
|
|
@@ -196,7 +224,7 @@ export function convertToOpenAIChatMessages({
|
|
|
196
224
|
contentValue = output.value;
|
|
197
225
|
break;
|
|
198
226
|
case 'execution-denied':
|
|
199
|
-
contentValue = output.reason ?? 'Tool execution denied.';
|
|
227
|
+
contentValue = output.reason ?? 'Tool call execution denied.';
|
|
200
228
|
break;
|
|
201
229
|
case 'content':
|
|
202
230
|
case 'json':
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';
|
|
2
2
|
|
|
3
3
|
export function mapOpenAIFinishReason(
|
|
4
4
|
finishReason: string | null | undefined,
|
|
5
|
-
):
|
|
5
|
+
): LanguageModelV4FinishReason['unified'] {
|
|
6
6
|
switch (finishReason) {
|
|
7
7
|
case 'stop':
|
|
8
8
|
return 'stop';
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { JSONSchema7 } from '@ai-sdk/provider';
|
|
2
|
-
import {
|
|
1
|
+
import type { JSONSchema7 } from '@ai-sdk/provider';
|
|
2
|
+
import {
|
|
3
|
+
lazySchema,
|
|
4
|
+
zodSchema,
|
|
5
|
+
type InferSchema,
|
|
6
|
+
} from '@ai-sdk/provider-utils';
|
|
3
7
|
import { z } from 'zod/v4';
|
|
4
8
|
import { openaiErrorDataSchema } from '../openai-error';
|
|
5
9
|
|
|
@@ -1,55 +1,58 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
SharedV3Warning,
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4,
|
|
3
|
+
LanguageModelV4CallOptions,
|
|
4
|
+
LanguageModelV4Content,
|
|
5
|
+
LanguageModelV4FinishReason,
|
|
6
|
+
LanguageModelV4GenerateResult,
|
|
7
|
+
LanguageModelV4StreamPart,
|
|
8
|
+
LanguageModelV4StreamResult,
|
|
9
|
+
SharedV4ProviderMetadata,
|
|
10
|
+
SharedV4Warning,
|
|
12
11
|
} from '@ai-sdk/provider';
|
|
13
12
|
import {
|
|
14
|
-
|
|
15
|
-
ParseResult,
|
|
13
|
+
StreamingToolCallTracker,
|
|
16
14
|
combineHeaders,
|
|
17
15
|
createEventSourceResponseHandler,
|
|
18
16
|
createJsonResponseHandler,
|
|
19
17
|
generateId,
|
|
20
|
-
|
|
18
|
+
isCustomReasoning,
|
|
21
19
|
parseProviderOptions,
|
|
22
20
|
postJsonToApi,
|
|
21
|
+
serializeModelOptions,
|
|
22
|
+
WORKFLOW_DESERIALIZE,
|
|
23
|
+
WORKFLOW_SERIALIZE,
|
|
24
|
+
type FetchFunction,
|
|
25
|
+
type ParseResult,
|
|
23
26
|
} from '@ai-sdk/provider-utils';
|
|
24
27
|
import { openaiFailedResponseHandler } from '../openai-error';
|
|
25
28
|
import { getOpenAILanguageModelCapabilities } from '../openai-language-model-capabilities';
|
|
26
29
|
import {
|
|
27
|
-
OpenAIChatUsage,
|
|
28
30
|
convertOpenAIChatUsage,
|
|
31
|
+
type OpenAIChatUsage,
|
|
29
32
|
} from './convert-openai-chat-usage';
|
|
30
33
|
import { convertToOpenAIChatMessages } from './convert-to-openai-chat-messages';
|
|
31
34
|
import { getResponseMetadata } from './get-response-metadata';
|
|
32
35
|
import { mapOpenAIFinishReason } from './map-openai-finish-reason';
|
|
33
36
|
import {
|
|
34
|
-
OpenAIChatChunk,
|
|
35
37
|
openaiChatChunkSchema,
|
|
36
38
|
openaiChatResponseSchema,
|
|
39
|
+
type OpenAIChatChunk,
|
|
37
40
|
} from './openai-chat-api';
|
|
38
41
|
import {
|
|
39
|
-
OpenAIChatModelId,
|
|
40
42
|
openaiLanguageModelChatOptions,
|
|
43
|
+
type OpenAIChatModelId,
|
|
41
44
|
} from './openai-chat-options';
|
|
42
45
|
import { prepareChatTools } from './openai-chat-prepare-tools';
|
|
43
46
|
|
|
44
47
|
type OpenAIChatConfig = {
|
|
45
48
|
provider: string;
|
|
46
|
-
headers
|
|
49
|
+
headers?: () => Record<string, string | undefined>;
|
|
47
50
|
url: (options: { modelId: string; path: string }) => string;
|
|
48
51
|
fetch?: FetchFunction;
|
|
49
52
|
};
|
|
50
53
|
|
|
51
|
-
export class OpenAIChatLanguageModel implements
|
|
52
|
-
readonly specificationVersion = '
|
|
54
|
+
export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
55
|
+
readonly specificationVersion = 'v4';
|
|
53
56
|
|
|
54
57
|
readonly modelId: OpenAIChatModelId;
|
|
55
58
|
|
|
@@ -59,6 +62,20 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
59
62
|
|
|
60
63
|
private readonly config: OpenAIChatConfig;
|
|
61
64
|
|
|
65
|
+
static [WORKFLOW_SERIALIZE](model: OpenAIChatLanguageModel) {
|
|
66
|
+
return serializeModelOptions({
|
|
67
|
+
modelId: model.modelId,
|
|
68
|
+
config: model.config,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
73
|
+
modelId: OpenAIChatModelId;
|
|
74
|
+
config: OpenAIChatConfig;
|
|
75
|
+
}) {
|
|
76
|
+
return new OpenAIChatLanguageModel(options.modelId, options.config);
|
|
77
|
+
}
|
|
78
|
+
|
|
62
79
|
constructor(modelId: OpenAIChatModelId, config: OpenAIChatConfig) {
|
|
63
80
|
this.modelId = modelId;
|
|
64
81
|
this.config = config;
|
|
@@ -81,9 +98,10 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
81
98
|
seed,
|
|
82
99
|
tools,
|
|
83
100
|
toolChoice,
|
|
101
|
+
reasoning,
|
|
84
102
|
providerOptions,
|
|
85
|
-
}:
|
|
86
|
-
const warnings:
|
|
103
|
+
}: LanguageModelV4CallOptions) {
|
|
104
|
+
const warnings: SharedV4Warning[] = [];
|
|
87
105
|
|
|
88
106
|
// Parse provider options
|
|
89
107
|
const openaiOptions =
|
|
@@ -94,6 +112,12 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
94
112
|
})) ?? {};
|
|
95
113
|
|
|
96
114
|
const modelCapabilities = getOpenAILanguageModelCapabilities(this.modelId);
|
|
115
|
+
|
|
116
|
+
// AI SDK reasoning values map directly to the OpenAI reasoning values.
|
|
117
|
+
const resolvedReasoningEffort =
|
|
118
|
+
openaiOptions.reasoningEffort ??
|
|
119
|
+
(isCustomReasoning(reasoning) ? reasoning : undefined);
|
|
120
|
+
|
|
97
121
|
const isReasoningModel =
|
|
98
122
|
openaiOptions.forceReasoning ?? modelCapabilities.isReasoningModel;
|
|
99
123
|
|
|
@@ -168,7 +192,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
168
192
|
store: openaiOptions.store,
|
|
169
193
|
metadata: openaiOptions.metadata,
|
|
170
194
|
prediction: openaiOptions.prediction,
|
|
171
|
-
reasoning_effort:
|
|
195
|
+
reasoning_effort: resolvedReasoningEffort,
|
|
172
196
|
service_tier: openaiOptions.serviceTier,
|
|
173
197
|
prompt_cache_key: openaiOptions.promptCacheKey,
|
|
174
198
|
prompt_cache_retention: openaiOptions.promptCacheRetention,
|
|
@@ -184,7 +208,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
184
208
|
// when reasoning effort is none, gpt-5.1 models allow temperature, topP, logprobs
|
|
185
209
|
// https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
|
|
186
210
|
if (
|
|
187
|
-
|
|
211
|
+
resolvedReasoningEffort !== 'none' ||
|
|
188
212
|
!modelCapabilities.supportsNonReasoningParameters
|
|
189
213
|
) {
|
|
190
214
|
if (baseArgs.temperature != null) {
|
|
@@ -314,8 +338,8 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
314
338
|
}
|
|
315
339
|
|
|
316
340
|
async doGenerate(
|
|
317
|
-
options:
|
|
318
|
-
): Promise<
|
|
341
|
+
options: LanguageModelV4CallOptions,
|
|
342
|
+
): Promise<LanguageModelV4GenerateResult> {
|
|
319
343
|
const { args: body, warnings } = await this.getArgs(options);
|
|
320
344
|
|
|
321
345
|
const {
|
|
@@ -327,7 +351,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
327
351
|
path: '/chat/completions',
|
|
328
352
|
modelId: this.modelId,
|
|
329
353
|
}),
|
|
330
|
-
headers: combineHeaders(this.config.headers(), options.headers),
|
|
354
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
331
355
|
body,
|
|
332
356
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
333
357
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -338,7 +362,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
338
362
|
});
|
|
339
363
|
|
|
340
364
|
const choice = response.choices[0];
|
|
341
|
-
const content: Array<
|
|
365
|
+
const content: Array<LanguageModelV4Content> = [];
|
|
342
366
|
|
|
343
367
|
// text content:
|
|
344
368
|
const text = choice.message.content;
|
|
@@ -369,8 +393,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
369
393
|
|
|
370
394
|
// provider metadata:
|
|
371
395
|
const completionTokenDetails = response.usage?.completion_tokens_details;
|
|
372
|
-
const
|
|
373
|
-
const providerMetadata: SharedV3ProviderMetadata = { openai: {} };
|
|
396
|
+
const providerMetadata: SharedV4ProviderMetadata = { openai: {} };
|
|
374
397
|
if (completionTokenDetails?.accepted_prediction_tokens != null) {
|
|
375
398
|
providerMetadata.openai.acceptedPredictionTokens =
|
|
376
399
|
completionTokenDetails?.accepted_prediction_tokens;
|
|
@@ -402,8 +425,8 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
402
425
|
}
|
|
403
426
|
|
|
404
427
|
async doStream(
|
|
405
|
-
options:
|
|
406
|
-
): Promise<
|
|
428
|
+
options: LanguageModelV4CallOptions,
|
|
429
|
+
): Promise<LanguageModelV4StreamResult> {
|
|
407
430
|
const { args, warnings } = await this.getArgs(options);
|
|
408
431
|
|
|
409
432
|
const body = {
|
|
@@ -419,7 +442,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
419
442
|
path: '/chat/completions',
|
|
420
443
|
modelId: this.modelId,
|
|
421
444
|
}),
|
|
422
|
-
headers: combineHeaders(this.config.headers(), options.headers),
|
|
445
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
423
446
|
body,
|
|
424
447
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
425
448
|
successfulResponseHandler: createEventSourceResponseHandler(
|
|
@@ -429,17 +452,9 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
429
452
|
fetch: this.config.fetch,
|
|
430
453
|
});
|
|
431
454
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
function: {
|
|
436
|
-
name: string;
|
|
437
|
-
arguments: string;
|
|
438
|
-
};
|
|
439
|
-
hasFinished: boolean;
|
|
440
|
-
}> = [];
|
|
441
|
-
|
|
442
|
-
let finishReason: LanguageModelV3FinishReason = {
|
|
455
|
+
let toolCallTracker: StreamingToolCallTracker;
|
|
456
|
+
|
|
457
|
+
let finishReason: LanguageModelV4FinishReason = {
|
|
443
458
|
unified: 'other',
|
|
444
459
|
raw: undefined,
|
|
445
460
|
};
|
|
@@ -447,15 +462,19 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
447
462
|
let metadataExtracted = false;
|
|
448
463
|
let isActiveText = false;
|
|
449
464
|
|
|
450
|
-
const providerMetadata:
|
|
465
|
+
const providerMetadata: SharedV4ProviderMetadata = { openai: {} };
|
|
451
466
|
|
|
452
467
|
return {
|
|
453
468
|
stream: response.pipeThrough(
|
|
454
469
|
new TransformStream<
|
|
455
470
|
ParseResult<OpenAIChatChunk>,
|
|
456
|
-
|
|
471
|
+
LanguageModelV4StreamPart
|
|
457
472
|
>({
|
|
458
473
|
start(controller) {
|
|
474
|
+
toolCallTracker = new StreamingToolCallTracker(controller, {
|
|
475
|
+
generateId,
|
|
476
|
+
typeValidation: 'if-present',
|
|
477
|
+
});
|
|
459
478
|
controller.enqueue({ type: 'stream-start', warnings });
|
|
460
479
|
},
|
|
461
480
|
|
|
@@ -547,124 +566,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
547
566
|
|
|
548
567
|
if (delta.tool_calls != null) {
|
|
549
568
|
for (const toolCallDelta of delta.tool_calls) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
// Tool call start. OpenAI returns all information except the arguments in the first chunk.
|
|
553
|
-
if (toolCalls[index] == null) {
|
|
554
|
-
if (
|
|
555
|
-
toolCallDelta.type != null &&
|
|
556
|
-
toolCallDelta.type !== 'function'
|
|
557
|
-
) {
|
|
558
|
-
throw new InvalidResponseDataError({
|
|
559
|
-
data: toolCallDelta,
|
|
560
|
-
message: `Expected 'function' type.`,
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (toolCallDelta.id == null) {
|
|
565
|
-
throw new InvalidResponseDataError({
|
|
566
|
-
data: toolCallDelta,
|
|
567
|
-
message: `Expected 'id' to be a string.`,
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
if (toolCallDelta.function?.name == null) {
|
|
572
|
-
throw new InvalidResponseDataError({
|
|
573
|
-
data: toolCallDelta,
|
|
574
|
-
message: `Expected 'function.name' to be a string.`,
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
controller.enqueue({
|
|
579
|
-
type: 'tool-input-start',
|
|
580
|
-
id: toolCallDelta.id,
|
|
581
|
-
toolName: toolCallDelta.function.name,
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
toolCalls[index] = {
|
|
585
|
-
id: toolCallDelta.id,
|
|
586
|
-
type: 'function',
|
|
587
|
-
function: {
|
|
588
|
-
name: toolCallDelta.function.name,
|
|
589
|
-
arguments: toolCallDelta.function.arguments ?? '',
|
|
590
|
-
},
|
|
591
|
-
hasFinished: false,
|
|
592
|
-
};
|
|
593
|
-
|
|
594
|
-
const toolCall = toolCalls[index];
|
|
595
|
-
|
|
596
|
-
if (
|
|
597
|
-
toolCall.function?.name != null &&
|
|
598
|
-
toolCall.function?.arguments != null
|
|
599
|
-
) {
|
|
600
|
-
// send delta if the argument text has already started:
|
|
601
|
-
if (toolCall.function.arguments.length > 0) {
|
|
602
|
-
controller.enqueue({
|
|
603
|
-
type: 'tool-input-delta',
|
|
604
|
-
id: toolCall.id,
|
|
605
|
-
delta: toolCall.function.arguments,
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// check if tool call is complete
|
|
610
|
-
// (some providers send the full tool call in one chunk):
|
|
611
|
-
if (isParsableJson(toolCall.function.arguments)) {
|
|
612
|
-
controller.enqueue({
|
|
613
|
-
type: 'tool-input-end',
|
|
614
|
-
id: toolCall.id,
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
controller.enqueue({
|
|
618
|
-
type: 'tool-call',
|
|
619
|
-
toolCallId: toolCall.id ?? generateId(),
|
|
620
|
-
toolName: toolCall.function.name,
|
|
621
|
-
input: toolCall.function.arguments,
|
|
622
|
-
});
|
|
623
|
-
toolCall.hasFinished = true;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// existing tool call, merge if not finished
|
|
631
|
-
const toolCall = toolCalls[index];
|
|
632
|
-
|
|
633
|
-
if (toolCall.hasFinished) {
|
|
634
|
-
continue;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (toolCallDelta.function?.arguments != null) {
|
|
638
|
-
toolCall.function!.arguments +=
|
|
639
|
-
toolCallDelta.function?.arguments ?? '';
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// send delta
|
|
643
|
-
controller.enqueue({
|
|
644
|
-
type: 'tool-input-delta',
|
|
645
|
-
id: toolCall.id,
|
|
646
|
-
delta: toolCallDelta.function.arguments ?? '',
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
// check if tool call is complete
|
|
650
|
-
if (
|
|
651
|
-
toolCall.function?.name != null &&
|
|
652
|
-
toolCall.function?.arguments != null &&
|
|
653
|
-
isParsableJson(toolCall.function.arguments)
|
|
654
|
-
) {
|
|
655
|
-
controller.enqueue({
|
|
656
|
-
type: 'tool-input-end',
|
|
657
|
-
id: toolCall.id,
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
controller.enqueue({
|
|
661
|
-
type: 'tool-call',
|
|
662
|
-
toolCallId: toolCall.id ?? generateId(),
|
|
663
|
-
toolName: toolCall.function.name,
|
|
664
|
-
input: toolCall.function.arguments,
|
|
665
|
-
});
|
|
666
|
-
toolCall.hasFinished = true;
|
|
667
|
-
}
|
|
569
|
+
toolCallTracker.processDelta(toolCallDelta);
|
|
668
570
|
}
|
|
669
571
|
}
|
|
670
572
|
|
|
@@ -687,6 +589,8 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
|
|
|
687
589
|
controller.enqueue({ type: 'text-end', id: '0' });
|
|
688
590
|
}
|
|
689
591
|
|
|
592
|
+
toolCallTracker.flush();
|
|
593
|
+
|
|
690
594
|
controller.enqueue({
|
|
691
595
|
type: 'finish',
|
|
692
596
|
finishReason,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
lazySchema,
|
|
3
|
+
zodSchema,
|
|
4
|
+
type InferSchema,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
2
6
|
import { z } from 'zod/v4';
|
|
3
7
|
|
|
4
8
|
// https://platform.openai.com/docs/models
|
|
@@ -51,8 +55,13 @@ export type OpenAIChatModelId =
|
|
|
51
55
|
| 'gpt-5.2-chat-latest'
|
|
52
56
|
| 'gpt-5.2-pro'
|
|
53
57
|
| 'gpt-5.2-pro-2025-12-11'
|
|
58
|
+
| 'gpt-5.3-chat-latest'
|
|
54
59
|
| 'gpt-5.4'
|
|
55
60
|
| 'gpt-5.4-2026-03-05'
|
|
61
|
+
| 'gpt-5.4-mini'
|
|
62
|
+
| 'gpt-5.4-mini-2026-03-17'
|
|
63
|
+
| 'gpt-5.4-nano'
|
|
64
|
+
| 'gpt-5.4-nano-2026-03-17'
|
|
56
65
|
| 'gpt-5.4-pro'
|
|
57
66
|
| 'gpt-5.4-pro-2026-03-05'
|
|
58
67
|
| (string & {});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
LanguageModelV3CallOptions,
|
|
3
|
-
SharedV3Warning,
|
|
4
2
|
UnsupportedFunctionalityError,
|
|
3
|
+
type LanguageModelV4CallOptions,
|
|
4
|
+
type SharedV4Warning,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
|
-
import {
|
|
6
|
+
import type {
|
|
7
7
|
OpenAIChatToolChoice,
|
|
8
8
|
OpenAIChatFunctionTool,
|
|
9
9
|
} from './openai-chat-api';
|
|
@@ -12,17 +12,17 @@ export function prepareChatTools({
|
|
|
12
12
|
tools,
|
|
13
13
|
toolChoice,
|
|
14
14
|
}: {
|
|
15
|
-
tools:
|
|
16
|
-
toolChoice?:
|
|
15
|
+
tools: LanguageModelV4CallOptions['tools'];
|
|
16
|
+
toolChoice?: LanguageModelV4CallOptions['toolChoice'];
|
|
17
17
|
}): {
|
|
18
18
|
tools?: OpenAIChatFunctionTool[];
|
|
19
19
|
toolChoice?: OpenAIChatToolChoice;
|
|
20
|
-
toolWarnings: Array<
|
|
20
|
+
toolWarnings: Array<SharedV4Warning>;
|
|
21
21
|
} {
|
|
22
22
|
// when the tools array is empty, change it to undefined to prevent errors:
|
|
23
23
|
tools = tools?.length ? tools : undefined;
|
|
24
24
|
|
|
25
|
-
const toolWarnings:
|
|
25
|
+
const toolWarnings: SharedV4Warning[] = [];
|
|
26
26
|
|
|
27
27
|
if (tools == null) {
|
|
28
28
|
return { tools: undefined, toolChoice: undefined, toolWarnings };
|