@ai-sdk/openai 3.0.83 → 3.0.85

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.
@@ -2,6 +2,7 @@ import {
2
2
  UnsupportedFunctionalityError,
3
3
  type LanguageModelV3Prompt,
4
4
  type LanguageModelV3ToolApprovalResponsePart,
5
+ type SharedV3ProviderOptions,
5
6
  type SharedV3Warning,
6
7
  } from '@ai-sdk/provider';
7
8
  import {
@@ -37,6 +38,17 @@ function serializeToolCallArguments(input: unknown): string {
37
38
  return JSON.stringify(input === undefined ? {} : input);
38
39
  }
39
40
 
41
+ type OpenAIPromptCacheBreakpoint = { mode: 'explicit' };
42
+
43
+ function getPromptCacheBreakpoint(
44
+ providerOptions: SharedV3ProviderOptions | undefined,
45
+ providerOptionsName: string,
46
+ ): OpenAIPromptCacheBreakpoint | undefined {
47
+ return providerOptions?.[providerOptionsName]?.promptCacheBreakpoint as
48
+ | OpenAIPromptCacheBreakpoint
49
+ | undefined;
50
+ }
51
+
40
52
  /**
41
53
  * Check if a string is a file ID based on the given prefixes
42
54
  * Returns false if prefixes is undefined (disables file ID detection)
@@ -82,16 +94,48 @@ export async function convertToOpenAIResponsesInput({
82
94
  const warnings: Array<SharedV3Warning> = [];
83
95
  const processedApprovalIds = new Set<string>();
84
96
 
85
- for (const { role, content } of prompt) {
97
+ for (const { role, content, providerOptions } of prompt) {
86
98
  switch (role) {
87
99
  case 'system': {
88
100
  switch (systemMessageMode) {
89
101
  case 'system': {
90
- input.push({ role: 'system', content });
102
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
103
+ providerOptions,
104
+ providerOptionsName,
105
+ );
106
+ input.push({
107
+ role: 'system',
108
+ content:
109
+ promptCacheBreakpoint == null
110
+ ? content
111
+ : [
112
+ {
113
+ type: 'input_text',
114
+ text: content,
115
+ prompt_cache_breakpoint: promptCacheBreakpoint,
116
+ },
117
+ ],
118
+ });
91
119
  break;
92
120
  }
93
121
  case 'developer': {
94
- input.push({ role: 'developer', content });
122
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
123
+ providerOptions,
124
+ providerOptionsName,
125
+ );
126
+ input.push({
127
+ role: 'developer',
128
+ content:
129
+ promptCacheBreakpoint == null
130
+ ? content
131
+ : [
132
+ {
133
+ type: 'input_text',
134
+ text: content,
135
+ prompt_cache_breakpoint: promptCacheBreakpoint,
136
+ },
137
+ ],
138
+ });
95
139
  break;
96
140
  }
97
141
  case 'remove': {
@@ -117,9 +161,23 @@ export async function convertToOpenAIResponsesInput({
117
161
  content: content.map((part, index) => {
118
162
  switch (part.type) {
119
163
  case 'text': {
120
- return { type: 'input_text', text: part.text };
164
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
165
+ part.providerOptions,
166
+ providerOptionsName,
167
+ );
168
+ return {
169
+ type: 'input_text',
170
+ text: part.text,
171
+ ...(promptCacheBreakpoint != null && {
172
+ prompt_cache_breakpoint: promptCacheBreakpoint,
173
+ }),
174
+ };
121
175
  }
122
176
  case 'file': {
177
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
178
+ part.providerOptions,
179
+ providerOptionsName,
180
+ );
123
181
  const mediaType =
124
182
  part.mediaType === 'image/*' ? 'image/jpeg' : part.mediaType;
125
183
 
@@ -136,6 +194,9 @@ export async function convertToOpenAIResponsesInput({
136
194
  }),
137
195
  detail:
138
196
  part.providerOptions?.[providerOptionsName]?.imageDetail,
197
+ ...(promptCacheBreakpoint != null && {
198
+ prompt_cache_breakpoint: promptCacheBreakpoint,
199
+ }),
139
200
  };
140
201
  }
141
202
 
@@ -143,6 +204,9 @@ export async function convertToOpenAIResponsesInput({
143
204
  return {
144
205
  type: 'input_file',
145
206
  file_url: part.data.toString(),
207
+ ...(promptCacheBreakpoint != null && {
208
+ prompt_cache_breakpoint: promptCacheBreakpoint,
209
+ }),
146
210
  };
147
211
  }
148
212
 
@@ -168,6 +232,9 @@ export async function convertToOpenAIResponsesInput({
168
232
  : `part-${index}`),
169
233
  file_data: `data:${mediaType};base64,${convertToBase64(part.data)}`,
170
234
  }),
235
+ ...(promptCacheBreakpoint != null && {
236
+ prompt_cache_breakpoint: promptCacheBreakpoint,
237
+ }),
171
238
  };
172
239
  }
173
240
  }
@@ -743,9 +810,19 @@ export async function convertToOpenAIResponsesInput({
743
810
  case 'content':
744
811
  outputValue = output.value
745
812
  .map(item => {
813
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
814
+ item.providerOptions,
815
+ providerOptionsName,
816
+ );
746
817
  switch (item.type) {
747
818
  case 'text':
748
- return { type: 'input_text' as const, text: item.text };
819
+ return {
820
+ type: 'input_text' as const,
821
+ text: item.text,
822
+ ...(promptCacheBreakpoint != null && {
823
+ prompt_cache_breakpoint: promptCacheBreakpoint,
824
+ }),
825
+ };
749
826
  case 'image-data':
750
827
  return {
751
828
  type: 'input_image' as const,
@@ -753,6 +830,9 @@ export async function convertToOpenAIResponsesInput({
753
830
  detail:
754
831
  item.providerOptions?.[providerOptionsName]
755
832
  ?.imageDetail,
833
+ ...(promptCacheBreakpoint != null && {
834
+ prompt_cache_breakpoint: promptCacheBreakpoint,
835
+ }),
756
836
  };
757
837
  case 'image-url':
758
838
  return {
@@ -761,17 +841,26 @@ export async function convertToOpenAIResponsesInput({
761
841
  detail:
762
842
  item.providerOptions?.[providerOptionsName]
763
843
  ?.imageDetail,
844
+ ...(promptCacheBreakpoint != null && {
845
+ prompt_cache_breakpoint: promptCacheBreakpoint,
846
+ }),
764
847
  };
765
848
  case 'file-data':
766
849
  return {
767
850
  type: 'input_file' as const,
768
851
  filename: item.filename ?? 'data',
769
852
  file_data: `data:${item.mediaType};base64,${item.data}`,
853
+ ...(promptCacheBreakpoint != null && {
854
+ prompt_cache_breakpoint: promptCacheBreakpoint,
855
+ }),
770
856
  };
771
857
  case 'file-url':
772
858
  return {
773
859
  type: 'input_file' as const,
774
860
  file_url: item.url,
861
+ ...(promptCacheBreakpoint != null && {
862
+ prompt_cache_breakpoint: promptCacheBreakpoint,
863
+ }),
775
864
  };
776
865
  default:
777
866
  warnings.push({
@@ -810,9 +899,19 @@ export async function convertToOpenAIResponsesInput({
810
899
  case 'content':
811
900
  contentValue = output.value
812
901
  .map(item => {
902
+ const promptCacheBreakpoint = getPromptCacheBreakpoint(
903
+ item.providerOptions,
904
+ providerOptionsName,
905
+ );
813
906
  switch (item.type) {
814
907
  case 'text': {
815
- return { type: 'input_text' as const, text: item.text };
908
+ return {
909
+ type: 'input_text' as const,
910
+ text: item.text,
911
+ ...(promptCacheBreakpoint != null && {
912
+ prompt_cache_breakpoint: promptCacheBreakpoint,
913
+ }),
914
+ };
816
915
  }
817
916
 
818
917
  case 'image-data': {
@@ -822,6 +921,9 @@ export async function convertToOpenAIResponsesInput({
822
921
  detail:
823
922
  item.providerOptions?.[providerOptionsName]
824
923
  ?.imageDetail,
924
+ ...(promptCacheBreakpoint != null && {
925
+ prompt_cache_breakpoint: promptCacheBreakpoint,
926
+ }),
825
927
  };
826
928
  }
827
929
 
@@ -832,6 +934,9 @@ export async function convertToOpenAIResponsesInput({
832
934
  detail:
833
935
  item.providerOptions?.[providerOptionsName]
834
936
  ?.imageDetail,
937
+ ...(promptCacheBreakpoint != null && {
938
+ prompt_cache_breakpoint: promptCacheBreakpoint,
939
+ }),
835
940
  };
836
941
  }
837
942
 
@@ -840,6 +945,9 @@ export async function convertToOpenAIResponsesInput({
840
945
  type: 'input_file' as const,
841
946
  filename: item.filename ?? 'data',
842
947
  file_data: `data:${item.mediaType};base64,${item.data}`,
948
+ ...(promptCacheBreakpoint != null && {
949
+ prompt_cache_breakpoint: promptCacheBreakpoint,
950
+ }),
843
951
  };
844
952
  }
845
953
 
@@ -847,6 +955,9 @@ export async function convertToOpenAIResponsesInput({
847
955
  return {
848
956
  type: 'input_file' as const,
849
957
  file_url: item.url,
958
+ ...(promptCacheBreakpoint != null && {
959
+ prompt_cache_breakpoint: promptCacheBreakpoint,
960
+ }),
850
961
  };
851
962
  }
852
963
 
@@ -72,18 +72,49 @@ export type OpenAIResponsesApplyPatchOperationDiffDoneChunk = {
72
72
 
73
73
  export type OpenAIResponsesSystemMessage = {
74
74
  role: 'system' | 'developer';
75
- content: string;
75
+ content:
76
+ | string
77
+ | Array<{
78
+ type: 'input_text';
79
+ text: string;
80
+ prompt_cache_breakpoint?: { mode: 'explicit' };
81
+ }>;
76
82
  };
77
83
 
78
84
  export type OpenAIResponsesUserMessage = {
79
85
  role: 'user';
80
86
  content: Array<
81
- | { type: 'input_text'; text: string }
82
- | { type: 'input_image'; image_url: string }
83
- | { type: 'input_image'; file_id: string }
84
- | { type: 'input_file'; file_url: string }
85
- | { type: 'input_file'; filename: string; file_data: string }
86
- | { type: 'input_file'; file_id: string }
87
+ | {
88
+ type: 'input_text';
89
+ text: string;
90
+ prompt_cache_breakpoint?: { mode: 'explicit' };
91
+ }
92
+ | {
93
+ type: 'input_image';
94
+ image_url: string;
95
+ prompt_cache_breakpoint?: { mode: 'explicit' };
96
+ }
97
+ | {
98
+ type: 'input_image';
99
+ file_id: string;
100
+ prompt_cache_breakpoint?: { mode: 'explicit' };
101
+ }
102
+ | {
103
+ type: 'input_file';
104
+ file_url: string;
105
+ prompt_cache_breakpoint?: { mode: 'explicit' };
106
+ }
107
+ | {
108
+ type: 'input_file';
109
+ filename: string;
110
+ file_data: string;
111
+ prompt_cache_breakpoint?: { mode: 'explicit' };
112
+ }
113
+ | {
114
+ type: 'input_file';
115
+ file_id: string;
116
+ prompt_cache_breakpoint?: { mode: 'explicit' };
117
+ }
87
118
  >;
88
119
  };
89
120
 
@@ -109,10 +140,27 @@ export type OpenAIResponsesFunctionCallOutput = {
109
140
  output:
110
141
  | string
111
142
  | Array<
112
- | { type: 'input_text'; text: string }
113
- | { type: 'input_image'; image_url: string }
114
- | { type: 'input_file'; filename: string; file_data: string }
115
- | { type: 'input_file'; file_url: string }
143
+ | {
144
+ type: 'input_text';
145
+ text: string;
146
+ prompt_cache_breakpoint?: { mode: 'explicit' };
147
+ }
148
+ | {
149
+ type: 'input_image';
150
+ image_url: string;
151
+ prompt_cache_breakpoint?: { mode: 'explicit' };
152
+ }
153
+ | {
154
+ type: 'input_file';
155
+ filename: string;
156
+ file_data: string;
157
+ prompt_cache_breakpoint?: { mode: 'explicit' };
158
+ }
159
+ | {
160
+ type: 'input_file';
161
+ file_url: string;
162
+ prompt_cache_breakpoint?: { mode: 'explicit' };
163
+ }
116
164
  >;
117
165
  };
118
166
 
@@ -527,6 +575,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
527
575
  input_tokens_details: z
528
576
  .object({
529
577
  cached_tokens: z.number().nullish(),
578
+ cache_write_tokens: z.number().nullish(),
530
579
  orchestration_input_tokens: z.number().nullish(),
531
580
  orchestration_input_cached_tokens: z.number().nullish(),
532
581
  })
@@ -539,6 +588,11 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
539
588
  })
540
589
  .nullish(),
541
590
  }),
591
+ reasoning: z
592
+ .object({
593
+ context: z.string().nullish(),
594
+ })
595
+ .nullish(),
542
596
  service_tier: z.string().nullish(),
543
597
  }),
544
598
  }),
@@ -559,6 +613,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
559
613
  input_tokens_details: z
560
614
  .object({
561
615
  cached_tokens: z.number().nullish(),
616
+ cache_write_tokens: z.number().nullish(),
562
617
  orchestration_input_tokens: z.number().nullish(),
563
618
  orchestration_input_cached_tokens: z.number().nullish(),
564
619
  })
@@ -572,6 +627,11 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
572
627
  .nullish(),
573
628
  })
574
629
  .nullish(),
630
+ reasoning: z
631
+ .object({
632
+ context: z.string().nullish(),
633
+ })
634
+ .nullish(),
575
635
  service_tier: z.string().nullish(),
576
636
  }),
577
637
  }),
@@ -1404,6 +1464,11 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1404
1464
  )
1405
1465
  .optional(),
1406
1466
  service_tier: z.string().nullish(),
1467
+ reasoning: z
1468
+ .object({
1469
+ context: z.string().nullish(),
1470
+ })
1471
+ .nullish(),
1407
1472
  incomplete_details: z.object({ reason: z.string() }).nullish(),
1408
1473
  usage: z
1409
1474
  .object({
@@ -1411,6 +1476,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1411
1476
  input_tokens_details: z
1412
1477
  .object({
1413
1478
  cached_tokens: z.number().nullish(),
1479
+ cache_write_tokens: z.number().nullish(),
1414
1480
  orchestration_input_tokens: z.number().nullish(),
1415
1481
  orchestration_input_cached_tokens: z.number().nullish(),
1416
1482
  })
@@ -341,6 +341,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
341
341
  service_tier: openaiOptions?.serviceTier,
342
342
  include,
343
343
  prompt_cache_key: openaiOptions?.promptCacheKey,
344
+ prompt_cache_options: openaiOptions?.promptCacheOptions,
344
345
  prompt_cache_retention: openaiOptions?.promptCacheRetention,
345
346
  safety_identifier: openaiOptions?.safetyIdentifier,
346
347
  top_logprobs: topLogprobs,
@@ -349,7 +350,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
349
350
  // model-specific settings:
350
351
  ...(isReasoningModel &&
351
352
  (openaiOptions?.reasoningEffort != null ||
352
- openaiOptions?.reasoningSummary != null) && {
353
+ openaiOptions?.reasoningSummary != null ||
354
+ openaiOptions?.reasoningMode != null ||
355
+ openaiOptions?.reasoningContext != null) && {
353
356
  reasoning: {
354
357
  ...(openaiOptions?.reasoningEffort != null && {
355
358
  effort: openaiOptions.reasoningEffort,
@@ -357,6 +360,12 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
357
360
  ...(openaiOptions?.reasoningSummary != null && {
358
361
  summary: openaiOptions.reasoningSummary,
359
362
  }),
363
+ ...(openaiOptions?.reasoningMode != null && {
364
+ mode: openaiOptions.reasoningMode,
365
+ }),
366
+ ...(openaiOptions?.reasoningContext != null && {
367
+ context: openaiOptions.reasoningContext,
368
+ }),
360
369
  },
361
370
  }),
362
371
  };
@@ -406,6 +415,22 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
406
415
  details: 'reasoningSummary is not supported for non-reasoning models',
407
416
  });
408
417
  }
418
+
419
+ if (openaiOptions?.reasoningMode != null) {
420
+ warnings.push({
421
+ type: 'unsupported',
422
+ feature: 'reasoningMode',
423
+ details: 'reasoningMode is not supported for non-reasoning models',
424
+ });
425
+ }
426
+
427
+ if (openaiOptions?.reasoningContext != null) {
428
+ warnings.push({
429
+ type: 'unsupported',
430
+ feature: 'reasoningContext',
431
+ details: 'reasoningContext is not supported for non-reasoning models',
432
+ });
433
+ }
409
434
  }
410
435
 
411
436
  // Validate flex processing support
@@ -997,6 +1022,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
997
1022
  ...(typeof response.service_tier === 'string'
998
1023
  ? { serviceTier: response.service_tier }
999
1024
  : {}),
1025
+ ...(response.reasoning?.context != null
1026
+ ? { reasoningContext: response.reasoning.context }
1027
+ : {}),
1000
1028
  } satisfies ResponsesProviderMetadata,
1001
1029
  };
1002
1030
 
@@ -1130,6 +1158,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1130
1158
  > = {};
1131
1159
 
1132
1160
  let serviceTier: string | undefined;
1161
+ let reasoningContext: ResponsesProviderMetadata['reasoningContext'];
1133
1162
  const hostedToolSearchCallIds: string[] = [];
1134
1163
  let encounteredStreamError = false;
1135
1164
 
@@ -2048,6 +2077,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2048
2077
  if (typeof value.response.service_tier === 'string') {
2049
2078
  serviceTier = value.response.service_tier;
2050
2079
  }
2080
+ if (value.response.reasoning?.context != null) {
2081
+ reasoningContext = value.response.reasoning.context;
2082
+ }
2051
2083
  } else if (isResponseFailedChunk(value)) {
2052
2084
  const incompleteReason =
2053
2085
  value.response.incomplete_details?.reason;
@@ -2061,6 +2093,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2061
2093
  raw: incompleteReason ?? 'error',
2062
2094
  };
2063
2095
  usage = value.response.usage ?? undefined;
2096
+ if (value.response.reasoning?.context != null) {
2097
+ reasoningContext = value.response.reasoning.context;
2098
+ }
2064
2099
 
2065
2100
  if (!encounteredStreamError && value.response.error != null) {
2066
2101
  encounteredStreamError = true;
@@ -2158,6 +2193,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2158
2193
  responseId: responseId,
2159
2194
  ...(logprobs.length > 0 ? { logprobs } : {}),
2160
2195
  ...(serviceTier !== undefined ? { serviceTier } : {}),
2196
+ ...(reasoningContext !== undefined ? { reasoningContext } : {}),
2161
2197
  } satisfies ResponsesProviderMetadata,
2162
2198
  };
2163
2199
 
@@ -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,
@@ -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 = {