@ai-sdk/openai 4.0.105 → 4.0.107
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 +26 -0
- package/dist/index.d.ts +51 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +611 -202
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +6 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +565 -164
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +127 -5
- package/package.json +3 -3
- package/src/chat/convert-to-openai-chat-messages.ts +22 -3
- package/src/chat/openai-chat-language-model.ts +27 -20
- package/src/completion/openai-completion-language-model.ts +11 -3
- package/src/embedding/openai-embedding-model.ts +2 -1
- package/src/files/openai-files.ts +10 -8
- package/src/image/openai-image-model-options.ts +10 -20
- package/src/image/openai-image-model.ts +10 -21
- package/src/live/openai-realtime-model-live.ts +15 -16
- package/src/openai-batch.ts +22 -6
- package/src/openai-config.ts +2 -1
- package/src/openai-decision-model.ts +2 -1
- package/src/openai-model-catalog.ts +935 -0
- package/src/openai-provider.ts +18 -13
- package/src/realtime/openai-realtime-factory.ts +6 -6
- package/src/realtime/openai-realtime-model.ts +7 -3
- package/src/resolve-openai-reasoning-effort.ts +45 -0
- package/src/responses/convert-to-openai-responses-input.ts +29 -11
- package/src/responses/openai-responses-language-model.ts +28 -22
- package/src/skills/openai-skills.ts +4 -2
- package/src/speech/openai-speech-model.ts +5 -1
- package/src/speech-translation/openai-speech-translation-model.ts +5 -1
- package/src/transcription/openai-transcription-model-options.ts +12 -0
- package/src/transcription/openai-transcription-model.ts +29 -56
- package/src/openai-language-model-capabilities.ts +0 -196
|
@@ -0,0 +1,935 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ModelCapabilities,
|
|
3
|
+
ModelCapability,
|
|
4
|
+
ModelEntry,
|
|
5
|
+
ProviderOptionDefinition,
|
|
6
|
+
ProviderOptionDefinitions,
|
|
7
|
+
} from '@ai-sdk/provider';
|
|
8
|
+
import type { OpenAILanguageModelResponsesOptions } from './responses/openai-responses-language-model-options';
|
|
9
|
+
|
|
10
|
+
type OpenAILanguageProviderOption = keyof OpenAILanguageModelResponsesOptions;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Options accepted under `providerOptions.openai`. Language options are the
|
|
14
|
+
* Responses API options. The `satisfies` check keeps this list in sync with
|
|
15
|
+
* the option schema.
|
|
16
|
+
*/
|
|
17
|
+
export const openaiProviderOptions = {
|
|
18
|
+
language: {
|
|
19
|
+
conversation: { description: 'OpenAI Conversation ID to continue.' },
|
|
20
|
+
include: { description: 'Extra fields to include in the response.' },
|
|
21
|
+
includeWebSearchSources: {
|
|
22
|
+
description: 'Include web search action sources.',
|
|
23
|
+
},
|
|
24
|
+
instructions: { description: 'Instructions for the model.' },
|
|
25
|
+
logprobs: { description: 'Return token log probabilities.' },
|
|
26
|
+
maxToolCalls: { description: 'Maximum number of built-in tool calls.' },
|
|
27
|
+
metadata: { description: 'Metadata to store with the generation.' },
|
|
28
|
+
parallelToolCalls: { description: 'Whether to use parallel tool calls.' },
|
|
29
|
+
previousResponseId: { description: 'Previous response ID to continue.' },
|
|
30
|
+
promptCacheKey: { description: 'Cache key for prompt caching.' },
|
|
31
|
+
promptCacheOptions: { description: 'Prompt cache mode, TTL, and prewarm.' },
|
|
32
|
+
promptCacheRetention: { description: 'Prompt cache retention policy.' },
|
|
33
|
+
reasoningEffort: { description: 'Reasoning effort.' },
|
|
34
|
+
reasoningEffortUpdate: {
|
|
35
|
+
description: 'Reasoning effort update from this response onward.',
|
|
36
|
+
},
|
|
37
|
+
reasoningMode: { description: 'Standard or pro reasoning mode.' },
|
|
38
|
+
reasoningContext: { description: 'Which reasoning items can be used.' },
|
|
39
|
+
reasoningSummary: { description: 'Reasoning summary output.' },
|
|
40
|
+
safetyIdentifier: { description: 'Identifier for safety monitoring.' },
|
|
41
|
+
serviceTier: { description: 'Service tier for the request.' },
|
|
42
|
+
store: { description: 'Whether to store the generation.' },
|
|
43
|
+
passThroughUnsupportedFiles: {
|
|
44
|
+
description: 'Send non-image file types as generic input files.',
|
|
45
|
+
},
|
|
46
|
+
strictJsonSchema: { description: 'Use strict JSON schema validation.' },
|
|
47
|
+
textVerbosity: { description: 'Verbosity of text responses.' },
|
|
48
|
+
truncation: { description: 'Output truncation behavior.' },
|
|
49
|
+
user: { description: 'End-user identifier for abuse monitoring.' },
|
|
50
|
+
systemMessageMode: { description: 'System message role override.' },
|
|
51
|
+
forceReasoning: { description: 'Treat the model as a reasoning model.' },
|
|
52
|
+
contextManagement: { description: 'Server-side context compaction.' },
|
|
53
|
+
compactionTrigger: { description: 'Request explicit compaction.' },
|
|
54
|
+
allowedTools: { description: 'Restrict callable tools to a subset.' },
|
|
55
|
+
} satisfies Record<OpenAILanguageProviderOption, ProviderOptionDefinition>,
|
|
56
|
+
} as const satisfies ProviderOptionDefinitions;
|
|
57
|
+
|
|
58
|
+
export type OpenAILanguageModelEntry = {
|
|
59
|
+
capabilities: {
|
|
60
|
+
language: NonNullable<ModelCapabilities['language']> & {
|
|
61
|
+
serviceTiers: {
|
|
62
|
+
flex: boolean;
|
|
63
|
+
priority: boolean;
|
|
64
|
+
};
|
|
65
|
+
configurationUpdates: boolean;
|
|
66
|
+
tools: NonNullable<ModelCapabilities['language']>['tools'] & {
|
|
67
|
+
async: boolean;
|
|
68
|
+
};
|
|
69
|
+
reasoning: NonNullable<ModelCapabilities['language']>['reasoning'] & {
|
|
70
|
+
sampling: 'unsupported' | 'reasoning-effort-none';
|
|
71
|
+
};
|
|
72
|
+
promptCache: {
|
|
73
|
+
retention: boolean;
|
|
74
|
+
options: ModelCapability;
|
|
75
|
+
};
|
|
76
|
+
endpoints: {
|
|
77
|
+
chat: {
|
|
78
|
+
temperatureWithoutReasoning: boolean;
|
|
79
|
+
functionToolsWithReasoning: boolean;
|
|
80
|
+
};
|
|
81
|
+
responses: {
|
|
82
|
+
logprobsRequireNonReasoning: boolean;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
providerOptions: {
|
|
88
|
+
language: Record<OpenAILanguageProviderOption, ModelCapability>;
|
|
89
|
+
};
|
|
90
|
+
requestConfig: {
|
|
91
|
+
language: {
|
|
92
|
+
systemMessageMode: 'remove' | 'system' | 'developer';
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
type OpenAILanguageModelDefinition = {
|
|
98
|
+
capabilities?: {
|
|
99
|
+
language?: Partial<
|
|
100
|
+
Pick<
|
|
101
|
+
OpenAILanguageModelEntry['capabilities']['language'],
|
|
102
|
+
'inputs' | 'objectGeneration' | 'tools' | 'reasoning'
|
|
103
|
+
>
|
|
104
|
+
>;
|
|
105
|
+
};
|
|
106
|
+
requestConfig?: {
|
|
107
|
+
language?: Partial<OpenAILanguageModelEntry['requestConfig']['language']>;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const configurableNonReasoning: OpenAILanguageModelDefinition = {
|
|
112
|
+
capabilities: {
|
|
113
|
+
language: {
|
|
114
|
+
reasoning: {
|
|
115
|
+
support: true,
|
|
116
|
+
efforts: ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
117
|
+
sampling: 'reasoning-effort-none',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Model-specific exceptions belong in the table; family rules provide defaults.
|
|
124
|
+
const openaiLanguageModelDefinitions: Record<
|
|
125
|
+
string,
|
|
126
|
+
OpenAILanguageModelDefinition
|
|
127
|
+
> = {
|
|
128
|
+
'gpt-6-astra': {},
|
|
129
|
+
'gpt-6.1-sol': {},
|
|
130
|
+
'gpt-6.1-terra': configurableNonReasoning,
|
|
131
|
+
'gpt-6-luna': configurableNonReasoning,
|
|
132
|
+
'gpt-6-sol': configurableNonReasoning,
|
|
133
|
+
'gpt-5.6': {},
|
|
134
|
+
'gpt-5.6-luna': {},
|
|
135
|
+
'gpt-5.6-sol': {},
|
|
136
|
+
'gpt-5.6-terra': {},
|
|
137
|
+
'gpt-5.5': {},
|
|
138
|
+
'gpt-5.4-pro': {},
|
|
139
|
+
'gpt-5.4': {},
|
|
140
|
+
'gpt-5.4-mini': {},
|
|
141
|
+
'gpt-5.4-nano': {},
|
|
142
|
+
'gpt-5.3-chat-latest': {},
|
|
143
|
+
'gpt-5.2-pro': {},
|
|
144
|
+
'gpt-5.2-chat-latest': {},
|
|
145
|
+
'gpt-5.2': {},
|
|
146
|
+
'gpt-5.1-codex-mini': {},
|
|
147
|
+
'gpt-5.1-codex': {},
|
|
148
|
+
'gpt-5.1-chat-latest': {},
|
|
149
|
+
'gpt-5.1': {},
|
|
150
|
+
'gpt-5-pro': {},
|
|
151
|
+
'gpt-5': {},
|
|
152
|
+
'gpt-5-mini': {},
|
|
153
|
+
'gpt-5-nano': {},
|
|
154
|
+
'gpt-5-codex': {},
|
|
155
|
+
'gpt-5-chat-latest': {
|
|
156
|
+
capabilities: {
|
|
157
|
+
language: {
|
|
158
|
+
objectGeneration: false,
|
|
159
|
+
tools: { usage: false, streaming: false, async: false },
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
'gpt-4.1': {},
|
|
164
|
+
'gpt-4.1-mini': {},
|
|
165
|
+
'gpt-4.1-nano': {},
|
|
166
|
+
'gpt-4o': {},
|
|
167
|
+
'gpt-4o-mini': {},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const openaiLanguageModelEntries: Readonly<
|
|
171
|
+
Record<string, OpenAILanguageModelEntry>
|
|
172
|
+
> = Object.fromEntries(
|
|
173
|
+
Object.entries(openaiLanguageModelDefinitions).map(
|
|
174
|
+
([modelId, definition]) => [
|
|
175
|
+
modelId,
|
|
176
|
+
createOpenAILanguageModelEntry(modelId, definition),
|
|
177
|
+
],
|
|
178
|
+
),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
function createOpenAILanguageModelEntry(
|
|
182
|
+
modelId: string,
|
|
183
|
+
definition?: OpenAILanguageModelDefinition,
|
|
184
|
+
): OpenAILanguageModelEntry {
|
|
185
|
+
const oSeriesVersion = getOSeriesVersion(modelId);
|
|
186
|
+
const gptVersion = getGptVersion(modelId);
|
|
187
|
+
const isRecognizedModelFamily =
|
|
188
|
+
oSeriesVersion != null ||
|
|
189
|
+
gptVersion != null ||
|
|
190
|
+
/^gpt-4o(?:-|$)/.test(modelId);
|
|
191
|
+
const isGptChatModel =
|
|
192
|
+
gptVersion?.minor == null &&
|
|
193
|
+
(gptVersion?.variant?.startsWith('chat') ?? false);
|
|
194
|
+
const isGptNanoModel = gptVersion?.variant?.startsWith('nano') ?? false;
|
|
195
|
+
const isGpt6OrLaterModel = gptVersion != null && gptVersion.major >= 6;
|
|
196
|
+
const isGpt56OrLaterModel =
|
|
197
|
+
isGpt6OrLaterModel ||
|
|
198
|
+
(gptVersion?.major === 5 && (gptVersion.minor ?? 0) >= 6);
|
|
199
|
+
// Pro, codex, and chat variants are excluded because their supported
|
|
200
|
+
// efforts are not known.
|
|
201
|
+
const isStandardGpt5Model =
|
|
202
|
+
gptVersion?.major === 5 &&
|
|
203
|
+
(gptVersion.variant == null ||
|
|
204
|
+
/^(?:(?:mini|nano|sol|luna|terra)(?:-\d{4}-\d{2}-\d{2})?|\d{4}-\d{2}-\d{2})$/.test(
|
|
205
|
+
gptVersion.variant,
|
|
206
|
+
));
|
|
207
|
+
const supportsFlexProcessing =
|
|
208
|
+
(oSeriesVersion != null && oSeriesVersion >= 3) ||
|
|
209
|
+
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
210
|
+
const supportsPriorityProcessing =
|
|
211
|
+
modelId.startsWith('gpt-4') ||
|
|
212
|
+
(gptVersion != null &&
|
|
213
|
+
gptVersion.major >= 5 &&
|
|
214
|
+
!isGptNanoModel &&
|
|
215
|
+
!isGptChatModel) ||
|
|
216
|
+
(oSeriesVersion != null && oSeriesVersion >= 3);
|
|
217
|
+
const isReasoningModel =
|
|
218
|
+
oSeriesVersion != null ||
|
|
219
|
+
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
220
|
+
const reasoningSampling: OpenAILanguageModelEntry['capabilities']['language']['reasoning']['sampling'] =
|
|
221
|
+
definition?.capabilities?.language?.reasoning?.sampling ??
|
|
222
|
+
(!isGpt6OrLaterModel &&
|
|
223
|
+
gptVersion != null &&
|
|
224
|
+
(gptVersion.major > 5 ||
|
|
225
|
+
(gptVersion.major === 5 && (gptVersion.minor ?? 0) >= 1))
|
|
226
|
+
? 'reasoning-effort-none'
|
|
227
|
+
: 'unsupported');
|
|
228
|
+
const sampling: OpenAILanguageModelEntry['capabilities']['language']['sampling']['temperature'] =
|
|
229
|
+
!isRecognizedModelFamily
|
|
230
|
+
? 'unknown'
|
|
231
|
+
: !isReasoningModel
|
|
232
|
+
? true
|
|
233
|
+
: reasoningSampling === 'reasoning-effort-none'
|
|
234
|
+
? { condition: 'Only when reasoning effort is none.' }
|
|
235
|
+
: false;
|
|
236
|
+
const chatTemperatureSupportedWithoutReasoning =
|
|
237
|
+
!modelId.startsWith('gpt-4o-search-preview') &&
|
|
238
|
+
!modelId.startsWith('gpt-4o-mini-search-preview');
|
|
239
|
+
|
|
240
|
+
const entry: Omit<OpenAILanguageModelEntry, 'providerOptions'> = {
|
|
241
|
+
capabilities: {
|
|
242
|
+
language: {
|
|
243
|
+
inputs: definition?.capabilities?.language?.inputs ?? {
|
|
244
|
+
image: definition != null ? true : 'unknown',
|
|
245
|
+
audio: definition != null ? false : 'unknown',
|
|
246
|
+
},
|
|
247
|
+
objectGeneration:
|
|
248
|
+
definition?.capabilities?.language?.objectGeneration ??
|
|
249
|
+
(definition != null ? true : 'unknown'),
|
|
250
|
+
tools: definition?.capabilities?.language?.tools ?? {
|
|
251
|
+
usage: definition != null ? true : 'unknown',
|
|
252
|
+
streaming: definition != null ? true : 'unknown',
|
|
253
|
+
async: isGpt6OrLaterModel,
|
|
254
|
+
},
|
|
255
|
+
reasoning: {
|
|
256
|
+
support: isRecognizedModelFamily ? isReasoningModel : 'unknown',
|
|
257
|
+
efforts:
|
|
258
|
+
definition?.capabilities?.language?.reasoning?.efforts ??
|
|
259
|
+
(isGpt6OrLaterModel
|
|
260
|
+
? ['low', 'medium', 'high', 'xhigh', 'max']
|
|
261
|
+
: isStandardGpt5Model
|
|
262
|
+
? getGpt5ReasoningEfforts(gptVersion.minor ?? 0)
|
|
263
|
+
: undefined),
|
|
264
|
+
sampling: reasoningSampling,
|
|
265
|
+
},
|
|
266
|
+
sampling: {
|
|
267
|
+
temperature: chatTemperatureSupportedWithoutReasoning
|
|
268
|
+
? sampling
|
|
269
|
+
: {
|
|
270
|
+
condition:
|
|
271
|
+
'Not supported by Chat Completions search preview models.',
|
|
272
|
+
},
|
|
273
|
+
topP: sampling,
|
|
274
|
+
logprobs:
|
|
275
|
+
isReasoningModel && !isGpt6OrLaterModel
|
|
276
|
+
? {
|
|
277
|
+
condition:
|
|
278
|
+
reasoningSampling === 'reasoning-effort-none'
|
|
279
|
+
? 'Chat Completions requires reasoning effort none; Responses support is model-dependent.'
|
|
280
|
+
: 'Not supported by Chat Completions while reasoning; Responses support is model-dependent.',
|
|
281
|
+
}
|
|
282
|
+
: sampling,
|
|
283
|
+
},
|
|
284
|
+
serviceTiers: {
|
|
285
|
+
flex: supportsFlexProcessing,
|
|
286
|
+
priority: supportsPriorityProcessing,
|
|
287
|
+
},
|
|
288
|
+
configurationUpdates: isGpt6OrLaterModel,
|
|
289
|
+
promptCache: {
|
|
290
|
+
retention: !isGpt6OrLaterModel,
|
|
291
|
+
// Prompt cache options are documented for GPT-5.6 and later.
|
|
292
|
+
options: isRecognizedModelFamily ? isGpt56OrLaterModel : 'unknown',
|
|
293
|
+
},
|
|
294
|
+
endpoints: {
|
|
295
|
+
chat: {
|
|
296
|
+
temperatureWithoutReasoning:
|
|
297
|
+
chatTemperatureSupportedWithoutReasoning,
|
|
298
|
+
// GPT-5.6 rejects function tools with reasoning in Chat Completions.
|
|
299
|
+
functionToolsWithReasoning: !(
|
|
300
|
+
gptVersion?.major === 5 && (gptVersion.minor ?? 0) >= 6
|
|
301
|
+
),
|
|
302
|
+
},
|
|
303
|
+
responses: {
|
|
304
|
+
logprobsRequireNonReasoning: isGpt6OrLaterModel,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
requestConfig: {
|
|
310
|
+
language: {
|
|
311
|
+
systemMessageMode:
|
|
312
|
+
definition?.requestConfig?.language?.systemMessageMode ??
|
|
313
|
+
(isReasoningModel ? 'developer' : 'system'),
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// Options default to supported; exceptions reuse capabilities declared above.
|
|
319
|
+
const { language } = entry.capabilities;
|
|
320
|
+
return {
|
|
321
|
+
...entry,
|
|
322
|
+
providerOptions: {
|
|
323
|
+
language: declareProviderOptions(
|
|
324
|
+
openaiProviderOptions.language,
|
|
325
|
+
definition != null ? true : 'unknown',
|
|
326
|
+
{
|
|
327
|
+
logprobs: language.sampling.logprobs,
|
|
328
|
+
maxToolCalls: language.tools.usage,
|
|
329
|
+
parallelToolCalls: language.tools.usage,
|
|
330
|
+
allowedTools: language.tools.usage,
|
|
331
|
+
reasoningEffort: language.reasoning.support,
|
|
332
|
+
reasoningSummary: language.reasoning.support,
|
|
333
|
+
reasoningMode: language.reasoning.support,
|
|
334
|
+
reasoningContext: language.reasoning.support,
|
|
335
|
+
reasoningEffortUpdate: language.configurationUpdates,
|
|
336
|
+
promptCacheOptions: language.promptCache.options,
|
|
337
|
+
promptCacheRetention: language.promptCache.retention,
|
|
338
|
+
},
|
|
339
|
+
),
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Applies one default support value to every option, except the listed ones. */
|
|
345
|
+
function declareProviderOptions<Option extends string>(
|
|
346
|
+
definitions: Readonly<Record<Option, ProviderOptionDefinition>>,
|
|
347
|
+
support: ModelCapability,
|
|
348
|
+
exceptions: Partial<Record<Option, ModelCapability>>,
|
|
349
|
+
): Record<Option, ModelCapability> {
|
|
350
|
+
return Object.fromEntries(
|
|
351
|
+
(Object.keys(definitions) as Option[]).map(option => [
|
|
352
|
+
option,
|
|
353
|
+
exceptions[option] ?? support,
|
|
354
|
+
]),
|
|
355
|
+
) as Record<Option, ModelCapability>;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function getGpt5ReasoningEfforts(minor: number): readonly string[] {
|
|
359
|
+
if (minor === 0) {
|
|
360
|
+
return ['minimal', 'low', 'medium', 'high'];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (minor === 1) {
|
|
364
|
+
return ['none', 'low', 'medium', 'high'];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (minor < 6) {
|
|
368
|
+
return ['none', 'low', 'medium', 'high', 'xhigh'];
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return ['none', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function getOSeriesVersion(modelId: string): number | undefined {
|
|
375
|
+
const match = /^o(\d+)(?:-|$)/.exec(modelId);
|
|
376
|
+
return match == null ? undefined : Number(match[1]);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function getGptVersion(modelId: string):
|
|
380
|
+
| {
|
|
381
|
+
major: number;
|
|
382
|
+
minor: number | undefined;
|
|
383
|
+
variant: string | undefined;
|
|
384
|
+
}
|
|
385
|
+
| undefined {
|
|
386
|
+
const match = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/.exec(modelId);
|
|
387
|
+
return match == null
|
|
388
|
+
? undefined
|
|
389
|
+
: {
|
|
390
|
+
major: Number(match[1]),
|
|
391
|
+
minor: match[2] == null ? undefined : Number(match[2]),
|
|
392
|
+
variant: match[3],
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
type OperationModelEntry<Section extends keyof ModelCapabilities> = {
|
|
397
|
+
readonly capabilities: {
|
|
398
|
+
readonly [Key in Section]-?: NonNullable<ModelCapabilities[Key]>;
|
|
399
|
+
};
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
export type OpenAIImageModelEntry = OperationModelEntry<'image'> & {
|
|
403
|
+
readonly requestConfig: {
|
|
404
|
+
readonly image: {
|
|
405
|
+
readonly responseFormat: 'provider-default' | 'b64_json';
|
|
406
|
+
};
|
|
407
|
+
};
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
export type OpenAITranscriptionModelEntry =
|
|
411
|
+
OperationModelEntry<'transcription'> & {
|
|
412
|
+
readonly requestConfig: {
|
|
413
|
+
readonly transcription: {
|
|
414
|
+
readonly responseFormat?: 'verbose_json' | 'diarized_json';
|
|
415
|
+
readonly chunkingStrategy?: 'auto';
|
|
416
|
+
readonly languageField: 'language' | 'languages';
|
|
417
|
+
};
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const openaiEmbeddingModelEntries: Readonly<
|
|
422
|
+
Record<string, OperationModelEntry<'embedding'>>
|
|
423
|
+
> = {
|
|
424
|
+
'text-embedding-3-large': {
|
|
425
|
+
capabilities: {
|
|
426
|
+
embedding: { defaultDimensions: 3072, customDimensions: true },
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
'text-embedding-3-small': {
|
|
430
|
+
capabilities: {
|
|
431
|
+
embedding: { defaultDimensions: 1536, customDimensions: true },
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
'text-embedding-ada-002': {
|
|
435
|
+
capabilities: {
|
|
436
|
+
embedding: { defaultDimensions: 1536, customDimensions: false },
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const gptImageSizes = ['1024x1024', '1536x1024', '1024x1536'];
|
|
442
|
+
const customImageSizes = {
|
|
443
|
+
description: 'Standard presets and custom dimensions',
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
function createGptImageEntry(
|
|
447
|
+
sizes: NonNullable<ModelCapabilities['image']>['sizes'],
|
|
448
|
+
): OpenAIImageModelEntry {
|
|
449
|
+
return {
|
|
450
|
+
capabilities: {
|
|
451
|
+
image: {
|
|
452
|
+
sizes,
|
|
453
|
+
fileInput: true,
|
|
454
|
+
maskInput: true,
|
|
455
|
+
maxImagesPerCall: 10,
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
requestConfig: { image: { responseFormat: 'provider-default' } },
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const openaiImageModelEntries: Readonly<Record<string, OpenAIImageModelEntry>> =
|
|
463
|
+
{
|
|
464
|
+
'gpt-image-2.5-flare': createGptImageEntry(customImageSizes),
|
|
465
|
+
'gpt-image-2.5-sunburst': createGptImageEntry(customImageSizes),
|
|
466
|
+
'gpt-image-2': createGptImageEntry(gptImageSizes),
|
|
467
|
+
'gpt-image-1.5': createGptImageEntry(gptImageSizes),
|
|
468
|
+
'gpt-image-1-mini': createGptImageEntry(gptImageSizes),
|
|
469
|
+
'gpt-image-1': createGptImageEntry(gptImageSizes),
|
|
470
|
+
'dall-e-3': {
|
|
471
|
+
capabilities: {
|
|
472
|
+
image: {
|
|
473
|
+
sizes: ['1024x1024', '1792x1024', '1024x1792'],
|
|
474
|
+
fileInput: false,
|
|
475
|
+
maskInput: false,
|
|
476
|
+
maxImagesPerCall: 1,
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
requestConfig: { image: { responseFormat: 'b64_json' } },
|
|
480
|
+
},
|
|
481
|
+
'dall-e-2': {
|
|
482
|
+
capabilities: {
|
|
483
|
+
image: {
|
|
484
|
+
sizes: ['256x256', '512x512', '1024x1024'],
|
|
485
|
+
fileInput: true,
|
|
486
|
+
maskInput: true,
|
|
487
|
+
maxImagesPerCall: 10,
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
requestConfig: { image: { responseFormat: 'b64_json' } },
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const imageModelAliases: Readonly<Record<string, OpenAIImageModelEntry>> = {
|
|
495
|
+
'gpt-image-2.5-flare-2026-09-08':
|
|
496
|
+
openaiImageModelEntries['gpt-image-2.5-flare'],
|
|
497
|
+
'gpt-image-2.5-sunburst-2026-09-08':
|
|
498
|
+
openaiImageModelEntries['gpt-image-2.5-sunburst'],
|
|
499
|
+
'chatgpt-image-latest': createGptImageEntry({ description: 'Unknown' }),
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
function resolveOpenAIImageModelEntry(modelId: string): OpenAIImageModelEntry {
|
|
503
|
+
if (Object.prototype.hasOwnProperty.call(openaiImageModelEntries, modelId)) {
|
|
504
|
+
return openaiImageModelEntries[modelId];
|
|
505
|
+
}
|
|
506
|
+
if (Object.prototype.hasOwnProperty.call(imageModelAliases, modelId)) {
|
|
507
|
+
return imageModelAliases[modelId];
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
capabilities: {
|
|
511
|
+
image: {
|
|
512
|
+
sizes: { description: 'Unknown' },
|
|
513
|
+
fileInput: 'unknown',
|
|
514
|
+
maskInput: 'unknown',
|
|
515
|
+
maxImagesPerCall: modelId.startsWith('gpt-image-') ? 10 : 1,
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
requestConfig: {
|
|
519
|
+
image: {
|
|
520
|
+
responseFormat:
|
|
521
|
+
modelId.startsWith('chatgpt-image-') ||
|
|
522
|
+
modelId.startsWith('gpt-image-')
|
|
523
|
+
? 'provider-default'
|
|
524
|
+
: 'b64_json',
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export const openaiImageModelMaxImagesPerCall: Record<string, number> =
|
|
531
|
+
Object.fromEntries(
|
|
532
|
+
Object.entries({ ...openaiImageModelEntries, ...imageModelAliases }).map(
|
|
533
|
+
([modelId, entry]) => [
|
|
534
|
+
modelId,
|
|
535
|
+
entry.capabilities.image.maxImagesPerCall ?? 1,
|
|
536
|
+
],
|
|
537
|
+
),
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
const defaultTranscriptionRequestConfig: OpenAITranscriptionModelEntry['requestConfig']['transcription'] =
|
|
541
|
+
{
|
|
542
|
+
languageField: 'language',
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
const openaiTranscriptionModelEntries: Readonly<
|
|
546
|
+
Record<string, OpenAITranscriptionModelEntry>
|
|
547
|
+
> = {
|
|
548
|
+
'whisper-1': {
|
|
549
|
+
capabilities: {
|
|
550
|
+
transcription: {
|
|
551
|
+
transcription: true,
|
|
552
|
+
streaming: false,
|
|
553
|
+
streamingDelay: false,
|
|
554
|
+
duration: true,
|
|
555
|
+
segments: true,
|
|
556
|
+
language: true,
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
requestConfig: {
|
|
560
|
+
transcription: {
|
|
561
|
+
...defaultTranscriptionRequestConfig,
|
|
562
|
+
responseFormat: 'verbose_json',
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
'gpt-4o-mini-transcribe': {
|
|
567
|
+
capabilities: {
|
|
568
|
+
transcription: {
|
|
569
|
+
transcription: true,
|
|
570
|
+
streaming: false,
|
|
571
|
+
streamingDelay: false,
|
|
572
|
+
duration: false,
|
|
573
|
+
segments: false,
|
|
574
|
+
language: false,
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
requestConfig: { transcription: defaultTranscriptionRequestConfig },
|
|
578
|
+
},
|
|
579
|
+
'gpt-4o-transcribe': {
|
|
580
|
+
capabilities: {
|
|
581
|
+
transcription: {
|
|
582
|
+
transcription: true,
|
|
583
|
+
streaming: false,
|
|
584
|
+
streamingDelay: false,
|
|
585
|
+
duration: false,
|
|
586
|
+
segments: false,
|
|
587
|
+
language: false,
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
requestConfig: { transcription: defaultTranscriptionRequestConfig },
|
|
591
|
+
},
|
|
592
|
+
'gpt-4o-transcribe-diarize': {
|
|
593
|
+
capabilities: {
|
|
594
|
+
transcription: {
|
|
595
|
+
transcription: true,
|
|
596
|
+
streaming: false,
|
|
597
|
+
streamingDelay: false,
|
|
598
|
+
duration: true,
|
|
599
|
+
segments: true,
|
|
600
|
+
language: false,
|
|
601
|
+
},
|
|
602
|
+
},
|
|
603
|
+
requestConfig: {
|
|
604
|
+
transcription: {
|
|
605
|
+
...defaultTranscriptionRequestConfig,
|
|
606
|
+
responseFormat: 'diarized_json',
|
|
607
|
+
chunkingStrategy: 'auto',
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
'gpt-transcribe': {
|
|
612
|
+
capabilities: {
|
|
613
|
+
transcription: {
|
|
614
|
+
transcription: true,
|
|
615
|
+
streaming: true,
|
|
616
|
+
streamingDelay: false,
|
|
617
|
+
duration: false,
|
|
618
|
+
segments: false,
|
|
619
|
+
language: true,
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
requestConfig: { transcription: { languageField: 'languages' } },
|
|
623
|
+
},
|
|
624
|
+
'gpt-live-transcribe': {
|
|
625
|
+
capabilities: {
|
|
626
|
+
transcription: {
|
|
627
|
+
transcription: false,
|
|
628
|
+
streaming: true,
|
|
629
|
+
streamingDelay: true,
|
|
630
|
+
duration: false,
|
|
631
|
+
segments: false,
|
|
632
|
+
language: false,
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
requestConfig: { transcription: { languageField: 'languages' } },
|
|
636
|
+
},
|
|
637
|
+
'gpt-realtime-whisper': {
|
|
638
|
+
capabilities: {
|
|
639
|
+
transcription: {
|
|
640
|
+
transcription: false,
|
|
641
|
+
streaming: true,
|
|
642
|
+
streamingDelay: true,
|
|
643
|
+
duration: false,
|
|
644
|
+
segments: false,
|
|
645
|
+
language: false,
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
requestConfig: { transcription: defaultTranscriptionRequestConfig },
|
|
649
|
+
},
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
const transcriptionSnapshotModelIds = [
|
|
653
|
+
'gpt-transcribe',
|
|
654
|
+
'gpt-live-transcribe',
|
|
655
|
+
'gpt-realtime-whisper',
|
|
656
|
+
] as const;
|
|
657
|
+
|
|
658
|
+
function resolveOpenAITranscriptionModelEntry(
|
|
659
|
+
modelId: string,
|
|
660
|
+
): OpenAITranscriptionModelEntry {
|
|
661
|
+
if (
|
|
662
|
+
Object.prototype.hasOwnProperty.call(
|
|
663
|
+
openaiTranscriptionModelEntries,
|
|
664
|
+
modelId,
|
|
665
|
+
)
|
|
666
|
+
) {
|
|
667
|
+
return openaiTranscriptionModelEntries[modelId];
|
|
668
|
+
}
|
|
669
|
+
for (const baseModelId of transcriptionSnapshotModelIds) {
|
|
670
|
+
if (modelId.startsWith(`${baseModelId}-`)) {
|
|
671
|
+
return openaiTranscriptionModelEntries[baseModelId];
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
capabilities: {
|
|
676
|
+
transcription: {
|
|
677
|
+
transcription: 'unknown',
|
|
678
|
+
streaming: 'unknown',
|
|
679
|
+
streamingDelay: 'unknown',
|
|
680
|
+
duration: 'unknown',
|
|
681
|
+
segments: 'unknown',
|
|
682
|
+
language: 'unknown',
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
requestConfig: { transcription: defaultTranscriptionRequestConfig },
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const openaiSpeechTranslationModelEntries: Readonly<
|
|
690
|
+
Record<string, OperationModelEntry<'speechTranslation'>>
|
|
691
|
+
> = {
|
|
692
|
+
'gpt-realtime-translate': {
|
|
693
|
+
capabilities: {
|
|
694
|
+
speechTranslation: {
|
|
695
|
+
translatedAudio: true,
|
|
696
|
+
translatedText: true,
|
|
697
|
+
sourceTranscript: true,
|
|
698
|
+
},
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
const openaiSpeechModelEntries: Readonly<
|
|
704
|
+
Record<string, OperationModelEntry<'speech'>>
|
|
705
|
+
> = {
|
|
706
|
+
'tts-1': { capabilities: { speech: { instructions: false } } },
|
|
707
|
+
'tts-1-hd': { capabilities: { speech: { instructions: false } } },
|
|
708
|
+
'gpt-4o-mini-tts': { capabilities: { speech: { instructions: true } } },
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
type RealtimeRequestConfig = {
|
|
712
|
+
api: 'live' | 'realtime';
|
|
713
|
+
startup: 'session-start' | 'session-update';
|
|
714
|
+
finalization: 'session-close' | 'transport-close';
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
type OpenAIRealtimeModelEntry = {
|
|
718
|
+
capabilities: { realtime: NonNullable<ModelCapabilities['realtime']> };
|
|
719
|
+
requestConfig: { realtime: RealtimeRequestConfig };
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
type OpenAIRequestConfig = {
|
|
723
|
+
language?: OpenAILanguageModelEntry['requestConfig']['language'];
|
|
724
|
+
image?: OpenAIImageModelEntry['requestConfig']['image'];
|
|
725
|
+
transcription?: OpenAITranscriptionModelEntry['requestConfig']['transcription'];
|
|
726
|
+
realtime?: RealtimeRequestConfig;
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
type OpenAIModelEntry = ModelEntry<OpenAIRequestConfig> & {
|
|
730
|
+
capabilities: ModelCapabilities & {
|
|
731
|
+
language?: OpenAILanguageModelEntry['capabilities']['language'];
|
|
732
|
+
};
|
|
733
|
+
providerOptions?: {
|
|
734
|
+
language?: OpenAILanguageModelEntry['providerOptions']['language'];
|
|
735
|
+
};
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
const liveModelEntry = {
|
|
739
|
+
capabilities: {
|
|
740
|
+
realtime: {
|
|
741
|
+
conversation: 'continuous',
|
|
742
|
+
transports: ['websocket', 'webrtc'],
|
|
743
|
+
connections: ['server-websocket', 'webrtc'],
|
|
744
|
+
},
|
|
745
|
+
},
|
|
746
|
+
requestConfig: {
|
|
747
|
+
realtime: {
|
|
748
|
+
api: 'live',
|
|
749
|
+
startup: 'session-start',
|
|
750
|
+
finalization: 'session-close',
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
} as const satisfies OpenAIRealtimeModelEntry;
|
|
754
|
+
|
|
755
|
+
const realtimeModelEntry = {
|
|
756
|
+
capabilities: {
|
|
757
|
+
realtime: {
|
|
758
|
+
conversation: 'turn-based',
|
|
759
|
+
transports: ['websocket'],
|
|
760
|
+
connections: ['client-secret-websocket'],
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
requestConfig: {
|
|
764
|
+
realtime: {
|
|
765
|
+
api: 'realtime',
|
|
766
|
+
startup: 'session-update',
|
|
767
|
+
finalization: 'transport-close',
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
} as const satisfies OpenAIRealtimeModelEntry;
|
|
771
|
+
|
|
772
|
+
const realtimeModelEntries = {
|
|
773
|
+
'gpt-live-1': liveModelEntry,
|
|
774
|
+
'gpt-realtime': realtimeModelEntry,
|
|
775
|
+
} as const;
|
|
776
|
+
|
|
777
|
+
export type OpenAIKnownLiveModelId = {
|
|
778
|
+
[ModelId in keyof typeof realtimeModelEntries]: (typeof realtimeModelEntries)[ModelId]['requestConfig']['realtime']['api'] extends 'live'
|
|
779
|
+
? ModelId
|
|
780
|
+
: never;
|
|
781
|
+
}[keyof typeof realtimeModelEntries];
|
|
782
|
+
|
|
783
|
+
/** Keep the existing realtime interface's shape while deriving it from the catalog. */
|
|
784
|
+
export const openaiLiveRealtimeCapabilities = {
|
|
785
|
+
...liveModelEntry.capabilities.realtime,
|
|
786
|
+
startup: liveModelEntry.requestConfig.realtime.startup,
|
|
787
|
+
finalization: liveModelEntry.requestConfig.realtime.finalization,
|
|
788
|
+
} as const;
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* The catalog consumed by request builders and documentation. Source declarations
|
|
792
|
+
* are grouped by operation; entries for the same model combine their sections.
|
|
793
|
+
* An absent section is undeclared, not an instruction to reject a request.
|
|
794
|
+
*/
|
|
795
|
+
export const openaiModelCatalog: Readonly<Record<string, OpenAIModelEntry>> =
|
|
796
|
+
combineModelEntries([
|
|
797
|
+
openaiLanguageModelEntries,
|
|
798
|
+
openaiEmbeddingModelEntries,
|
|
799
|
+
openaiImageModelEntries,
|
|
800
|
+
openaiTranscriptionModelEntries,
|
|
801
|
+
openaiSpeechTranslationModelEntries,
|
|
802
|
+
openaiSpeechModelEntries,
|
|
803
|
+
realtimeModelEntries,
|
|
804
|
+
]);
|
|
805
|
+
|
|
806
|
+
function combineModelEntries(
|
|
807
|
+
catalogs: ReadonlyArray<Readonly<Record<string, OpenAIModelEntry>>>,
|
|
808
|
+
): Record<string, OpenAIModelEntry> {
|
|
809
|
+
const entries = new Map<string, OpenAIModelEntry>();
|
|
810
|
+
for (const catalog of catalogs) {
|
|
811
|
+
for (const [modelId, entry] of Object.entries(catalog)) {
|
|
812
|
+
const previous = entries.get(modelId);
|
|
813
|
+
entries.set(modelId, {
|
|
814
|
+
capabilities: { ...previous?.capabilities, ...entry.capabilities },
|
|
815
|
+
...(previous?.providerOptions != null || entry.providerOptions != null
|
|
816
|
+
? {
|
|
817
|
+
providerOptions: {
|
|
818
|
+
...previous?.providerOptions,
|
|
819
|
+
...entry.providerOptions,
|
|
820
|
+
},
|
|
821
|
+
}
|
|
822
|
+
: {}),
|
|
823
|
+
...(previous?.requestConfig != null || entry.requestConfig != null
|
|
824
|
+
? {
|
|
825
|
+
requestConfig: {
|
|
826
|
+
...previous?.requestConfig,
|
|
827
|
+
...entry.requestConfig,
|
|
828
|
+
},
|
|
829
|
+
}
|
|
830
|
+
: {}),
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return Object.fromEntries(entries);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function findModelEntry(modelId: string): OpenAIModelEntry | undefined {
|
|
838
|
+
return Object.prototype.hasOwnProperty.call(openaiModelCatalog, modelId)
|
|
839
|
+
? openaiModelCatalog[modelId]
|
|
840
|
+
: undefined;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
export function getOpenAILanguageModelEntry(
|
|
844
|
+
modelId: string,
|
|
845
|
+
): OpenAILanguageModelEntry {
|
|
846
|
+
const entry = findModelEntry(modelId);
|
|
847
|
+
return entry?.capabilities.language != null &&
|
|
848
|
+
entry.providerOptions?.language != null &&
|
|
849
|
+
entry.requestConfig?.language != null
|
|
850
|
+
? {
|
|
851
|
+
capabilities: { language: entry.capabilities.language },
|
|
852
|
+
providerOptions: { language: entry.providerOptions.language },
|
|
853
|
+
requestConfig: { language: entry.requestConfig.language },
|
|
854
|
+
}
|
|
855
|
+
: createOpenAILanguageModelEntry(modelId);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
export function getOpenAIImageModelEntry(
|
|
859
|
+
modelId: string,
|
|
860
|
+
): OpenAIImageModelEntry {
|
|
861
|
+
const entry = findModelEntry(modelId);
|
|
862
|
+
return entry?.capabilities.image != null && entry.requestConfig?.image != null
|
|
863
|
+
? {
|
|
864
|
+
capabilities: { image: entry.capabilities.image },
|
|
865
|
+
requestConfig: { image: entry.requestConfig.image },
|
|
866
|
+
}
|
|
867
|
+
: resolveOpenAIImageModelEntry(modelId);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
export function getOpenAITranscriptionModelEntry(
|
|
871
|
+
modelId: string,
|
|
872
|
+
): OpenAITranscriptionModelEntry {
|
|
873
|
+
const entry = findModelEntry(modelId);
|
|
874
|
+
return entry?.capabilities.transcription != null &&
|
|
875
|
+
entry.requestConfig?.transcription != null
|
|
876
|
+
? {
|
|
877
|
+
capabilities: { transcription: entry.capabilities.transcription },
|
|
878
|
+
requestConfig: { transcription: entry.requestConfig.transcription },
|
|
879
|
+
}
|
|
880
|
+
: resolveOpenAITranscriptionModelEntry(modelId);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
export function getOpenAIRealtimeModelEntry(
|
|
884
|
+
modelId: string,
|
|
885
|
+
api?: 'live' | 'realtime',
|
|
886
|
+
): OpenAIRealtimeModelEntry {
|
|
887
|
+
const entry = findModelEntry(modelId);
|
|
888
|
+
if (
|
|
889
|
+
entry?.capabilities.realtime != null &&
|
|
890
|
+
entry.requestConfig?.realtime != null &&
|
|
891
|
+
(api == null || entry.requestConfig.realtime.api === api)
|
|
892
|
+
) {
|
|
893
|
+
return {
|
|
894
|
+
capabilities: { realtime: entry.capabilities.realtime },
|
|
895
|
+
requestConfig: { realtime: entry.requestConfig.realtime },
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
return api === 'live' ? liveModelEntry : realtimeModelEntry;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
export type OpenAILanguageModelCapabilities = {
|
|
902
|
+
isReasoningModel: boolean;
|
|
903
|
+
systemMessageMode: 'remove' | 'system' | 'developer';
|
|
904
|
+
supportsFlexProcessing: boolean;
|
|
905
|
+
supportsPriorityProcessing: boolean;
|
|
906
|
+
supportsConfigurationUpdate: boolean;
|
|
907
|
+
supportsAsyncToolCalling: boolean;
|
|
908
|
+
supportedReasoningEfforts: readonly string[] | undefined;
|
|
909
|
+
supportsChatFunctionToolsWithReasoning: boolean;
|
|
910
|
+
supportsNonReasoningParameters: boolean;
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
/** Compatibility projection for callers that still consume the legacy shape. */
|
|
914
|
+
export function getOpenAILanguageModelCapabilities(
|
|
915
|
+
modelId: string,
|
|
916
|
+
): OpenAILanguageModelCapabilities {
|
|
917
|
+
const {
|
|
918
|
+
capabilities: { language: capabilities },
|
|
919
|
+
requestConfig: { language: requestConfig },
|
|
920
|
+
} = getOpenAILanguageModelEntry(modelId);
|
|
921
|
+
|
|
922
|
+
return {
|
|
923
|
+
isReasoningModel: capabilities.reasoning.support === true,
|
|
924
|
+
systemMessageMode: requestConfig.systemMessageMode,
|
|
925
|
+
supportsFlexProcessing: capabilities.serviceTiers.flex,
|
|
926
|
+
supportsPriorityProcessing: capabilities.serviceTiers.priority,
|
|
927
|
+
supportsConfigurationUpdate: capabilities.configurationUpdates,
|
|
928
|
+
supportsAsyncToolCalling: capabilities.tools.async,
|
|
929
|
+
supportedReasoningEfforts: capabilities.reasoning.efforts,
|
|
930
|
+
supportsChatFunctionToolsWithReasoning:
|
|
931
|
+
capabilities.endpoints.chat.functionToolsWithReasoning,
|
|
932
|
+
supportsNonReasoningParameters:
|
|
933
|
+
capabilities.reasoning.sampling === 'reasoning-effort-none',
|
|
934
|
+
};
|
|
935
|
+
}
|