@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.
@@ -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, and GPT-6 Luna
249
- support `'none'`, `'low'`, `'medium'`, `'high'`, `'xhigh'`, and `'max'`. GPT-6
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-realtime-whisper`.
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
- Structured state, instructions, and criterion descriptions are serialized as JSON
3695
- text. Score levels receive zero-based index labels. Non-null Boolean criteria are
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). For example:
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: { safetyIdentifier: 'user-123' } satisfies OpenAIDecisionModelOptions,
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.103",
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.69"
39
+ "@ai-sdk/provider-utils": "5.0.71"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -72,6 +72,7 @@ export type OpenAIChatModelId =
72
72
  | 'gpt-5.6-terra'
73
73
  | 'gpt-6-astra'
74
74
  | 'gpt-6.1-sol'
75
+ | 'gpt-6.1-terra'
75
76
  | 'gpt-6-luna'
76
77
  | 'gpt-6-sol'
77
78
  | (string & {});
@@ -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 { getOpenAILanguageModelCapabilities } from '../openai-language-model-capabilities';
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
- // AI SDK reasoning values map directly to the OpenAI reasoning values.
125
- let resolvedReasoningEffort =
126
- openaiOptions.reasoningEffort ??
127
- (isCustomReasoning(reasoning) ? reasoning : undefined);
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
- resolvedReasoningEffort != null &&
131
- modelCapabilities.supportedReasoningEfforts != null &&
132
- !modelCapabilities.supportedReasoningEfforts.includes(
133
- resolvedReasoningEffort,
134
- )
135
+ requiresNoReasoningEffort &&
136
+ isCustomReasoning(reasoning) &&
137
+ reasoning !== 'none'
135
138
  ) {
136
139
  warnings.push({
137
- type: 'unsupported',
138
- feature: 'reasoningEffort',
139
- details: `${this.modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`,
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.supportedReasoningEfforts != null &&
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:
@@ -47,7 +47,7 @@ import {
47
47
  type OpenAIResponsesLogprobs,
48
48
  } from './responses/openai-responses-api';
49
49
  import {
50
- mapWebSearchOutput,
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.status === 'failed' || part.status === 'incomplete'
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: 'input_text',
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 !== 'safetyIdentifier')
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
- supportedReasoningEfforts: readonly string[] | undefined;
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
- const isGpt6SolOrLuna = modelId === 'gpt-6-sol' || modelId === 'gpt-6-luna';
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 and GPT-6 Sol/Luna support temperature,
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
- isGpt6SolOrLuna ||
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: isGpt6SolOrLuna
108
+ supportedReasoningEfforts: isGpt6OrLaterModelWithNoneEffort
65
109
  ? ['none', 'low', 'medium', 'high', 'xhigh', 'max']
66
110
  : isGpt6OrLaterModel
67
111
  ? ['low', 'medium', 'high', 'xhigh', 'max']
68
- : undefined,
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 { getOpenAILanguageModelCapabilities } from '../openai-language-model-capabilities';
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
- let resolvedReasoningEffort =
345
- openaiOptions?.reasoningEffort ??
346
- (isCustomReasoning(reasoning) ? reasoning : undefined);
347
-
348
- if (
349
- resolvedReasoningEffort != null &&
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
- modelCapabilities.supportedReasoningEfforts?.includes(effort) === false
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.supportedReasoningEfforts != null &&
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.status === 'failed' || part.status === 'incomplete'
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.status === 'failed' ||
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 mapWebSearchOutput(
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
- * @default ['segment']
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-realtime-whisper`.
75
+ * Options for streaming transcription models such as `gpt-live-transcribe`.
77
76
  */
78
77
  streaming: z
79
78
  .object({