@ai-sdk/openai 4.0.103 → 4.0.105
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 +39 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +181 -75
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +14 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +148 -66
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +20 -10
- package/package.json +2 -2
- package/src/chat/openai-chat-language-model-options.ts +1 -0
- package/src/chat/openai-chat-language-model.ts +30 -15
- package/src/normalize-openai-json-schema.ts +2 -1
- package/src/openai-batch.ts +2 -4
- package/src/openai-decision-model-options.ts +1 -0
- package/src/openai-decision-model.ts +46 -5
- package/src/openai-language-model-capabilities.ts +105 -7
- package/src/responses/openai-responses-language-model-options.ts +2 -0
- package/src/responses/openai-responses-language-model.ts +34 -32
- package/src/transcription/openai-transcription-model-options.ts +5 -6
- package/src/transcription/openai-transcription-model.ts +89 -48
package/docs/03-openai.mdx
CHANGED
|
@@ -245,9 +245,9 @@ The following provider options are available:
|
|
|
245
245
|
Reasoning effort for reasoning models. Defaults to `medium`. If you use `providerOptions` to set the `reasoningEffort` option, this model setting will be ignored.
|
|
246
246
|
|
|
247
247
|
<Note>
|
|
248
|
-
Supported reasoning efforts vary by model. GPT-5.6, GPT-6 Sol,
|
|
249
|
-
support `'none'`, `'low'`, `'medium'`, `'high'`, `'xhigh'`, and
|
|
250
|
-
Astra and GPT-6.1 Sol do not support `'none'`.
|
|
248
|
+
Supported reasoning efforts vary by model. GPT-5.6, GPT-6 Sol, GPT-6 Luna, and
|
|
249
|
+
GPT-6.1 Terra support `'none'`, `'low'`, `'medium'`, `'high'`, `'xhigh'`, and
|
|
250
|
+
`'max'`. GPT-6 Astra and GPT-6.1 Sol do not support `'none'`.
|
|
251
251
|
</Note>
|
|
252
252
|
|
|
253
253
|
<Note type="warning">
|
|
@@ -3069,6 +3069,7 @@ The following optional provider options are available for OpenAI completion mode
|
|
|
3069
3069
|
| --------------------- | ----------- | ----------- | ----------------- | ---------- |
|
|
3070
3070
|
| `gpt-6-astra` | <Check /> | <Cross /> | <Check /> | <Check /> |
|
|
3071
3071
|
| `gpt-6.1-sol` | <Check /> | <Cross /> | <Check /> | <Check /> |
|
|
3072
|
+
| `gpt-6.1-terra` | <Check /> | <Cross /> | <Check /> | <Check /> |
|
|
3072
3073
|
| `gpt-6-luna` | <Check /> | <Cross /> | <Check /> | <Check /> |
|
|
3073
3074
|
| `gpt-6-sol` | <Check /> | <Cross /> | <Check /> | <Check /> |
|
|
3074
3075
|
| `gpt-5.6` | <Check /> | <Cross /> | <Check /> | <Check /> |
|
|
@@ -3508,12 +3509,13 @@ The following provider options are available:
|
|
|
3508
3509
|
|
|
3509
3510
|
- **timestampGranularities** _string[]_
|
|
3510
3511
|
The granularity of the timestamps in the transcription.
|
|
3511
|
-
Defaults to `['segment']`.
|
|
3512
|
+
Defaults to `['segment']` when `responseFormat` is `verbose_json`.
|
|
3512
3513
|
Possible values are `['word']`, `['segment']`, and `['word', 'segment']`.
|
|
3513
3514
|
Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.
|
|
3514
3515
|
|
|
3515
3516
|
- **language** _string_
|
|
3516
3517
|
The language of the input audio. Supplying the input language in ISO-639-1 format (e.g. 'en') will improve accuracy and latency.
|
|
3518
|
+
For streaming with `gpt-transcribe` and `gpt-live-transcribe`, it is sent as a single-entry `languages` list.
|
|
3517
3519
|
Optional.
|
|
3518
3520
|
|
|
3519
3521
|
- **prompt** _string_
|
|
@@ -3529,13 +3531,13 @@ The following provider options are available:
|
|
|
3529
3531
|
Additional information to include in the transcription response.
|
|
3530
3532
|
|
|
3531
3533
|
- **responseFormat** _'json' | 'verbose_json' | 'diarized_json'_
|
|
3532
|
-
The format of the transcription response. `gpt-4o-transcribe-diarize` defaults to `diarized_json`.
|
|
3534
|
+
The format of the transcription response. `whisper-1` defaults to `verbose_json` and `gpt-4o-transcribe-diarize` defaults to `diarized_json`. Other models use the service default unless this option is set.
|
|
3533
3535
|
|
|
3534
3536
|
- **chunkingStrategy** _'auto' | object_
|
|
3535
3537
|
Controls how the audio is split into chunks. `gpt-4o-transcribe-diarize` defaults to `'auto'`; provide this option to override the default. The object form configures OpenAI server-side VAD with `type: 'server_vad'` and optional `threshold`, `prefixPaddingMs`, and `silenceDurationMs` fields.
|
|
3536
3538
|
|
|
3537
3539
|
- **streaming** _object_
|
|
3538
|
-
Options for streaming transcription models such as `gpt-
|
|
3540
|
+
Options for streaming transcription models such as `gpt-live-transcribe`.
|
|
3539
3541
|
Use with `experimental_streamTranscribe`.
|
|
3540
3542
|
- **delay** _'minimal' | 'low' | 'medium' | 'high' | 'xhigh'_
|
|
3541
3543
|
Latency/accuracy tradeoff for realtime transcription.
|
|
@@ -3551,6 +3553,8 @@ The following provider options are available:
|
|
|
3551
3553
|
| `gpt-4o-mini-transcribe` | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
3552
3554
|
| `gpt-4o-transcribe` | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
3553
3555
|
| `gpt-4o-transcribe-diarize` | <Check /> | <Cross /> | <Check /> | <Check /> | <Cross /> |
|
|
3556
|
+
| `gpt-transcribe` | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> |
|
|
3557
|
+
| `gpt-live-transcribe` | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
|
|
3554
3558
|
| `gpt-realtime-whisper` | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
|
|
3555
3559
|
|
|
3556
3560
|
## Translation Models
|
|
@@ -3691,8 +3695,10 @@ include probability distributions. Boolean questions use the API's predicate
|
|
|
3691
3695
|
primitive and return P(true). Apply thresholds in application code, for example
|
|
3692
3696
|
`answers.requestsRefund.probability >= 0.5`.
|
|
3693
3697
|
|
|
3694
|
-
|
|
3695
|
-
|
|
3698
|
+
JSON state parts that hold an object or an array of objects are sent as native
|
|
3699
|
+
`input_json` parts when they fit its limits (32 container levels, 10,000 values, and
|
|
3700
|
+
256 KiB of compact UTF-8 JSON). Larger JSON state, other JSON state values, structured
|
|
3701
|
+
instructions, and criterion descriptions are serialized as JSON text. Score levels receive zero-based index labels. Non-null Boolean criteria are
|
|
3696
3702
|
appended to predicate instructions under `Criteria for true:` and `Criteria for false:`.
|
|
3697
3703
|
String criteria are passed through; structured criteria are serialized as JSON text.
|
|
3698
3704
|
|
|
@@ -3714,11 +3720,15 @@ state: [
|
|
|
3714
3720
|
|
|
3715
3721
|
The factory respects `createOpenAI` settings, including custom fetch and base URL.
|
|
3716
3722
|
Set `providerOptions.openai.safetyIdentifier` to an opaque end-user identifier for
|
|
3717
|
-
safety monitoring (up to 128 characters).
|
|
3723
|
+
safety monitoring (up to 128 characters). Set `providerOptions.openai.promptCacheKey`
|
|
3724
|
+
to partition prompt cache reuse across requests that share `state`. For example:
|
|
3718
3725
|
|
|
3719
3726
|
```ts
|
|
3720
3727
|
providerOptions: {
|
|
3721
|
-
openai: {
|
|
3728
|
+
openai: {
|
|
3729
|
+
safetyIdentifier: 'user-123',
|
|
3730
|
+
promptCacheKey: 'tenant-42',
|
|
3731
|
+
} satisfies OpenAIDecisionModelOptions,
|
|
3722
3732
|
}
|
|
3723
3733
|
```
|
|
3724
3734
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.105",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@ai-sdk/provider": "4.0.34",
|
|
39
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.71"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@ai-sdk/test-server": "2.0.4",
|
|
@@ -26,7 +26,10 @@ import {
|
|
|
26
26
|
type ParseResult,
|
|
27
27
|
} from '@ai-sdk/provider-utils';
|
|
28
28
|
import { openaiFailedResponseHandler } from '../openai-error';
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
getOpenAILanguageModelCapabilities,
|
|
31
|
+
resolveOpenAIReasoningEffort,
|
|
32
|
+
} from '../openai-language-model-capabilities';
|
|
30
33
|
import {
|
|
31
34
|
createOpenAIProviderStreamError,
|
|
32
35
|
throwIfOpenAIStreamErrorBeforeOutput,
|
|
@@ -121,26 +124,38 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
121
124
|
|
|
122
125
|
const modelCapabilities = getOpenAILanguageModelCapabilities(this.modelId);
|
|
123
126
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
// Some models reject function tools unless reasoning is disabled, even
|
|
128
|
+
// when no reasoning effort is sent, so reasoning is set to 'none'.
|
|
129
|
+
const requiresNoReasoningEffort =
|
|
130
|
+
!modelCapabilities.supportsChatFunctionToolsWithReasoning &&
|
|
131
|
+
openaiOptions.reasoningEffort == null &&
|
|
132
|
+
(tools?.some(tool => tool.type === 'function') ?? false);
|
|
128
133
|
|
|
129
134
|
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
resolvedReasoningEffort,
|
|
134
|
-
)
|
|
135
|
+
requiresNoReasoningEffort &&
|
|
136
|
+
isCustomReasoning(reasoning) &&
|
|
137
|
+
reasoning !== 'none'
|
|
135
138
|
) {
|
|
136
139
|
warnings.push({
|
|
137
|
-
type: '
|
|
138
|
-
feature: '
|
|
139
|
-
details: `${this.modelId}
|
|
140
|
+
type: 'compatibility',
|
|
141
|
+
feature: 'reasoning',
|
|
142
|
+
details: `${this.modelId} does not support function tools with reasoning in the Chat Completions API. reasoning effort set to "none". Use the Responses API to combine reasoning with function tools.`,
|
|
140
143
|
});
|
|
141
|
-
resolvedReasoningEffort = undefined;
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
const resolvedReasoningEffort = requiresNoReasoningEffort
|
|
147
|
+
? 'none'
|
|
148
|
+
: resolveOpenAIReasoningEffort({
|
|
149
|
+
providerReasoningEffort: openaiOptions.reasoningEffort,
|
|
150
|
+
reasoning,
|
|
151
|
+
// Only the Responses API supports `max`.
|
|
152
|
+
supportedReasoningEfforts:
|
|
153
|
+
modelCapabilities.supportedReasoningEfforts?.filter(
|
|
154
|
+
effort => effort !== 'max',
|
|
155
|
+
),
|
|
156
|
+
warnings,
|
|
157
|
+
});
|
|
158
|
+
|
|
144
159
|
const isReasoningModel =
|
|
145
160
|
openaiOptions.forceReasoning ?? modelCapabilities.isReasoningModel;
|
|
146
161
|
|
|
@@ -246,7 +261,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
246
261
|
};
|
|
247
262
|
|
|
248
263
|
if (
|
|
249
|
-
modelCapabilities.
|
|
264
|
+
modelCapabilities.supportsConfigurationUpdate &&
|
|
250
265
|
baseArgs.prompt_cache_retention != null
|
|
251
266
|
) {
|
|
252
267
|
baseArgs.prompt_cache_retention = undefined;
|
|
@@ -58,9 +58,10 @@ export function normalizeOpenAIJsonSchema(schema: JSONSchema7): {
|
|
|
58
58
|
const propertyNames = schema.propertyNames;
|
|
59
59
|
|
|
60
60
|
if (propertyNames != null) {
|
|
61
|
+
// Property names are always strings in JSON, so omitted type is a string schema.
|
|
61
62
|
if (
|
|
62
63
|
typeof propertyNames === 'boolean' ||
|
|
63
|
-
propertyNames.type !== 'string'
|
|
64
|
+
(propertyNames.type != null && propertyNames.type !== 'string')
|
|
64
65
|
) {
|
|
65
66
|
throw new UnsupportedFunctionalityError({
|
|
66
67
|
functionality:
|
package/src/openai-batch.ts
CHANGED
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
type OpenAIResponsesLogprobs,
|
|
48
48
|
} from './responses/openai-responses-api';
|
|
49
49
|
import {
|
|
50
|
-
|
|
50
|
+
mapWebSearchResult,
|
|
51
51
|
OpenAIResponsesLanguageModel,
|
|
52
52
|
openaiResponsesSupportedUrls,
|
|
53
53
|
} from './responses/openai-responses-language-model';
|
|
@@ -812,9 +812,7 @@ async function convertOpenAIBatchResult(
|
|
|
812
812
|
type: 'tool-result',
|
|
813
813
|
toolCallId: part.id,
|
|
814
814
|
toolName: 'web_search',
|
|
815
|
-
...(part
|
|
816
|
-
? { isError: true, result: { status: part.status } }
|
|
817
|
-
: { result: mapWebSearchOutput(part.action) }),
|
|
815
|
+
...mapWebSearchResult(part),
|
|
818
816
|
dynamic: true,
|
|
819
817
|
});
|
|
820
818
|
break;
|
|
@@ -10,6 +10,7 @@ export const openaiDecisionModelOptions = lazySchema(() =>
|
|
|
10
10
|
z.object({
|
|
11
11
|
/** An opaque end-user identifier for safety monitoring, up to 128 characters. */
|
|
12
12
|
safetyIdentifier: z.string().max(128).optional(),
|
|
13
|
+
promptCacheKey: z.string().optional(),
|
|
13
14
|
}),
|
|
14
15
|
),
|
|
15
16
|
);
|
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
type Experimental_DecisionModelV4 as DecisionModelV4,
|
|
5
5
|
type Experimental_DecisionModelV4Answer as DecisionModelV4Answer,
|
|
6
6
|
type Experimental_DecisionModelV4Input as DecisionModelV4Input,
|
|
7
|
+
type JSONObject,
|
|
8
|
+
type JSONValue,
|
|
7
9
|
} from '@ai-sdk/provider';
|
|
8
10
|
import {
|
|
9
11
|
lazySchema,
|
|
@@ -148,6 +150,7 @@ export class DecisionOpenAIModel implements DecisionModelV4 {
|
|
|
148
150
|
body: {
|
|
149
151
|
model: this.modelId,
|
|
150
152
|
safety_identifier: openaiOptions?.safetyIdentifier,
|
|
153
|
+
prompt_cache_key: openaiOptions?.promptCacheKey,
|
|
151
154
|
input: [
|
|
152
155
|
{
|
|
153
156
|
role: 'user',
|
|
@@ -155,10 +158,9 @@ export class DecisionOpenAIModel implements DecisionModelV4 {
|
|
|
155
158
|
if (part.type === 'text')
|
|
156
159
|
return { type: 'input_text', text: part.text };
|
|
157
160
|
if (part.type === 'json')
|
|
158
|
-
return
|
|
159
|
-
type: '
|
|
160
|
-
text: JSON.stringify(part.value)
|
|
161
|
-
};
|
|
161
|
+
return isInputJsonData(part.value)
|
|
162
|
+
? { type: 'input_json', data: part.value }
|
|
163
|
+
: { type: 'input_text', text: JSON.stringify(part.value) };
|
|
162
164
|
if (part.data.type !== 'data') {
|
|
163
165
|
throw new UnsupportedFunctionalityError({
|
|
164
166
|
functionality: `OpenAI decision file input: ${part.mediaType} (${part.data.type})`,
|
|
@@ -323,7 +325,7 @@ export class DecisionOpenAIModel implements DecisionModelV4 {
|
|
|
323
325
|
? [{ type: 'unsupported' as const, feature: 'reasoning' }]
|
|
324
326
|
: []),
|
|
325
327
|
...Object.keys(providerOptions?.openai ?? {})
|
|
326
|
-
.filter(option => option
|
|
328
|
+
.filter(option => !SUPPORTED_PROVIDER_OPTIONS.has(option))
|
|
327
329
|
.map(option => ({
|
|
328
330
|
type: 'unsupported' as const,
|
|
329
331
|
feature: `providerOptions.openai.${option}`,
|
|
@@ -351,6 +353,45 @@ export class DecisionOpenAIModel implements DecisionModelV4 {
|
|
|
351
353
|
}
|
|
352
354
|
}
|
|
353
355
|
|
|
356
|
+
const SUPPORTED_PROVIDER_OPTIONS = new Set([
|
|
357
|
+
'safetyIdentifier',
|
|
358
|
+
'promptCacheKey',
|
|
359
|
+
]);
|
|
360
|
+
|
|
354
361
|
function toText(input: DecisionModelV4Input): string {
|
|
355
362
|
return typeof input === 'string' ? input : JSON.stringify(input);
|
|
356
363
|
}
|
|
364
|
+
|
|
365
|
+
const INPUT_JSON_MAX_DEPTH = 32;
|
|
366
|
+
const INPUT_JSON_MAX_VALUES = 10_000;
|
|
367
|
+
const INPUT_JSON_MAX_BYTES = 256 * 1024;
|
|
368
|
+
|
|
369
|
+
function isInputJsonData(value: JSONValue): value is JSONObject | JSONObject[] {
|
|
370
|
+
const hasInputJsonShape = Array.isArray(value)
|
|
371
|
+
? value.length > 0 && value.every(isJSONObject)
|
|
372
|
+
: isJSONObject(value);
|
|
373
|
+
return hasInputJsonShape && fitsInputJsonLimits(value);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function fitsInputJsonLimits(value: JSONValue): boolean {
|
|
377
|
+
let count = 0;
|
|
378
|
+
const visit = (node: JSONValue | undefined, depth: number): boolean => {
|
|
379
|
+
count += 1;
|
|
380
|
+
if (count > INPUT_JSON_MAX_VALUES) return false;
|
|
381
|
+
if (node === null || typeof node !== 'object') return true;
|
|
382
|
+
if (depth > INPUT_JSON_MAX_DEPTH) return false;
|
|
383
|
+
const children: readonly (JSONValue | undefined)[] = Array.isArray(node)
|
|
384
|
+
? node
|
|
385
|
+
: Object.values(node);
|
|
386
|
+
return children.every(child => visit(child, depth + 1));
|
|
387
|
+
};
|
|
388
|
+
return (
|
|
389
|
+
visit(value, 1) &&
|
|
390
|
+
new TextEncoder().encode(JSON.stringify(value)).length <=
|
|
391
|
+
INPUT_JSON_MAX_BYTES
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function isJSONObject(value: JSONValue): value is JSONObject {
|
|
396
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
397
|
+
}
|
|
@@ -1,3 +1,25 @@
|
|
|
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
|
+
// Unknown model families send the reasoning value directly.
|
|
13
|
+
const ALL_OPENAI_REASONING_EFFORTS: readonly OpenAIReasoningEffort[] = [
|
|
14
|
+
'none',
|
|
15
|
+
'minimal',
|
|
16
|
+
'low',
|
|
17
|
+
'medium',
|
|
18
|
+
'high',
|
|
19
|
+
'xhigh',
|
|
20
|
+
'max',
|
|
21
|
+
];
|
|
22
|
+
|
|
1
23
|
export type OpenAILanguageModelCapabilities = {
|
|
2
24
|
isReasoningModel: boolean;
|
|
3
25
|
systemMessageMode: 'remove' | 'system' | 'developer';
|
|
@@ -5,7 +27,17 @@ export type OpenAILanguageModelCapabilities = {
|
|
|
5
27
|
supportsPriorityProcessing: boolean;
|
|
6
28
|
supportsConfigurationUpdate: boolean;
|
|
7
29
|
supportsAsyncToolCalling: boolean;
|
|
8
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Reasoning efforts supported by the model family, or `undefined` when they
|
|
32
|
+
* are not known.
|
|
33
|
+
*/
|
|
34
|
+
supportedReasoningEfforts: readonly OpenAIReasoningEffort[] | undefined;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether function tools can be combined with a reasoning effort other than
|
|
38
|
+
* `none` in the Chat Completions API.
|
|
39
|
+
*/
|
|
40
|
+
supportsChatFunctionToolsWithReasoning: boolean;
|
|
9
41
|
|
|
10
42
|
/**
|
|
11
43
|
* Allow temperature, topP, logProbs when reasoningEffort is none.
|
|
@@ -24,7 +56,19 @@ export function getOpenAILanguageModelCapabilities(
|
|
|
24
56
|
const isGptNanoModel = gptVersion?.variant?.startsWith('nano') ?? false;
|
|
25
57
|
const isGpt6OrLaterModel = gptVersion != null && gptVersion.major >= 6;
|
|
26
58
|
|
|
27
|
-
|
|
59
|
+
// Pro, codex, and chat variants are excluded because their supported
|
|
60
|
+
// efforts are not known.
|
|
61
|
+
const isStandardGpt5Model =
|
|
62
|
+
gptVersion?.major === 5 &&
|
|
63
|
+
(gptVersion.variant == null ||
|
|
64
|
+
/^(?:(?:mini|nano|sol|luna|terra)(?:-\d{4}-\d{2}-\d{2})?|\d{4}-\d{2}-\d{2})$/.test(
|
|
65
|
+
gptVersion.variant,
|
|
66
|
+
));
|
|
67
|
+
|
|
68
|
+
const isGpt6OrLaterModelWithNoneEffort =
|
|
69
|
+
modelId === 'gpt-6-sol' ||
|
|
70
|
+
modelId === 'gpt-6-luna' ||
|
|
71
|
+
modelId === 'gpt-6.1-terra';
|
|
28
72
|
|
|
29
73
|
const supportsFlexProcessing =
|
|
30
74
|
(oSeriesVersion != null && oSeriesVersion >= 3) ||
|
|
@@ -45,10 +89,10 @@ export function getOpenAILanguageModelCapabilities(
|
|
|
45
89
|
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
46
90
|
|
|
47
91
|
// https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
|
|
48
|
-
// GPT-5.1 through GPT-5.x
|
|
49
|
-
// topP, and logProbs when reasoningEffort is none.
|
|
92
|
+
// GPT-5.1 through GPT-5.x, GPT-6 Sol/Luna, and GPT-6.1 Terra support
|
|
93
|
+
// temperature, topP, and logProbs when reasoningEffort is none.
|
|
50
94
|
const supportsNonReasoningParameters =
|
|
51
|
-
|
|
95
|
+
isGpt6OrLaterModelWithNoneEffort ||
|
|
52
96
|
(!isGpt6OrLaterModel &&
|
|
53
97
|
gptVersion != null &&
|
|
54
98
|
(gptVersion.major > 5 ||
|
|
@@ -61,17 +105,41 @@ export function getOpenAILanguageModelCapabilities(
|
|
|
61
105
|
supportsPriorityProcessing,
|
|
62
106
|
supportsConfigurationUpdate: isGpt6OrLaterModel,
|
|
63
107
|
supportsAsyncToolCalling: isGpt6OrLaterModel,
|
|
64
|
-
supportedReasoningEfforts:
|
|
108
|
+
supportedReasoningEfforts: isGpt6OrLaterModelWithNoneEffort
|
|
65
109
|
? ['none', 'low', 'medium', 'high', 'xhigh', 'max']
|
|
66
110
|
: isGpt6OrLaterModel
|
|
67
111
|
? ['low', 'medium', 'high', 'xhigh', 'max']
|
|
68
|
-
:
|
|
112
|
+
: isStandardGpt5Model
|
|
113
|
+
? getGpt5ReasoningEfforts(gptVersion.minor ?? 0)
|
|
114
|
+
: undefined,
|
|
115
|
+
// GPT-5.6 rejects function tools with reasoning in the Chat Completions API.
|
|
116
|
+
supportsChatFunctionToolsWithReasoning: !(
|
|
117
|
+
gptVersion?.major === 5 && (gptVersion.minor ?? 0) >= 6
|
|
118
|
+
),
|
|
69
119
|
isReasoningModel,
|
|
70
120
|
systemMessageMode,
|
|
71
121
|
supportsNonReasoningParameters,
|
|
72
122
|
};
|
|
73
123
|
}
|
|
74
124
|
|
|
125
|
+
function getGpt5ReasoningEfforts(
|
|
126
|
+
minor: number,
|
|
127
|
+
): readonly OpenAIReasoningEffort[] {
|
|
128
|
+
if (minor === 0) {
|
|
129
|
+
return ['minimal', 'low', 'medium', 'high'];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (minor === 1) {
|
|
133
|
+
return ['none', 'low', 'medium', 'high'];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (minor < 6) {
|
|
137
|
+
return ['none', 'low', 'medium', 'high', 'xhigh'];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return ['none', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
141
|
+
}
|
|
142
|
+
|
|
75
143
|
function getOSeriesVersion(modelId: string): number | undefined {
|
|
76
144
|
const match = /^o(\d+)(?:-|$)/.exec(modelId);
|
|
77
145
|
return match == null ? undefined : Number(match[1]);
|
|
@@ -96,3 +164,33 @@ function getGptVersion(modelId: string):
|
|
|
96
164
|
variant: match[3],
|
|
97
165
|
};
|
|
98
166
|
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Resolves the reasoning effort to send to OpenAI.
|
|
170
|
+
*
|
|
171
|
+
* A reasoning effort from provider options is sent unchanged. Otherwise the
|
|
172
|
+
* top-level reasoning value is mapped to the closest effort that the model
|
|
173
|
+
* family supports, or sent directly when the supported efforts are not known.
|
|
174
|
+
*/
|
|
175
|
+
export function resolveOpenAIReasoningEffort({
|
|
176
|
+
providerReasoningEffort,
|
|
177
|
+
reasoning,
|
|
178
|
+
supportedReasoningEfforts,
|
|
179
|
+
warnings,
|
|
180
|
+
}: {
|
|
181
|
+
providerReasoningEffort: string | null | undefined;
|
|
182
|
+
reasoning: LanguageModelV4CallOptions['reasoning'];
|
|
183
|
+
supportedReasoningEfforts: readonly OpenAIReasoningEffort[] | undefined;
|
|
184
|
+
warnings: SharedV4Warning[];
|
|
185
|
+
}): string | undefined {
|
|
186
|
+
return mapReasoningToProviderEffort({
|
|
187
|
+
reasoning,
|
|
188
|
+
providerOptionsEffort: providerReasoningEffort,
|
|
189
|
+
effortMap: Object.fromEntries(
|
|
190
|
+
(supportedReasoningEfforts ?? ALL_OPENAI_REASONING_EFFORTS).map(
|
|
191
|
+
effort => [effort, effort],
|
|
192
|
+
),
|
|
193
|
+
),
|
|
194
|
+
warnings,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
@@ -59,6 +59,7 @@ export const openaiResponsesReasoningModelIds = [
|
|
|
59
59
|
'gpt-5.6-terra',
|
|
60
60
|
'gpt-6-astra',
|
|
61
61
|
'gpt-6.1-sol',
|
|
62
|
+
'gpt-6.1-terra',
|
|
62
63
|
'gpt-6-luna',
|
|
63
64
|
'gpt-6-sol',
|
|
64
65
|
] as const;
|
|
@@ -135,6 +136,7 @@ export type OpenAIResponsesModelId =
|
|
|
135
136
|
| 'gpt-5.6-terra'
|
|
136
137
|
| 'gpt-6-astra'
|
|
137
138
|
| 'gpt-6.1-sol'
|
|
139
|
+
| 'gpt-6.1-terra'
|
|
138
140
|
| 'gpt-6-luna'
|
|
139
141
|
| 'gpt-6-sol'
|
|
140
142
|
| 'gpt-5-2025-08-07'
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
createJsonResponseHandler,
|
|
23
23
|
createToolNameMapping,
|
|
24
24
|
generateId,
|
|
25
|
-
isCustomReasoning,
|
|
26
25
|
parseProviderOptions,
|
|
27
26
|
postJsonToApi,
|
|
28
27
|
serializeModelOptions,
|
|
@@ -37,7 +36,10 @@ import {
|
|
|
37
36
|
type OpenAIConfig,
|
|
38
37
|
} from '../openai-config';
|
|
39
38
|
import { openaiFailedResponseHandler } from '../openai-error';
|
|
40
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
getOpenAILanguageModelCapabilities,
|
|
41
|
+
resolveOpenAIReasoningEffort,
|
|
42
|
+
} from '../openai-language-model-capabilities';
|
|
41
43
|
import {
|
|
42
44
|
createOpenAIProviderStreamError,
|
|
43
45
|
throwIfOpenAIStreamErrorBeforeOutput,
|
|
@@ -341,24 +343,12 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
341
343
|
});
|
|
342
344
|
}
|
|
343
345
|
|
|
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
|
-
}
|
|
346
|
+
const resolvedReasoningEffort = resolveOpenAIReasoningEffort({
|
|
347
|
+
providerReasoningEffort: openaiOptions?.reasoningEffort,
|
|
348
|
+
reasoning,
|
|
349
|
+
supportedReasoningEfforts: modelCapabilities.supportedReasoningEfforts,
|
|
350
|
+
warnings,
|
|
351
|
+
});
|
|
362
352
|
|
|
363
353
|
const resolvedReasoningSummary =
|
|
364
354
|
openaiOptions?.reasoningSummary !== undefined
|
|
@@ -458,10 +448,13 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
458
448
|
|
|
459
449
|
// The schema accepts update efforts supported by any supported model. Check
|
|
460
450
|
// whether this specific model supports the requested effort.
|
|
451
|
+
const supportedReasoningEfforts =
|
|
452
|
+
modelCapabilities.supportedReasoningEfforts as
|
|
453
|
+
| readonly string[]
|
|
454
|
+
| undefined;
|
|
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.
|
|
@@ -725,7 +718,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
725
718
|
}
|
|
726
719
|
|
|
727
720
|
if (
|
|
728
|
-
modelCapabilities.
|
|
721
|
+
modelCapabilities.supportsConfigurationUpdate &&
|
|
729
722
|
(baseArgs.top_logprobs != null ||
|
|
730
723
|
baseArgs.include?.includes('message.output_text.logprobs'))
|
|
731
724
|
) {
|
|
@@ -1275,9 +1268,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1275
1268
|
toolName: toolNameMapping.toCustomToolName(
|
|
1276
1269
|
webSearchToolName ?? 'web_search',
|
|
1277
1270
|
),
|
|
1278
|
-
...(part
|
|
1279
|
-
? { isError: true, result: { status: part.status } }
|
|
1280
|
-
: { result: mapWebSearchOutput(part.action) }),
|
|
1271
|
+
...mapWebSearchResult(part),
|
|
1281
1272
|
});
|
|
1282
1273
|
|
|
1283
1274
|
if (part.action?.type === 'search') {
|
|
@@ -2190,10 +2181,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
2190
2181
|
toolName: toolNameMapping.toCustomToolName(
|
|
2191
2182
|
webSearchToolName ?? 'web_search',
|
|
2192
2183
|
),
|
|
2193
|
-
...(value.item
|
|
2194
|
-
value.item.status === 'incomplete'
|
|
2195
|
-
? { isError: true, result: { status: value.item.status } }
|
|
2196
|
-
: { result: mapWebSearchOutput(value.item.action) }),
|
|
2184
|
+
...mapWebSearchResult(value.item),
|
|
2197
2185
|
});
|
|
2198
2186
|
|
|
2199
2187
|
if (value.item.action?.type === 'search') {
|
|
@@ -3180,7 +3168,21 @@ function isResponseOutputChunk(chunk: OpenAIResponsesChunk): boolean {
|
|
|
3180
3168
|
);
|
|
3181
3169
|
}
|
|
3182
3170
|
|
|
3183
|
-
export function
|
|
3171
|
+
export function mapWebSearchResult({
|
|
3172
|
+
action,
|
|
3173
|
+
status,
|
|
3174
|
+
}: {
|
|
3175
|
+
action?: OpenAIResponsesWebSearchAction | null;
|
|
3176
|
+
status: string;
|
|
3177
|
+
}):
|
|
3178
|
+
| { isError: true; result: { status: string } }
|
|
3179
|
+
| { result: InferSchema<typeof webSearchOutputSchema> } {
|
|
3180
|
+
return status === 'completed'
|
|
3181
|
+
? { result: mapWebSearchOutput(action) }
|
|
3182
|
+
: { isError: true, result: { status } };
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
function mapWebSearchOutput(
|
|
3184
3186
|
action: OpenAIResponsesWebSearchAction | null | undefined,
|
|
3185
3187
|
): InferSchema<typeof webSearchOutputSchema> {
|
|
3186
3188
|
if (action == null) {
|
|
@@ -12,7 +12,9 @@ export type OpenAITranscriptionModelId =
|
|
|
12
12
|
| 'gpt-4o-mini-transcribe-2025-12-15'
|
|
13
13
|
| 'gpt-4o-transcribe'
|
|
14
14
|
| 'gpt-4o-transcribe-diarize'
|
|
15
|
+
| 'gpt-live-transcribe'
|
|
15
16
|
| 'gpt-realtime-whisper'
|
|
17
|
+
| 'gpt-transcribe'
|
|
16
18
|
| (string & {});
|
|
17
19
|
|
|
18
20
|
// https://platform.openai.com/docs/api-reference/audio/createTranscription
|
|
@@ -43,12 +45,9 @@ export const openAITranscriptionModelOptions = lazySchema(() =>
|
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
47
|
* The timestamp granularities to populate for this transcription.
|
|
46
|
-
*
|
|
48
|
+
* Defaults to ['segment'] when the response format is verbose_json.
|
|
47
49
|
*/
|
|
48
|
-
timestampGranularities: z
|
|
49
|
-
.array(z.enum(['word', 'segment']))
|
|
50
|
-
.default(['segment'])
|
|
51
|
-
.optional(),
|
|
50
|
+
timestampGranularities: z.array(z.enum(['word', 'segment'])).optional(),
|
|
52
51
|
|
|
53
52
|
/**
|
|
54
53
|
* The format of the transcription response.
|
|
@@ -73,7 +72,7 @@ export const openAITranscriptionModelOptions = lazySchema(() =>
|
|
|
73
72
|
.optional(),
|
|
74
73
|
|
|
75
74
|
/**
|
|
76
|
-
* Options for streaming transcription models such as `gpt-
|
|
75
|
+
* Options for streaming transcription models such as `gpt-live-transcribe`.
|
|
77
76
|
*/
|
|
78
77
|
streaming: z
|
|
79
78
|
.object({
|