@ai-sdk/openai 4.0.104 → 4.0.106
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 +34 -0
- package/dist/index.d.ts +50 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +651 -185
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +4 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +573 -142
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +101 -9
- package/package.json +3 -3
- package/src/chat/openai-chat-language-model.ts +50 -28
- 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-options.ts +1 -0
- package/src/openai-decision-model.ts +48 -6
- 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/openai-responses-language-model.ts +35 -36
- 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.ts +25 -56
- package/src/openai-language-model-capabilities.ts +0 -101
package/src/openai-provider.ts
CHANGED
|
@@ -12,6 +12,8 @@ import type {
|
|
|
12
12
|
Experimental_SpeechTranslationModelV4 as SpeechTranslationModelV4,
|
|
13
13
|
} from '@ai-sdk/provider';
|
|
14
14
|
import {
|
|
15
|
+
type Resolvable,
|
|
16
|
+
resolveWith,
|
|
15
17
|
loadApiKey,
|
|
16
18
|
loadOptionalSetting,
|
|
17
19
|
validateBaseURL,
|
|
@@ -175,8 +177,9 @@ export interface OpenAIProviderSettings {
|
|
|
175
177
|
|
|
176
178
|
/**
|
|
177
179
|
* API key for authenticating requests.
|
|
180
|
+
* A function is called before each request and may return a promise.
|
|
178
181
|
*/
|
|
179
|
-
apiKey?: string
|
|
182
|
+
apiKey?: Resolvable<string>;
|
|
180
183
|
|
|
181
184
|
/**
|
|
182
185
|
* OpenAI Organization.
|
|
@@ -230,18 +233,20 @@ export function createOpenAI(
|
|
|
230
233
|
const providerName = options.name ?? 'openai';
|
|
231
234
|
|
|
232
235
|
const getHeaders = () =>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
236
|
+
resolveWith(options.apiKey, apiKey =>
|
|
237
|
+
withUserAgentSuffix(
|
|
238
|
+
{
|
|
239
|
+
Authorization: `Bearer ${loadApiKey({
|
|
240
|
+
apiKey,
|
|
241
|
+
environmentVariableName: 'OPENAI_API_KEY',
|
|
242
|
+
description: 'OpenAI',
|
|
243
|
+
})}`,
|
|
244
|
+
'OpenAI-Organization': options.organization,
|
|
245
|
+
'OpenAI-Project': options.project,
|
|
246
|
+
...options.headers,
|
|
247
|
+
},
|
|
248
|
+
`ai-sdk-openai/${VERSION}`,
|
|
249
|
+
),
|
|
245
250
|
);
|
|
246
251
|
|
|
247
252
|
const createChatModel = (modelId: OpenAIChatModelId) =>
|
|
@@ -5,13 +5,15 @@ import {
|
|
|
5
5
|
type Experimental_RealtimeModelV4 as RealtimeModelV4,
|
|
6
6
|
} from '@ai-sdk/provider';
|
|
7
7
|
import { OpenAIRealtimeModelLive } from '../live/openai-realtime-model-live';
|
|
8
|
+
import {
|
|
9
|
+
getOpenAIRealtimeModelEntry,
|
|
10
|
+
type OpenAIKnownLiveModelId,
|
|
11
|
+
} from '../openai-model-catalog';
|
|
8
12
|
import {
|
|
9
13
|
OpenAIRealtimeModel,
|
|
10
14
|
type OpenAIRealtimeModelConfig,
|
|
11
15
|
} from './openai-realtime-model';
|
|
12
16
|
|
|
13
|
-
const knownLiveModelIds = ['gpt-live-1'] as const;
|
|
14
|
-
|
|
15
17
|
export type OpenAIRealtimeOptions = {
|
|
16
18
|
/** Overrides model ID routing, including for early-access models. */
|
|
17
19
|
api?: 'live' | 'realtime';
|
|
@@ -21,7 +23,7 @@ export interface OpenAIRealtimeFactory extends RealtimeFactoryV4 {
|
|
|
21
23
|
(modelId: string, options: { api: 'live' }): OpenAIRealtimeModelLive;
|
|
22
24
|
(modelId: string, options: { api: 'realtime' }): OpenAIRealtimeModel;
|
|
23
25
|
(
|
|
24
|
-
modelId:
|
|
26
|
+
modelId: OpenAIKnownLiveModelId,
|
|
25
27
|
options?: { api?: undefined },
|
|
26
28
|
): OpenAIRealtimeModelLive;
|
|
27
29
|
(modelId: string, options?: OpenAIRealtimeOptions): RealtimeModelV4;
|
|
@@ -46,9 +48,7 @@ function resolveRealtimeApi(
|
|
|
46
48
|
return api;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
return
|
|
50
|
-
? 'live'
|
|
51
|
-
: 'realtime';
|
|
51
|
+
return getOpenAIRealtimeModelEntry(modelId).requestConfig.realtime.api;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export function createOpenAIRealtimeFactory(
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Resolvable,
|
|
3
|
+
resolve,
|
|
4
|
+
type FetchFunction,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
1
6
|
import type {
|
|
2
7
|
Experimental_RealtimeModelV4 as RealtimeModelV4,
|
|
3
8
|
Experimental_RealtimeModelV4ClientEvent as RealtimeModelV4ClientEvent,
|
|
@@ -6,7 +11,6 @@ import type {
|
|
|
6
11
|
Experimental_RealtimeModelV4ServerEvent as RealtimeModelV4ServerEvent,
|
|
7
12
|
Experimental_RealtimeModelV4SessionConfig as RealtimeModelV4SessionConfig,
|
|
8
13
|
} from '@ai-sdk/provider';
|
|
9
|
-
import type { FetchFunction } from '@ai-sdk/provider-utils';
|
|
10
14
|
import {
|
|
11
15
|
buildOpenAISessionConfig,
|
|
12
16
|
parseOpenAIRealtimeServerEvent,
|
|
@@ -16,7 +20,7 @@ import {
|
|
|
16
20
|
export type OpenAIRealtimeModelConfig = {
|
|
17
21
|
provider: string;
|
|
18
22
|
baseURL: string;
|
|
19
|
-
headers:
|
|
23
|
+
headers: Resolvable<Record<string, string | undefined>>;
|
|
20
24
|
fetch?: FetchFunction;
|
|
21
25
|
};
|
|
22
26
|
|
|
@@ -47,7 +51,7 @@ export class OpenAIRealtimeModel implements RealtimeModelV4 {
|
|
|
47
51
|
const response = await fetchFn(url, {
|
|
48
52
|
method: 'POST',
|
|
49
53
|
headers: {
|
|
50
|
-
...this.config.headers
|
|
54
|
+
...(await resolve(this.config.headers)),
|
|
51
55
|
'Content-Type': 'application/json',
|
|
52
56
|
},
|
|
53
57
|
body: JSON.stringify({
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4CallOptions,
|
|
3
|
+
SharedV4Warning,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
import { mapReasoningToProviderEffort } from '@ai-sdk/provider-utils';
|
|
6
|
+
|
|
7
|
+
type OpenAIReasoningEffort = Exclude<
|
|
8
|
+
LanguageModelV4CallOptions['reasoning'],
|
|
9
|
+
'provider-default' | undefined
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
const allOpenAIReasoningEfforts: readonly OpenAIReasoningEffort[] = [
|
|
13
|
+
'none',
|
|
14
|
+
'minimal',
|
|
15
|
+
'low',
|
|
16
|
+
'medium',
|
|
17
|
+
'high',
|
|
18
|
+
'xhigh',
|
|
19
|
+
'max',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/** Resolve portable reasoning against the efforts declared by the catalog. */
|
|
23
|
+
export function resolveOpenAIReasoningEffort({
|
|
24
|
+
providerReasoningEffort,
|
|
25
|
+
reasoning,
|
|
26
|
+
supportedReasoningEfforts,
|
|
27
|
+
warnings,
|
|
28
|
+
}: {
|
|
29
|
+
providerReasoningEffort: string | null | undefined;
|
|
30
|
+
reasoning: LanguageModelV4CallOptions['reasoning'];
|
|
31
|
+
supportedReasoningEfforts: readonly string[] | undefined;
|
|
32
|
+
warnings: SharedV4Warning[];
|
|
33
|
+
}): string | undefined {
|
|
34
|
+
return mapReasoningToProviderEffort({
|
|
35
|
+
reasoning,
|
|
36
|
+
providerOptionsEffort: providerReasoningEffort,
|
|
37
|
+
effortMap: Object.fromEntries(
|
|
38
|
+
(supportedReasoningEfforts ?? allOpenAIReasoningEfforts).map(effort => [
|
|
39
|
+
effort,
|
|
40
|
+
effort,
|
|
41
|
+
]),
|
|
42
|
+
),
|
|
43
|
+
warnings,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -17,12 +17,12 @@ import {
|
|
|
17
17
|
type SharedV4Warning,
|
|
18
18
|
} from '@ai-sdk/provider';
|
|
19
19
|
import {
|
|
20
|
+
resolve,
|
|
20
21
|
combineHeaders,
|
|
21
22
|
createEventSourceResponseHandler,
|
|
22
23
|
createJsonResponseHandler,
|
|
23
24
|
createToolNameMapping,
|
|
24
25
|
generateId,
|
|
25
|
-
isCustomReasoning,
|
|
26
26
|
parseProviderOptions,
|
|
27
27
|
postJsonToApi,
|
|
28
28
|
serializeModelOptions,
|
|
@@ -37,11 +37,12 @@ import {
|
|
|
37
37
|
type OpenAIConfig,
|
|
38
38
|
} from '../openai-config';
|
|
39
39
|
import { openaiFailedResponseHandler } from '../openai-error';
|
|
40
|
-
import {
|
|
40
|
+
import { getOpenAILanguageModelEntry } from '../openai-model-catalog';
|
|
41
41
|
import {
|
|
42
42
|
createOpenAIProviderStreamError,
|
|
43
43
|
throwIfOpenAIStreamErrorBeforeOutput,
|
|
44
44
|
} from '../openai-stream-error';
|
|
45
|
+
import { resolveOpenAIReasoningEffort } from '../resolve-openai-reasoning-effort';
|
|
45
46
|
import type { applyPatchInputSchema } from '../tool/apply-patch';
|
|
46
47
|
import type {
|
|
47
48
|
codeInterpreterInputSchema,
|
|
@@ -224,10 +225,10 @@ function getConfigurationUpdateUnsupportedReason({
|
|
|
224
225
|
modelCapabilities,
|
|
225
226
|
options,
|
|
226
227
|
}: {
|
|
227
|
-
modelCapabilities: {
|
|
228
|
+
modelCapabilities: { configurationUpdates: boolean };
|
|
228
229
|
options: OpenAILanguageModelResponsesOptions | undefined;
|
|
229
230
|
}): string | undefined {
|
|
230
|
-
if (!modelCapabilities.
|
|
231
|
+
if (!modelCapabilities.configurationUpdates) {
|
|
231
232
|
return 'reasoningEffortUpdate is only supported by GPT-6 and later models';
|
|
232
233
|
}
|
|
233
234
|
|
|
@@ -302,7 +303,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
302
303
|
options: LanguageModelV4CallOptions;
|
|
303
304
|
}) {
|
|
304
305
|
const warnings: SharedV4Warning[] = [];
|
|
305
|
-
const
|
|
306
|
+
const {
|
|
307
|
+
capabilities: { language: modelCapabilities },
|
|
308
|
+
requestConfig: { language: modelRequestConfig },
|
|
309
|
+
} = getOpenAILanguageModelEntry(modelId);
|
|
306
310
|
|
|
307
311
|
if (topK != null) {
|
|
308
312
|
warnings.push({ type: 'unsupported', feature: 'topK' });
|
|
@@ -341,24 +345,12 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
341
345
|
});
|
|
342
346
|
}
|
|
343
347
|
|
|
344
|
-
|
|
345
|
-
openaiOptions?.reasoningEffort
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
modelCapabilities.supportedReasoningEfforts != null &&
|
|
351
|
-
!modelCapabilities.supportedReasoningEfforts.includes(
|
|
352
|
-
resolvedReasoningEffort,
|
|
353
|
-
)
|
|
354
|
-
) {
|
|
355
|
-
warnings.push({
|
|
356
|
-
type: 'unsupported',
|
|
357
|
-
feature: 'reasoningEffort',
|
|
358
|
-
details: `${modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`,
|
|
359
|
-
});
|
|
360
|
-
resolvedReasoningEffort = undefined;
|
|
361
|
-
}
|
|
348
|
+
const resolvedReasoningEffort = resolveOpenAIReasoningEffort({
|
|
349
|
+
providerReasoningEffort: openaiOptions?.reasoningEffort,
|
|
350
|
+
reasoning,
|
|
351
|
+
supportedReasoningEfforts: modelCapabilities.reasoning.efforts,
|
|
352
|
+
warnings,
|
|
353
|
+
});
|
|
362
354
|
|
|
363
355
|
const resolvedReasoningSummary =
|
|
364
356
|
openaiOptions?.reasoningSummary !== undefined
|
|
@@ -368,7 +360,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
368
360
|
: undefined;
|
|
369
361
|
|
|
370
362
|
const isReasoningModel =
|
|
371
|
-
openaiOptions?.forceReasoning ??
|
|
363
|
+
openaiOptions?.forceReasoning ??
|
|
364
|
+
modelCapabilities.reasoning.support === true;
|
|
372
365
|
|
|
373
366
|
if (openaiOptions?.conversation && openaiOptions?.previousResponseId) {
|
|
374
367
|
warnings.push({
|
|
@@ -412,7 +405,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
412
405
|
toolNameMapping,
|
|
413
406
|
customProviderToolNames,
|
|
414
407
|
outputSchemaToolNames,
|
|
415
|
-
supportsAsyncToolCalling: modelCapabilities.
|
|
408
|
+
supportsAsyncToolCalling: modelCapabilities.tools.async,
|
|
416
409
|
providerOptionsName,
|
|
417
410
|
});
|
|
418
411
|
|
|
@@ -430,7 +423,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
430
423
|
openaiOptions?.systemMessageMode ??
|
|
431
424
|
(isReasoningModel
|
|
432
425
|
? 'developer'
|
|
433
|
-
:
|
|
426
|
+
: modelRequestConfig.systemMessageMode),
|
|
434
427
|
providerOptionsName,
|
|
435
428
|
configurationUpdateUnsupportedReason,
|
|
436
429
|
explicitMessageItemType: config.explicitMessageItemType,
|
|
@@ -458,10 +451,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
458
451
|
|
|
459
452
|
// The schema accepts update efforts supported by any supported model. Check
|
|
460
453
|
// whether this specific model supports the requested effort.
|
|
454
|
+
const supportedReasoningEfforts = modelCapabilities.reasoning.efforts;
|
|
461
455
|
const getUpdateEffortUnsupportedReason = (effort: string | undefined) =>
|
|
462
|
-
effort != null &&
|
|
463
|
-
|
|
464
|
-
? `${modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`
|
|
456
|
+
effort != null && supportedReasoningEfforts?.includes(effort) === false
|
|
457
|
+
? `${modelId} only supports the following reasoning efforts: ${supportedReasoningEfforts.join(', ')}`
|
|
465
458
|
: undefined;
|
|
466
459
|
|
|
467
460
|
// Reject configuration updates whose effort value is unsupported by the selected model.
|
|
@@ -675,7 +668,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
675
668
|
};
|
|
676
669
|
|
|
677
670
|
if (
|
|
678
|
-
modelCapabilities.
|
|
671
|
+
!modelCapabilities.promptCache.retention &&
|
|
679
672
|
baseArgs.prompt_cache_retention != null
|
|
680
673
|
) {
|
|
681
674
|
baseArgs.prompt_cache_retention = undefined;
|
|
@@ -703,7 +696,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
703
696
|
if (
|
|
704
697
|
!(
|
|
705
698
|
effectiveReasoningEffort === 'none' &&
|
|
706
|
-
modelCapabilities.
|
|
699
|
+
modelCapabilities.reasoning.sampling === 'reasoning-effort-none'
|
|
707
700
|
)
|
|
708
701
|
) {
|
|
709
702
|
if (baseArgs.temperature != null) {
|
|
@@ -725,7 +718,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
725
718
|
}
|
|
726
719
|
|
|
727
720
|
if (
|
|
728
|
-
modelCapabilities.
|
|
721
|
+
modelCapabilities.endpoints.responses.logprobsRequireNonReasoning &&
|
|
729
722
|
(baseArgs.top_logprobs != null ||
|
|
730
723
|
baseArgs.include?.includes('message.output_text.logprobs'))
|
|
731
724
|
) {
|
|
@@ -781,7 +774,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
781
774
|
// Validate flex processing support
|
|
782
775
|
if (
|
|
783
776
|
openaiOptions?.serviceTier === 'flex' &&
|
|
784
|
-
!modelCapabilities.
|
|
777
|
+
!modelCapabilities.serviceTiers.flex
|
|
785
778
|
) {
|
|
786
779
|
warnings.push({
|
|
787
780
|
type: 'unsupported',
|
|
@@ -797,7 +790,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
797
790
|
if (
|
|
798
791
|
(openaiOptions?.serviceTier === 'priority' ||
|
|
799
792
|
openaiOptions?.serviceTier === 'fast') &&
|
|
800
|
-
!modelCapabilities.
|
|
793
|
+
!modelCapabilities.serviceTiers.priority
|
|
801
794
|
) {
|
|
802
795
|
warnings.push({
|
|
803
796
|
type: 'unsupported',
|
|
@@ -867,7 +860,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
867
860
|
rawValue: rawResponse,
|
|
868
861
|
} = await postJsonToApi({
|
|
869
862
|
url,
|
|
870
|
-
headers: combineHeaders(
|
|
863
|
+
headers: combineHeaders(
|
|
864
|
+
await resolve(this.config.headers),
|
|
865
|
+
options.headers,
|
|
866
|
+
),
|
|
871
867
|
body,
|
|
872
868
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
873
869
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -1570,7 +1566,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1570
1566
|
|
|
1571
1567
|
const { responseHeaders, value: response } = await postJsonToApi({
|
|
1572
1568
|
url,
|
|
1573
|
-
headers: combineHeaders(
|
|
1569
|
+
headers: combineHeaders(
|
|
1570
|
+
await resolve(this.config.headers),
|
|
1571
|
+
options.headers,
|
|
1572
|
+
),
|
|
1574
1573
|
body: {
|
|
1575
1574
|
...body,
|
|
1576
1575
|
stream: true,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { SkillsV4, SharedV4Warning } from '@ai-sdk/provider';
|
|
2
2
|
import {
|
|
3
|
+
type Resolvable,
|
|
4
|
+
resolve,
|
|
3
5
|
combineHeaders,
|
|
4
6
|
convertInlineFileDataToUint8Array,
|
|
5
7
|
createJsonResponseHandler,
|
|
@@ -12,7 +14,7 @@ import { openaiSkillResponseSchema } from './openai-skills-api';
|
|
|
12
14
|
interface OpenAISkillsConfig {
|
|
13
15
|
provider: string;
|
|
14
16
|
url: (options: { path: string }) => string;
|
|
15
|
-
headers:
|
|
17
|
+
headers: Resolvable<Record<string, string | undefined>>;
|
|
16
18
|
fetch?: FetchFunction;
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -50,7 +52,7 @@ export class OpenAISkills implements SkillsV4 {
|
|
|
50
52
|
|
|
51
53
|
const { value: response } = await postFormDataToApi({
|
|
52
54
|
url: this.config.url({ path: '/skills' }),
|
|
53
|
-
headers: combineHeaders(this.config.headers
|
|
55
|
+
headers: combineHeaders(await resolve(this.config.headers)),
|
|
54
56
|
formData,
|
|
55
57
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
56
58
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SpeechModelV4, SharedV4Warning } from '@ai-sdk/provider';
|
|
2
2
|
import {
|
|
3
|
+
resolve,
|
|
3
4
|
combineHeaders,
|
|
4
5
|
createBinaryResponseHandler,
|
|
5
6
|
parseProviderOptions,
|
|
@@ -131,7 +132,10 @@ export class OpenAISpeechModel implements SpeechModelV4 {
|
|
|
131
132
|
path: '/audio/speech',
|
|
132
133
|
modelId: this.modelId,
|
|
133
134
|
}),
|
|
134
|
-
headers: combineHeaders(
|
|
135
|
+
headers: combineHeaders(
|
|
136
|
+
await resolve(this.config.headers),
|
|
137
|
+
options.headers,
|
|
138
|
+
),
|
|
135
139
|
body: requestBody,
|
|
136
140
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
137
141
|
successfulResponseHandler: createBinaryResponseHandler(),
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type SharedV4Warning,
|
|
7
7
|
} from '@ai-sdk/provider';
|
|
8
8
|
import {
|
|
9
|
+
resolve,
|
|
9
10
|
combineHeaders,
|
|
10
11
|
connectToWebSocket,
|
|
11
12
|
convertToBase64,
|
|
@@ -101,7 +102,10 @@ export class OpenAISpeechTranslationModel implements SpeechTranslationModelV4 {
|
|
|
101
102
|
});
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
const headers = combineHeaders(
|
|
105
|
+
const headers = combineHeaders(
|
|
106
|
+
await resolve(this.config.headers),
|
|
107
|
+
options.headers,
|
|
108
|
+
);
|
|
105
109
|
const sessionUpdate = buildOpenAIRealtimeSpeechTranslationSession({
|
|
106
110
|
targetLanguage: options.targetLanguage,
|
|
107
111
|
});
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type TranscriptionModelV4CallOptions,
|
|
7
7
|
} from '@ai-sdk/provider';
|
|
8
8
|
import {
|
|
9
|
+
resolve,
|
|
9
10
|
combineHeaders,
|
|
10
11
|
convertBase64ToUint8Array,
|
|
11
12
|
convertToBase64,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
} from '@ai-sdk/provider-utils';
|
|
26
27
|
import type { OpenAIConfig } from '../openai-config';
|
|
27
28
|
import { openaiFailedResponseHandler } from '../openai-error';
|
|
29
|
+
import { getOpenAITranscriptionModelEntry } from '../openai-model-catalog';
|
|
28
30
|
import { openaiTranscriptionResponseSchema } from './openai-transcription-api';
|
|
29
31
|
import {
|
|
30
32
|
openAITranscriptionModelOptions,
|
|
@@ -57,47 +59,6 @@ type OpenAIRealtimeTranscriptionEvent = {
|
|
|
57
59
|
error?: { message?: string };
|
|
58
60
|
};
|
|
59
61
|
|
|
60
|
-
/**
|
|
61
|
-
* Whether `modelId` is `base` or one of its dated snapshots
|
|
62
|
-
* (e.g. `gpt-realtime-whisper-2026-01-01`).
|
|
63
|
-
*/
|
|
64
|
-
function isModelOrSnapshot(modelId: string, base: string): boolean {
|
|
65
|
-
return modelId === base || modelId.startsWith(`${base}-`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Model IDs that stream over the realtime transcription WebSocket and do not
|
|
70
|
-
* support the REST transcription endpoint.
|
|
71
|
-
*/
|
|
72
|
-
function isRealtimeTranscriptionModelId(modelId: string): boolean {
|
|
73
|
-
return (
|
|
74
|
-
isModelOrSnapshot(modelId, 'gpt-realtime-whisper') ||
|
|
75
|
-
isModelOrSnapshot(modelId, 'gpt-live-transcribe')
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Model IDs that support streaming transcription. `gpt-transcribe` also
|
|
81
|
-
* supports the REST transcription endpoint.
|
|
82
|
-
*/
|
|
83
|
-
function supportsStreamingTranscription(modelId: string): boolean {
|
|
84
|
-
return (
|
|
85
|
-
isRealtimeTranscriptionModelId(modelId) ||
|
|
86
|
-
isModelOrSnapshot(modelId, 'gpt-transcribe')
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Models whose realtime transcription session takes a `languages` list
|
|
92
|
-
* instead of the singular `language` field.
|
|
93
|
-
*/
|
|
94
|
-
function usesLanguagesList(modelId: string): boolean {
|
|
95
|
-
return (
|
|
96
|
-
isModelOrSnapshot(modelId, 'gpt-live-transcribe') ||
|
|
97
|
-
isModelOrSnapshot(modelId, 'gpt-transcribe')
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
62
|
interface OpenAITranscriptionModelConfig extends OpenAIConfig {
|
|
102
63
|
_internal?: {
|
|
103
64
|
currentDate?: () => Date;
|
|
@@ -226,15 +187,12 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
226
187
|
`audio.${fileExtension}`,
|
|
227
188
|
);
|
|
228
189
|
|
|
229
|
-
const
|
|
190
|
+
const { transcription: requestConfig } = getOpenAITranscriptionModelEntry(
|
|
191
|
+
this.modelId,
|
|
192
|
+
).requestConfig;
|
|
230
193
|
|
|
231
194
|
const responseFormat =
|
|
232
|
-
openAIOptions?.responseFormat ??
|
|
233
|
-
(isDiarizationModel
|
|
234
|
-
? 'diarized_json'
|
|
235
|
-
: this.modelId === 'whisper-1'
|
|
236
|
-
? 'verbose_json'
|
|
237
|
-
: undefined);
|
|
195
|
+
openAIOptions?.responseFormat ?? requestConfig.responseFormat;
|
|
238
196
|
|
|
239
197
|
// Timestamps are only returned with verbose_json, so only default to
|
|
240
198
|
// segments when that format is used.
|
|
@@ -243,8 +201,7 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
243
201
|
(responseFormat === 'verbose_json' ? ['segment'] : undefined);
|
|
244
202
|
|
|
245
203
|
const chunkingStrategy =
|
|
246
|
-
openAIOptions?.chunkingStrategy ??
|
|
247
|
-
(isDiarizationModel ? 'auto' : undefined);
|
|
204
|
+
openAIOptions?.chunkingStrategy ?? requestConfig.chunkingStrategy;
|
|
248
205
|
|
|
249
206
|
const transcriptionModelOptions = {
|
|
250
207
|
response_format: responseFormat,
|
|
@@ -296,7 +253,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
296
253
|
async doGenerate(
|
|
297
254
|
options: OpenAITranscriptionCallOptions,
|
|
298
255
|
): Promise<Awaited<ReturnType<TranscriptionModelV4['doGenerate']>>> {
|
|
299
|
-
if (
|
|
256
|
+
if (
|
|
257
|
+
getOpenAITranscriptionModelEntry(this.modelId).capabilities.transcription
|
|
258
|
+
.transcription === false
|
|
259
|
+
) {
|
|
300
260
|
throw new UnsupportedFunctionalityError({
|
|
301
261
|
functionality: `non-streaming transcription with ${this.modelId}`,
|
|
302
262
|
});
|
|
@@ -314,7 +274,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
314
274
|
path: '/audio/transcriptions',
|
|
315
275
|
modelId: this.modelId,
|
|
316
276
|
}),
|
|
317
|
-
headers: combineHeaders(
|
|
277
|
+
headers: combineHeaders(
|
|
278
|
+
await resolve(this.config.headers),
|
|
279
|
+
options.headers,
|
|
280
|
+
),
|
|
318
281
|
formData,
|
|
319
282
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
320
283
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -401,7 +364,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
401
364
|
): Promise<
|
|
402
365
|
Awaited<ReturnType<NonNullable<TranscriptionModelV4['doStream']>>>
|
|
403
366
|
> {
|
|
404
|
-
|
|
367
|
+
const entry = getOpenAITranscriptionModelEntry(this.modelId);
|
|
368
|
+
if (entry.capabilities.transcription.streaming !== true) {
|
|
405
369
|
throw new UnsupportedFunctionalityError({
|
|
406
370
|
functionality: `streaming transcription with ${this.modelId}`,
|
|
407
371
|
});
|
|
@@ -434,7 +398,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
434
398
|
}
|
|
435
399
|
|
|
436
400
|
// gpt-transcribe transcribes each committed turn, so it has no delay setting:
|
|
437
|
-
const supportsDelay =
|
|
401
|
+
const supportsDelay =
|
|
402
|
+
entry.capabilities.transcription.streamingDelay === true;
|
|
438
403
|
if (!supportsDelay && openAIOptions?.streaming?.delay != null) {
|
|
439
404
|
warnings.push({
|
|
440
405
|
type: 'unsupported',
|
|
@@ -443,7 +408,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
443
408
|
});
|
|
444
409
|
}
|
|
445
410
|
|
|
446
|
-
const headers = combineHeaders(
|
|
411
|
+
const headers = combineHeaders(
|
|
412
|
+
await resolve(this.config.headers),
|
|
413
|
+
options.headers,
|
|
414
|
+
);
|
|
447
415
|
const sessionUpdate = buildOpenAIRealtimeTranscriptionSession({
|
|
448
416
|
modelId: this.modelId,
|
|
449
417
|
inputAudioFormat: options.inputAudioFormat,
|
|
@@ -659,7 +627,8 @@ function buildOpenAIRealtimeTranscriptionSession({
|
|
|
659
627
|
transcription: {
|
|
660
628
|
model: modelId,
|
|
661
629
|
...(providerOptions?.language != null
|
|
662
|
-
?
|
|
630
|
+
? getOpenAITranscriptionModelEntry(modelId).requestConfig
|
|
631
|
+
.transcription.languageField === 'languages'
|
|
663
632
|
? { languages: [providerOptions.language] }
|
|
664
633
|
: { language: providerOptions.language }
|
|
665
634
|
: {}),
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
export type OpenAILanguageModelCapabilities = {
|
|
2
|
-
isReasoningModel: boolean;
|
|
3
|
-
systemMessageMode: 'remove' | 'system' | 'developer';
|
|
4
|
-
supportsFlexProcessing: boolean;
|
|
5
|
-
supportsPriorityProcessing: boolean;
|
|
6
|
-
supportsConfigurationUpdate: boolean;
|
|
7
|
-
supportsAsyncToolCalling: boolean;
|
|
8
|
-
supportedReasoningEfforts: readonly string[] | undefined;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Allow temperature, topP, logProbs when reasoningEffort is none.
|
|
12
|
-
*/
|
|
13
|
-
supportsNonReasoningParameters: boolean;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export function getOpenAILanguageModelCapabilities(
|
|
17
|
-
modelId: string,
|
|
18
|
-
): OpenAILanguageModelCapabilities {
|
|
19
|
-
const oSeriesVersion = getOSeriesVersion(modelId);
|
|
20
|
-
const gptVersion = getGptVersion(modelId);
|
|
21
|
-
const isGptChatModel =
|
|
22
|
-
gptVersion?.minor == null &&
|
|
23
|
-
(gptVersion?.variant?.startsWith('chat') ?? false);
|
|
24
|
-
const isGptNanoModel = gptVersion?.variant?.startsWith('nano') ?? false;
|
|
25
|
-
const isGpt6OrLaterModel = gptVersion != null && gptVersion.major >= 6;
|
|
26
|
-
|
|
27
|
-
const isGpt6OrLaterModelWithNoneEffort =
|
|
28
|
-
modelId === 'gpt-6-sol' ||
|
|
29
|
-
modelId === 'gpt-6-luna' ||
|
|
30
|
-
modelId === 'gpt-6.1-terra';
|
|
31
|
-
|
|
32
|
-
const supportsFlexProcessing =
|
|
33
|
-
(oSeriesVersion != null && oSeriesVersion >= 3) ||
|
|
34
|
-
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
35
|
-
|
|
36
|
-
const supportsPriorityProcessing =
|
|
37
|
-
modelId.startsWith('gpt-4') ||
|
|
38
|
-
(gptVersion != null &&
|
|
39
|
-
gptVersion.major >= 5 &&
|
|
40
|
-
!isGptNanoModel &&
|
|
41
|
-
!isGptChatModel) ||
|
|
42
|
-
(oSeriesVersion != null && oSeriesVersion >= 3);
|
|
43
|
-
|
|
44
|
-
// Only recognizable OpenAI model families should use the developer role.
|
|
45
|
-
// Fine-tuned, third-party, and custom model IDs keep conservative defaults.
|
|
46
|
-
const isReasoningModel =
|
|
47
|
-
oSeriesVersion != null ||
|
|
48
|
-
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
49
|
-
|
|
50
|
-
// https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
|
|
51
|
-
// GPT-5.1 through GPT-5.x, GPT-6 Sol/Luna, and GPT-6.1 Terra support
|
|
52
|
-
// temperature, topP, and logProbs when reasoningEffort is none.
|
|
53
|
-
const supportsNonReasoningParameters =
|
|
54
|
-
isGpt6OrLaterModelWithNoneEffort ||
|
|
55
|
-
(!isGpt6OrLaterModel &&
|
|
56
|
-
gptVersion != null &&
|
|
57
|
-
(gptVersion.major > 5 ||
|
|
58
|
-
(gptVersion.major === 5 && (gptVersion.minor ?? 0) >= 1)));
|
|
59
|
-
|
|
60
|
-
const systemMessageMode = isReasoningModel ? 'developer' : 'system';
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
supportsFlexProcessing,
|
|
64
|
-
supportsPriorityProcessing,
|
|
65
|
-
supportsConfigurationUpdate: isGpt6OrLaterModel,
|
|
66
|
-
supportsAsyncToolCalling: isGpt6OrLaterModel,
|
|
67
|
-
supportedReasoningEfforts: isGpt6OrLaterModelWithNoneEffort
|
|
68
|
-
? ['none', 'low', 'medium', 'high', 'xhigh', 'max']
|
|
69
|
-
: isGpt6OrLaterModel
|
|
70
|
-
? ['low', 'medium', 'high', 'xhigh', 'max']
|
|
71
|
-
: undefined,
|
|
72
|
-
isReasoningModel,
|
|
73
|
-
systemMessageMode,
|
|
74
|
-
supportsNonReasoningParameters,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function getOSeriesVersion(modelId: string): number | undefined {
|
|
79
|
-
const match = /^o(\d+)(?:-|$)/.exec(modelId);
|
|
80
|
-
return match == null ? undefined : Number(match[1]);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function getGptVersion(modelId: string):
|
|
84
|
-
| {
|
|
85
|
-
major: number;
|
|
86
|
-
minor: number | undefined;
|
|
87
|
-
variant: string | undefined;
|
|
88
|
-
}
|
|
89
|
-
| undefined {
|
|
90
|
-
const match = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/.exec(modelId);
|
|
91
|
-
|
|
92
|
-
if (match == null) {
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
major: Number(match[1]),
|
|
98
|
-
minor: match[2] == null ? undefined : Number(match[2]),
|
|
99
|
-
variant: match[3],
|
|
100
|
-
};
|
|
101
|
-
}
|