@ai-sdk/openai 4.0.10 → 4.0.11

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.
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  UnsupportedFunctionalityError,
3
3
  type LanguageModelV4Prompt,
4
+ type SharedV4ProviderOptions,
4
5
  type LanguageModelV4ToolApprovalResponsePart,
5
6
  type SharedV4Warning,
6
7
  } from '@ai-sdk/provider';
@@ -41,6 +42,17 @@ function serializeToolCallArguments(input: unknown): string {
41
42
  return JSON.stringify(input === undefined ? {} : input);
42
43
  }
43
44
 
45
+ type OpenAIPromptCacheBreakpoint = { mode: 'explicit' };
46
+
47
+ function getPromptCacheBreakpoint(
48
+ providerOptions: SharedV4ProviderOptions | undefined,
49
+ providerOptionsName: string,
50
+ ): OpenAIPromptCacheBreakpoint | undefined {
51
+ return providerOptions?.[providerOptionsName]?.promptCacheBreakpoint as
52
+ | OpenAIPromptCacheBreakpoint
53
+ | undefined;
54
+ }
55
+
44
56
  /**
45
57
  * This is soft-deprecated. Use provider references instead. Kept for backward compatibility
46
58
  * with the `fileIdPrefixes` option.
@@ -89,16 +101,48 @@ export async function convertToOpenAIResponsesInput({
89
101
  const warnings: Array<SharedV4Warning> = [];
90
102
  const processedApprovalIds = new Set<string>();
91
103
 
92
- for (const { role, content } of prompt) {
104
+ for (const { role, content, providerOptions } of prompt) {
93
105
  switch (role) {
94
106
  case 'system': {
95
107
  switch (systemMessageMode) {
96
108
  case 'system': {
97
- input.push({ role: 'system', content });
109
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
110
+ providerOptions,
111
+ providerOptionsName,
112
+ );
113
+ input.push({
114
+ role: 'system',
115
+ content:
116
+ promptCacheBreakpoint == null
117
+ ? content
118
+ : [
119
+ {
120
+ type: 'input_text',
121
+ text: content,
122
+ prompt_cache_breakpoint: promptCacheBreakpoint,
123
+ },
124
+ ],
125
+ });
98
126
  break;
99
127
  }
100
128
  case 'developer': {
101
- input.push({ role: 'developer', content });
129
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
130
+ providerOptions,
131
+ providerOptionsName,
132
+ );
133
+ input.push({
134
+ role: 'developer',
135
+ content:
136
+ promptCacheBreakpoint == null
137
+ ? content
138
+ : [
139
+ {
140
+ type: 'input_text',
141
+ text: content,
142
+ prompt_cache_breakpoint: promptCacheBreakpoint,
143
+ },
144
+ ],
145
+ });
102
146
  break;
103
147
  }
104
148
  case 'remove': {
@@ -124,9 +168,23 @@ export async function convertToOpenAIResponsesInput({
124
168
  content: content.map((part, index) => {
125
169
  switch (part.type) {
126
170
  case 'text': {
127
- return { type: 'input_text', text: part.text };
171
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
172
+ part.providerOptions,
173
+ providerOptionsName,
174
+ );
175
+ return {
176
+ type: 'input_text',
177
+ text: part.text,
178
+ ...(promptCacheBreakpoint != null && {
179
+ prompt_cache_breakpoint: promptCacheBreakpoint,
180
+ }),
181
+ };
128
182
  }
129
183
  case 'file': {
184
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
185
+ part.providerOptions,
186
+ providerOptionsName,
187
+ );
130
188
  switch (part.data.type) {
131
189
  case 'reference': {
132
190
  const fileId = resolveProviderReference({
@@ -141,12 +199,18 @@ export async function convertToOpenAIResponsesInput({
141
199
  detail:
142
200
  part.providerOptions?.[providerOptionsName]
143
201
  ?.imageDetail,
202
+ ...(promptCacheBreakpoint != null && {
203
+ prompt_cache_breakpoint: promptCacheBreakpoint,
204
+ }),
144
205
  };
145
206
  }
146
207
 
147
208
  return {
148
209
  type: 'input_file',
149
210
  file_id: fileId,
211
+ ...(promptCacheBreakpoint != null && {
212
+ prompt_cache_breakpoint: promptCacheBreakpoint,
213
+ }),
150
214
  };
151
215
  }
152
216
  case 'text': {
@@ -172,12 +236,18 @@ export async function convertToOpenAIResponsesInput({
172
236
  detail:
173
237
  part.providerOptions?.[providerOptionsName]
174
238
  ?.imageDetail,
239
+ ...(promptCacheBreakpoint != null && {
240
+ prompt_cache_breakpoint: promptCacheBreakpoint,
241
+ }),
175
242
  };
176
243
  } else {
177
244
  if (part.data.type === 'url') {
178
245
  return {
179
246
  type: 'input_file',
180
247
  file_url: part.data.url.toString(),
248
+ ...(promptCacheBreakpoint != null && {
249
+ prompt_cache_breakpoint: promptCacheBreakpoint,
250
+ }),
181
251
  };
182
252
  }
183
253
 
@@ -204,6 +274,9 @@ export async function convertToOpenAIResponsesInput({
204
274
  : `part-${index}`),
205
275
  file_data: `data:${fullMediaType};base64,${convertToBase64(part.data.data)}`,
206
276
  }),
277
+ ...(promptCacheBreakpoint != null && {
278
+ prompt_cache_breakpoint: promptCacheBreakpoint,
279
+ }),
207
280
  };
208
281
  }
209
282
  }
@@ -813,9 +886,19 @@ export async function convertToOpenAIResponsesInput({
813
886
  case 'content':
814
887
  outputValue = output.value
815
888
  .map(item => {
889
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
890
+ item.providerOptions,
891
+ providerOptionsName,
892
+ );
816
893
  switch (item.type) {
817
894
  case 'text':
818
- return { type: 'input_text' as const, text: item.text };
895
+ return {
896
+ type: 'input_text' as const,
897
+ text: item.text,
898
+ ...(promptCacheBreakpoint != null && {
899
+ prompt_cache_breakpoint: promptCacheBreakpoint,
900
+ }),
901
+ };
819
902
  case 'file': {
820
903
  const topLevel = getTopLevelMediaType(item.mediaType);
821
904
  const imageDetail =
@@ -831,12 +914,18 @@ export async function convertToOpenAIResponsesInput({
831
914
  type: 'input_image' as const,
832
915
  image_url: `data:${fullMediaType};base64,${convertToBase64(item.data.data)}`,
833
916
  detail: imageDetail,
917
+ ...(promptCacheBreakpoint != null && {
918
+ prompt_cache_breakpoint: promptCacheBreakpoint,
919
+ }),
834
920
  };
835
921
  }
836
922
  return {
837
923
  type: 'input_file' as const,
838
924
  filename: item.filename ?? 'data',
839
925
  file_data: `data:${fullMediaType};base64,${convertToBase64(item.data.data)}`,
926
+ ...(promptCacheBreakpoint != null && {
927
+ prompt_cache_breakpoint: promptCacheBreakpoint,
928
+ }),
840
929
  };
841
930
  }
842
931
 
@@ -846,11 +935,17 @@ export async function convertToOpenAIResponsesInput({
846
935
  type: 'input_image' as const,
847
936
  image_url: item.data.url.toString(),
848
937
  detail: imageDetail,
938
+ ...(promptCacheBreakpoint != null && {
939
+ prompt_cache_breakpoint: promptCacheBreakpoint,
940
+ }),
849
941
  };
850
942
  }
851
943
  return {
852
944
  type: 'input_file' as const,
853
945
  file_url: item.data.url.toString(),
946
+ ...(promptCacheBreakpoint != null && {
947
+ prompt_cache_breakpoint: promptCacheBreakpoint,
948
+ }),
854
949
  };
855
950
  }
856
951
 
@@ -897,9 +992,19 @@ export async function convertToOpenAIResponsesInput({
897
992
  case 'content':
898
993
  contentValue = output.value
899
994
  .map(item => {
995
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
996
+ item.providerOptions,
997
+ providerOptionsName,
998
+ );
900
999
  switch (item.type) {
901
1000
  case 'text': {
902
- return { type: 'input_text' as const, text: item.text };
1001
+ return {
1002
+ type: 'input_text' as const,
1003
+ text: item.text,
1004
+ ...(promptCacheBreakpoint != null && {
1005
+ prompt_cache_breakpoint: promptCacheBreakpoint,
1006
+ }),
1007
+ };
903
1008
  }
904
1009
 
905
1010
  case 'file': {
@@ -917,12 +1022,18 @@ export async function convertToOpenAIResponsesInput({
917
1022
  type: 'input_image' as const,
918
1023
  image_url: `data:${fullMediaType};base64,${convertToBase64(item.data.data)}`,
919
1024
  detail: imageDetail,
1025
+ ...(promptCacheBreakpoint != null && {
1026
+ prompt_cache_breakpoint: promptCacheBreakpoint,
1027
+ }),
920
1028
  };
921
1029
  }
922
1030
  return {
923
1031
  type: 'input_file' as const,
924
1032
  filename: item.filename ?? 'data',
925
1033
  file_data: `data:${fullMediaType};base64,${convertToBase64(item.data.data)}`,
1034
+ ...(promptCacheBreakpoint != null && {
1035
+ prompt_cache_breakpoint: promptCacheBreakpoint,
1036
+ }),
926
1037
  };
927
1038
  }
928
1039
 
@@ -932,11 +1043,17 @@ export async function convertToOpenAIResponsesInput({
932
1043
  type: 'input_image' as const,
933
1044
  image_url: item.data.url.toString(),
934
1045
  detail: imageDetail,
1046
+ ...(promptCacheBreakpoint != null && {
1047
+ prompt_cache_breakpoint: promptCacheBreakpoint,
1048
+ }),
935
1049
  };
936
1050
  }
937
1051
  return {
938
1052
  type: 'input_file' as const,
939
1053
  file_url: item.data.url.toString(),
1054
+ ...(promptCacheBreakpoint != null && {
1055
+ prompt_cache_breakpoint: promptCacheBreakpoint,
1056
+ }),
940
1057
  };
941
1058
  }
942
1059
 
@@ -73,18 +73,49 @@ export type OpenAIResponsesApplyPatchOperationDiffDoneChunk = {
73
73
 
74
74
  export type OpenAIResponsesSystemMessage = {
75
75
  role: 'system' | 'developer';
76
- content: string;
76
+ content:
77
+ | string
78
+ | Array<{
79
+ type: 'input_text';
80
+ text: string;
81
+ prompt_cache_breakpoint?: { mode: 'explicit' };
82
+ }>;
77
83
  };
78
84
 
79
85
  export type OpenAIResponsesUserMessage = {
80
86
  role: 'user';
81
87
  content: Array<
82
- | { type: 'input_text'; text: string }
83
- | { type: 'input_image'; image_url: string }
84
- | { type: 'input_image'; file_id: string }
85
- | { type: 'input_file'; file_url: string }
86
- | { type: 'input_file'; filename: string; file_data: string }
87
- | { type: 'input_file'; file_id: string }
88
+ | {
89
+ type: 'input_text';
90
+ text: string;
91
+ prompt_cache_breakpoint?: { mode: 'explicit' };
92
+ }
93
+ | {
94
+ type: 'input_image';
95
+ image_url: string;
96
+ prompt_cache_breakpoint?: { mode: 'explicit' };
97
+ }
98
+ | {
99
+ type: 'input_image';
100
+ file_id: string;
101
+ prompt_cache_breakpoint?: { mode: 'explicit' };
102
+ }
103
+ | {
104
+ type: 'input_file';
105
+ file_url: string;
106
+ prompt_cache_breakpoint?: { mode: 'explicit' };
107
+ }
108
+ | {
109
+ type: 'input_file';
110
+ filename: string;
111
+ file_data: string;
112
+ prompt_cache_breakpoint?: { mode: 'explicit' };
113
+ }
114
+ | {
115
+ type: 'input_file';
116
+ file_id: string;
117
+ prompt_cache_breakpoint?: { mode: 'explicit' };
118
+ }
88
119
  >;
89
120
  };
90
121
 
@@ -110,10 +141,27 @@ export type OpenAIResponsesFunctionCallOutput = {
110
141
  output:
111
142
  | string
112
143
  | Array<
113
- | { type: 'input_text'; text: string }
114
- | { type: 'input_image'; image_url: string }
115
- | { type: 'input_file'; filename: string; file_data: string }
116
- | { type: 'input_file'; file_url: string }
144
+ | {
145
+ type: 'input_text';
146
+ text: string;
147
+ prompt_cache_breakpoint?: { mode: 'explicit' };
148
+ }
149
+ | {
150
+ type: 'input_image';
151
+ image_url: string;
152
+ prompt_cache_breakpoint?: { mode: 'explicit' };
153
+ }
154
+ | {
155
+ type: 'input_file';
156
+ filename: string;
157
+ file_data: string;
158
+ prompt_cache_breakpoint?: { mode: 'explicit' };
159
+ }
160
+ | {
161
+ type: 'input_file';
162
+ file_url: string;
163
+ prompt_cache_breakpoint?: { mode: 'explicit' };
164
+ }
117
165
  >;
118
166
  };
119
167
 
@@ -534,6 +582,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
534
582
  input_tokens_details: z
535
583
  .object({
536
584
  cached_tokens: z.number().nullish(),
585
+ cache_write_tokens: z.number().nullish(),
537
586
  orchestration_input_tokens: z.number().nullish(),
538
587
  orchestration_input_cached_tokens: z.number().nullish(),
539
588
  })
@@ -546,6 +595,11 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
546
595
  })
547
596
  .nullish(),
548
597
  }),
598
+ reasoning: z
599
+ .object({
600
+ context: z.string().nullish(),
601
+ })
602
+ .nullish(),
549
603
  service_tier: z.string().nullish(),
550
604
  }),
551
605
  }),
@@ -566,6 +620,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
566
620
  input_tokens_details: z
567
621
  .object({
568
622
  cached_tokens: z.number().nullish(),
623
+ cache_write_tokens: z.number().nullish(),
569
624
  orchestration_input_tokens: z.number().nullish(),
570
625
  orchestration_input_cached_tokens: z.number().nullish(),
571
626
  })
@@ -579,6 +634,11 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
579
634
  .nullish(),
580
635
  })
581
636
  .nullish(),
637
+ reasoning: z
638
+ .object({
639
+ context: z.string().nullish(),
640
+ })
641
+ .nullish(),
582
642
  service_tier: z.string().nullish(),
583
643
  }),
584
644
  }),
@@ -1426,6 +1486,11 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1426
1486
  )
1427
1487
  .optional(),
1428
1488
  service_tier: z.string().nullish(),
1489
+ reasoning: z
1490
+ .object({
1491
+ context: z.string().nullish(),
1492
+ })
1493
+ .nullish(),
1429
1494
  incomplete_details: z.object({ reason: z.string() }).nullish(),
1430
1495
  usage: z
1431
1496
  .object({
@@ -1433,6 +1498,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1433
1498
  input_tokens_details: z
1434
1499
  .object({
1435
1500
  cached_tokens: z.number().nullish(),
1501
+ cache_write_tokens: z.number().nullish(),
1436
1502
  orchestration_input_tokens: z.number().nullish(),
1437
1503
  orchestration_input_cached_tokens: z.number().nullish(),
1438
1504
  })
@@ -230,11 +230,25 @@ export const openaiLanguageModelResponsesOptionsSchema = lazySchema(() =>
230
230
  */
231
231
  promptCacheKey: z.string().nullish(),
232
232
 
233
+ /**
234
+ * Prompt cache behavior for GPT-5.6 and later models.
235
+ * `mode` controls whether OpenAI also places an implicit breakpoint.
236
+ * `ttl` sets the minimum cache lifetime and currently only supports 30 minutes.
237
+ */
238
+ promptCacheOptions: z
239
+ .object({
240
+ mode: z.enum(['implicit', 'explicit']).optional(),
241
+ ttl: z.literal('30m').optional(),
242
+ })
243
+ .optional(),
244
+
233
245
  /**
234
246
  * The retention policy for the prompt cache.
235
247
  * - 'in_memory': Default. Standard prompt caching behavior.
236
248
  * - '24h': Extended prompt caching that keeps cached prefixes active for up to 24 hours.
237
- * Currently only available for 5.1 series models.
249
+ * Available for models before GPT-5.6 that support extended caching.
250
+ *
251
+ * @deprecated For GPT-5.6 and later models, use `promptCacheOptions.ttl`.
238
252
  *
239
253
  * @default 'in_memory'
240
254
  */
@@ -243,15 +257,26 @@ export const openaiLanguageModelResponsesOptionsSchema = lazySchema(() =>
243
257
  /**
244
258
  * Reasoning effort for reasoning models. Defaults to `medium`. If you use
245
259
  * `providerOptions` to set the `reasoningEffort` option, this model setting will be ignored.
246
- * Valid values: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'
247
- *
248
- * The 'none' type for `reasoningEffort` is only available for OpenAI's GPT-5.1
249
- * models. Also, the 'xhigh' type for `reasoningEffort` is only available for
250
- * OpenAI's GPT-5.1-Codex-Max model. Setting `reasoningEffort` to 'none' or 'xhigh' with unsupported models will result in
251
- * an error.
260
+ * GPT-5.6 supports 'none' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'.
261
+ * Supported values vary by model.
252
262
  */
253
263
  reasoningEffort: z.string().nullish(),
254
264
 
265
+ /**
266
+ * Controls how much model work GPT-5.6 performs before returning a final answer.
267
+ * `standard` is the default. `pro` increases quality, latency, and token usage.
268
+ */
269
+ reasoningMode: z.enum(['standard', 'pro']).optional(),
270
+
271
+ /**
272
+ * Controls which available reasoning items GPT-5.6 can use.
273
+ * `auto` uses the model default, `current_turn` excludes reasoning from earlier
274
+ * turns, and `all_turns` makes compatible earlier reasoning available.
275
+ */
276
+ reasoningContext: z
277
+ .enum(['auto', 'current_turn', 'all_turns'])
278
+ .optional(),
279
+
255
280
  /**
256
281
  * Controls reasoning summary output from the model.
257
282
  * Set to "auto" to automatically receive the richest level available,
@@ -367,6 +367,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
367
367
  service_tier: openaiOptions?.serviceTier,
368
368
  include,
369
369
  prompt_cache_key: openaiOptions?.promptCacheKey,
370
+ prompt_cache_options: openaiOptions?.promptCacheOptions,
370
371
  prompt_cache_retention: openaiOptions?.promptCacheRetention,
371
372
  safety_identifier: openaiOptions?.safetyIdentifier,
372
373
  top_logprobs: topLogprobs,
@@ -383,7 +384,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
383
384
  // model-specific settings:
384
385
  ...(isReasoningModel &&
385
386
  (resolvedReasoningEffort != null ||
386
- resolvedReasoningSummary != null) && {
387
+ resolvedReasoningSummary != null ||
388
+ openaiOptions?.reasoningMode != null ||
389
+ openaiOptions?.reasoningContext != null) && {
387
390
  reasoning: {
388
391
  ...(resolvedReasoningEffort != null && {
389
392
  effort: resolvedReasoningEffort,
@@ -391,6 +394,12 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
391
394
  ...(resolvedReasoningSummary != null && {
392
395
  summary: resolvedReasoningSummary,
393
396
  }),
397
+ ...(openaiOptions?.reasoningMode != null && {
398
+ mode: openaiOptions.reasoningMode,
399
+ }),
400
+ ...(openaiOptions?.reasoningContext != null && {
401
+ context: openaiOptions.reasoningContext,
402
+ }),
394
403
  },
395
404
  }),
396
405
  };
@@ -440,6 +449,22 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
440
449
  details: 'reasoningSummary is not supported for non-reasoning models',
441
450
  });
442
451
  }
452
+
453
+ if (openaiOptions?.reasoningMode != null) {
454
+ warnings.push({
455
+ type: 'unsupported',
456
+ feature: 'reasoningMode',
457
+ details: 'reasoningMode is not supported for non-reasoning models',
458
+ });
459
+ }
460
+
461
+ if (openaiOptions?.reasoningContext != null) {
462
+ warnings.push({
463
+ type: 'unsupported',
464
+ feature: 'reasoningContext',
465
+ details: 'reasoningContext is not supported for non-reasoning models',
466
+ });
467
+ }
443
468
  }
444
469
 
445
470
  // Validate flex processing support
@@ -1046,6 +1071,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1046
1071
  ...(typeof response.service_tier === 'string'
1047
1072
  ? { serviceTier: response.service_tier }
1048
1073
  : {}),
1074
+ ...(response.reasoning?.context != null
1075
+ ? { reasoningContext: response.reasoning.context }
1076
+ : {}),
1049
1077
  } satisfies ResponsesProviderMetadata,
1050
1078
  };
1051
1079
 
@@ -1179,6 +1207,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1179
1207
  > = {};
1180
1208
 
1181
1209
  let serviceTier: string | undefined;
1210
+ let reasoningContext: ResponsesProviderMetadata['reasoningContext'];
1182
1211
  const hostedToolSearchCallIds: string[] = [];
1183
1212
  let encounteredStreamError = false;
1184
1213
 
@@ -2109,6 +2138,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2109
2138
  if (typeof value.response.service_tier === 'string') {
2110
2139
  serviceTier = value.response.service_tier;
2111
2140
  }
2141
+ if (value.response.reasoning?.context != null) {
2142
+ reasoningContext = value.response.reasoning.context;
2143
+ }
2112
2144
  } else if (isResponseFailedChunk(value)) {
2113
2145
  const incompleteReason =
2114
2146
  value.response.incomplete_details?.reason;
@@ -2122,6 +2154,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2122
2154
  raw: incompleteReason ?? 'error',
2123
2155
  };
2124
2156
  usage = value.response.usage ?? undefined;
2157
+ if (value.response.reasoning?.context != null) {
2158
+ reasoningContext = value.response.reasoning.context;
2159
+ }
2125
2160
 
2126
2161
  if (!encounteredStreamError && value.response.error != null) {
2127
2162
  encounteredStreamError = true;
@@ -2219,6 +2254,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2219
2254
  responseId: responseId,
2220
2255
  ...(logprobs.length > 0 ? { logprobs } : {}),
2221
2256
  ...(serviceTier !== undefined ? { serviceTier } : {}),
2257
+ ...(reasoningContext !== undefined ? { reasoningContext } : {}),
2222
2258
  } satisfies ResponsesProviderMetadata,
2223
2259
  };
2224
2260
 
@@ -15,6 +15,7 @@ export type ResponsesProviderMetadata = {
15
15
  responseId: string | null | undefined;
16
16
  logprobs?: Array<OpenAIResponsesLogprobs>;
17
17
  serviceTier?: string;
18
+ reasoningContext?: string;
18
19
  };
19
20
 
20
21
  export type ResponsesReasoningProviderMetadata = {