@ai-sdk/openai 3.0.117 → 3.0.119
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 +14 -0
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +1135 -1066
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1120 -1050
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +1252 -1183
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +1237 -1167
- package/dist/internal/index.mjs.map +1 -1
- package/docs/03-openai.mdx +83 -34
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/responses/convert-to-openai-responses-input.ts +39 -0
- package/src/responses/openai-responses-language-model.ts +63 -14
- package/src/responses/openai-responses-options.ts +22 -0
package/docs/03-openai.mdx
CHANGED
|
@@ -325,55 +325,104 @@ The following OpenAI-specific metadata may be returned:
|
|
|
325
325
|
|
|
326
326
|
#### Changing Reasoning Effort Mid-Conversation
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
`reasoningEffortUpdate` as an OpenAI `configuration_update` input item before
|
|
331
|
-
the next user message. Keeping the request-level effort unchanged preserves the
|
|
332
|
-
original prompt prefix for prompt caching.
|
|
328
|
+
You can increase reasoning effort for difficult work or reduce it for routine
|
|
329
|
+
follow-ups while preserving the conversation prefix for prompt caching:
|
|
333
330
|
|
|
334
|
-
|
|
331
|
+
- Set the initial effort with request-level `providerOptions.openai.reasoningEffort` and keep that setting
|
|
332
|
+
unchanged throughout the conversation.
|
|
333
|
+
- For later changes, add a message-level `reasoningEffortUpdate` before the next
|
|
334
|
+
user message, following
|
|
335
|
+
[OpenAI's configuration update guidance](https://developers.openai.com/api/docs/guides/reasoning#change-reasoning-mid-conversation).
|
|
336
|
+
|
|
337
|
+
The new effort applies until another update changes it. When sending conversation
|
|
338
|
+
history in `messages`, include earlier effort updates along with the other messages.
|
|
339
|
+
|
|
340
|
+
Represent an update as an empty system message with
|
|
341
|
+
`providerOptions.openai.reasoningEffortUpdate`.
|
|
342
|
+
|
|
343
|
+
Set `allowSystemInMessages: true` to explicitly allow these trusted system messages
|
|
344
|
+
and suppress the warning for system messages in `messages`.
|
|
345
|
+
|
|
346
|
+
In this example, the conversation starts at low effort and switches to high
|
|
347
|
+
effort for the follow-up. Both requests keep `providerOptions.openai.reasoningEffort: 'low'`.
|
|
348
|
+
|
|
349
|
+
```ts
|
|
335
350
|
import {
|
|
336
351
|
openai,
|
|
337
|
-
type
|
|
338
|
-
type OpenaiResponsesProviderMetadata,
|
|
352
|
+
type OpenAIResponsesSystemMessageOptions,
|
|
339
353
|
} from '@ai-sdk/openai';
|
|
340
|
-
import { generateText } from 'ai';
|
|
354
|
+
import { generateText, type ModelMessage } from 'ai';
|
|
355
|
+
|
|
356
|
+
const messages: ModelMessage[] = [
|
|
357
|
+
{ role: 'user', content: 'Draft a migration plan.' },
|
|
358
|
+
];
|
|
341
359
|
|
|
342
360
|
const first = await generateText({
|
|
343
361
|
model: openai.responses('gpt-6-astra'),
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
openai: {
|
|
347
|
-
reasoningEffort: 'low',
|
|
348
|
-
} satisfies OpenAILanguageModelResponsesOptions,
|
|
349
|
-
},
|
|
362
|
+
providerOptions: { openai: { reasoningEffort: 'low' } },
|
|
363
|
+
messages,
|
|
350
364
|
});
|
|
351
365
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
366
|
+
messages.push(...first.response.messages);
|
|
367
|
+
messages.push(
|
|
368
|
+
{
|
|
369
|
+
role: 'system',
|
|
370
|
+
content: '',
|
|
371
|
+
providerOptions: {
|
|
372
|
+
openai: {
|
|
373
|
+
reasoningEffortUpdate: 'high',
|
|
374
|
+
} satisfies OpenAIResponsesSystemMessageOptions,
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
{ role: 'user', content: 'Analyze the failure modes.' },
|
|
378
|
+
);
|
|
360
379
|
|
|
361
380
|
const second = await generateText({
|
|
362
381
|
model: openai.responses('gpt-6-astra'),
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
previousResponseId,
|
|
367
|
-
reasoningEffort: 'low',
|
|
368
|
-
reasoningEffortUpdate: 'high',
|
|
369
|
-
} satisfies OpenAILanguageModelResponsesOptions,
|
|
370
|
-
},
|
|
382
|
+
providerOptions: { openai: { reasoningEffort: 'low' } },
|
|
383
|
+
allowSystemInMessages: true,
|
|
384
|
+
messages,
|
|
371
385
|
});
|
|
386
|
+
|
|
387
|
+
messages.push(...second.response.messages);
|
|
372
388
|
```
|
|
373
389
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
390
|
+
<Note>
|
|
391
|
+
The OpenAI Responses provider sends each message-level effort update as a
|
|
392
|
+
`configuration_update` input item.
|
|
393
|
+
</Note>
|
|
394
|
+
|
|
395
|
+
Message-level updates also work with `streamText` and `previousResponseId`.
|
|
396
|
+
|
|
397
|
+
##### Restrictions
|
|
398
|
+
|
|
399
|
+
Effort updates are supported on GPT-6-family models with these settings:
|
|
400
|
+
|
|
401
|
+
| Setting | Required value |
|
|
402
|
+
| ------------------- | ---------------------- |
|
|
403
|
+
| `reasoningMode` | `'standard'` (default) |
|
|
404
|
+
| `truncation` | `'disabled'` (default) |
|
|
405
|
+
|
|
406
|
+
- Each update **must** use exactly `content: ''`.
|
|
407
|
+
- Updates cannot be consecutive, even with the same effort. This also applies
|
|
408
|
+
across the boundary with history referenced by previousResponseId or
|
|
409
|
+
conversation.
|
|
410
|
+
|
|
411
|
+
Requests that violate these restrictions are rejected.
|
|
412
|
+
|
|
413
|
+
<Note>
|
|
414
|
+
Effort update messages are sent even if `systemMessageMode` is set to
|
|
415
|
+
`remove`.
|
|
416
|
+
</Note>
|
|
417
|
+
|
|
418
|
+
##### Request-Level Update Option
|
|
419
|
+
|
|
420
|
+
The request-level `providerOptions.openai.reasoningEffortUpdate` option is a
|
|
421
|
+
convenience that prepends a configuration update to the input. When using
|
|
422
|
+
message-level updates, omit this option.
|
|
423
|
+
|
|
424
|
+
The request-level option continues to warn and omit updates for unsupported
|
|
425
|
+
models or configurations.
|
|
377
426
|
|
|
378
427
|
#### Reasoning Output
|
|
379
428
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.119",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@ai-sdk/provider": "3.0.
|
|
40
|
-
"@ai-sdk/provider-utils": "4.0.
|
|
39
|
+
"@ai-sdk/provider": "3.0.18",
|
|
40
|
+
"@ai-sdk/provider-utils": "4.0.55"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "20.17.24",
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { createOpenAI, openai } from './openai-provider';
|
|
|
2
2
|
export type { OpenAIProvider, OpenAIProviderSettings } from './openai-provider';
|
|
3
3
|
export type {
|
|
4
4
|
OpenAILanguageModelResponsesOptions,
|
|
5
|
+
OpenAIResponsesSystemMessageOptions,
|
|
5
6
|
/** @deprecated Use `OpenAILanguageModelResponsesOptions` instead. */
|
|
6
7
|
OpenAILanguageModelResponsesOptions as OpenAIResponsesProviderOptions,
|
|
7
8
|
} from './responses/openai-responses-options';
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type ToolNameMapping,
|
|
17
17
|
} from '@ai-sdk/provider-utils';
|
|
18
18
|
import { z } from 'zod/v4';
|
|
19
|
+
import { openaiResponsesSystemMessageOptionsSchema } from './openai-responses-options';
|
|
19
20
|
import {
|
|
20
21
|
applyPatchInputSchema,
|
|
21
22
|
applyPatchOutputSchema,
|
|
@@ -330,6 +331,7 @@ export async function convertToOpenAIResponsesInput({
|
|
|
330
331
|
store,
|
|
331
332
|
hasConversation = false,
|
|
332
333
|
hasPreviousResponseId = false,
|
|
334
|
+
configurationUpdateUnsupportedReason,
|
|
333
335
|
hasLocalShellTool = false,
|
|
334
336
|
hasShellTool = false,
|
|
335
337
|
hasApplyPatchTool = false,
|
|
@@ -346,6 +348,7 @@ export async function convertToOpenAIResponsesInput({
|
|
|
346
348
|
store: boolean;
|
|
347
349
|
hasConversation?: boolean; // when true, skip assistant messages that already have item IDs
|
|
348
350
|
hasPreviousResponseId?: boolean; // when true, skip reasoning and function-call items that already exist in the previous response chain
|
|
351
|
+
configurationUpdateUnsupportedReason?: string;
|
|
349
352
|
hasLocalShellTool?: boolean;
|
|
350
353
|
hasShellTool?: boolean;
|
|
351
354
|
hasApplyPatchTool?: boolean;
|
|
@@ -371,6 +374,42 @@ export async function convertToOpenAIResponsesInput({
|
|
|
371
374
|
for (const { role, content, providerOptions } of prompt) {
|
|
372
375
|
switch (role) {
|
|
373
376
|
case 'system': {
|
|
377
|
+
// Keep effort updates at their original positions so they apply to
|
|
378
|
+
// the same parts of the conversation when the history is sent again.
|
|
379
|
+
let options = await parseProviderOptions({
|
|
380
|
+
provider: providerOptionsName,
|
|
381
|
+
providerOptions,
|
|
382
|
+
schema: openaiResponsesSystemMessageOptionsSchema,
|
|
383
|
+
});
|
|
384
|
+
if (options == null && providerOptionsName !== 'openai') {
|
|
385
|
+
options = await parseProviderOptions({
|
|
386
|
+
provider: 'openai',
|
|
387
|
+
providerOptions,
|
|
388
|
+
schema: openaiResponsesSystemMessageOptionsSchema,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
const effort = options?.reasoningEffortUpdate;
|
|
392
|
+
if (effort != null) {
|
|
393
|
+
const unsupportedReason =
|
|
394
|
+
content !== ''
|
|
395
|
+
? 'Message-level reasoningEffortUpdate requires empty system message content.'
|
|
396
|
+
: configurationUpdateUnsupportedReason;
|
|
397
|
+
|
|
398
|
+
if (unsupportedReason != null) {
|
|
399
|
+
throw new UnsupportedFunctionalityError({
|
|
400
|
+
functionality: 'Message-level reasoningEffortUpdate',
|
|
401
|
+
message: unsupportedReason,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
input.push({
|
|
406
|
+
type: 'configuration_update',
|
|
407
|
+
reasoning: { effort },
|
|
408
|
+
});
|
|
409
|
+
// The control is independent of systemMessageMode's text handling.
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
|
|
374
413
|
switch (systemMessageMode) {
|
|
375
414
|
case 'system': {
|
|
376
415
|
const promptCacheBreakpoint = getPromptCacheBreakpoint(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
UnsupportedFunctionalityError,
|
|
2
3
|
APICallError,
|
|
3
4
|
type JSONValue,
|
|
4
5
|
type LanguageModelV3,
|
|
@@ -72,6 +73,7 @@ import {
|
|
|
72
73
|
import {
|
|
73
74
|
openaiLanguageModelResponsesOptionsSchema,
|
|
74
75
|
TOP_LOGPROBS_MAX,
|
|
76
|
+
type OpenAILanguageModelResponsesOptions,
|
|
75
77
|
type OpenAIResponsesModelId,
|
|
76
78
|
} from './openai-responses-options';
|
|
77
79
|
import { prepareResponsesTools } from './openai-responses-prepare-tools';
|
|
@@ -108,6 +110,30 @@ function extractApprovalRequestIdToToolCallIdMapping(
|
|
|
108
110
|
return mapping;
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Enforces OpenAI's configuration update restrictions for reasoningEffortUpdate,
|
|
115
|
+
* returning a string describing the unsupported reason if any.
|
|
116
|
+
*
|
|
117
|
+
* @see https://developers.openai.com/api/docs/guides/reasoning#change-reasoning-mid-conversation
|
|
118
|
+
*/
|
|
119
|
+
function getConfigurationUpdateUnsupportedReason({
|
|
120
|
+
modelCapabilities,
|
|
121
|
+
options,
|
|
122
|
+
}: {
|
|
123
|
+
modelCapabilities: { supportsConfigurationUpdate: boolean };
|
|
124
|
+
options: OpenAILanguageModelResponsesOptions | undefined;
|
|
125
|
+
}): string | undefined {
|
|
126
|
+
if (!modelCapabilities.supportsConfigurationUpdate) {
|
|
127
|
+
return 'reasoningEffortUpdate is only supported by GPT-6 and later models';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (options?.reasoningMode === 'pro' || options?.truncation === 'auto') {
|
|
131
|
+
return 'reasoningEffortUpdate requires standard reasoning mode without automatic truncation';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
111
137
|
export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
112
138
|
readonly specificationVersion = 'v3';
|
|
113
139
|
|
|
@@ -246,9 +272,16 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
246
272
|
supportsAsyncToolCalling: modelCapabilities.supportsAsyncToolCalling,
|
|
247
273
|
});
|
|
248
274
|
|
|
275
|
+
const configurationUpdateUnsupportedReason =
|
|
276
|
+
getConfigurationUpdateUnsupportedReason({
|
|
277
|
+
modelCapabilities,
|
|
278
|
+
options: openaiOptions,
|
|
279
|
+
});
|
|
280
|
+
|
|
249
281
|
const { input, warnings: inputWarnings } =
|
|
250
282
|
await convertToOpenAIResponsesInput({
|
|
251
283
|
prompt,
|
|
284
|
+
configurationUpdateUnsupportedReason,
|
|
252
285
|
toolNameMapping,
|
|
253
286
|
systemMessageMode:
|
|
254
287
|
openaiOptions?.systemMessageMode ??
|
|
@@ -276,25 +309,41 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
276
309
|
warnings.push(...inputWarnings);
|
|
277
310
|
|
|
278
311
|
const reasoningEffortUpdate = openaiOptions?.reasoningEffortUpdate;
|
|
279
|
-
|
|
280
|
-
reasoningEffortUpdate
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
openaiOptions?.truncation !== 'auto');
|
|
284
|
-
|
|
285
|
-
if (reasoningEffortUpdate != null && !configurationUpdateIsSupported) {
|
|
312
|
+
if (
|
|
313
|
+
reasoningEffortUpdate != null &&
|
|
314
|
+
configurationUpdateUnsupportedReason != null
|
|
315
|
+
) {
|
|
286
316
|
warnings.push({
|
|
287
317
|
type: 'unsupported',
|
|
288
318
|
feature: 'reasoningEffortUpdate',
|
|
289
|
-
details:
|
|
290
|
-
? 'reasoningEffortUpdate is only supported by GPT-6 and later models'
|
|
291
|
-
: 'reasoningEffortUpdate requires standard reasoning mode without automatic truncation',
|
|
319
|
+
details: configurationUpdateUnsupportedReason,
|
|
292
320
|
});
|
|
293
321
|
} else if (reasoningEffortUpdate != null) {
|
|
294
|
-
input
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
322
|
+
const firstItem = input[0];
|
|
323
|
+
// If the first item already sets this effort, prepending another update
|
|
324
|
+
// would create an adjacent pair that OpenAI rejects.
|
|
325
|
+
if (
|
|
326
|
+
firstItem?.type !== 'configuration_update' ||
|
|
327
|
+
firstItem.reasoning.effort !== reasoningEffortUpdate
|
|
328
|
+
) {
|
|
329
|
+
input.unshift({
|
|
330
|
+
type: 'configuration_update',
|
|
331
|
+
reasoning: { effort: reasoningEffortUpdate },
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Conversion and prepending can make updates adjacent, so check afterward
|
|
337
|
+
// to catch combinations that OpenAI would reject.
|
|
338
|
+
for (let i = 1; i < input.length; i++) {
|
|
339
|
+
if (
|
|
340
|
+
input[i - 1].type === 'configuration_update' &&
|
|
341
|
+
input[i].type === 'configuration_update'
|
|
342
|
+
) {
|
|
343
|
+
throw new UnsupportedFunctionalityError({
|
|
344
|
+
functionality: 'Adjacent reasoning effort configuration updates',
|
|
345
|
+
});
|
|
346
|
+
}
|
|
298
347
|
}
|
|
299
348
|
|
|
300
349
|
const strictJsonSchema = openaiOptions?.strictJsonSchema ?? true;
|
|
@@ -405,3 +405,25 @@ export const openaiLanguageModelResponsesOptionsSchema = lazySchema(() =>
|
|
|
405
405
|
export type OpenAILanguageModelResponsesOptions = InferSchema<
|
|
406
406
|
typeof openaiLanguageModelResponsesOptionsSchema
|
|
407
407
|
>;
|
|
408
|
+
|
|
409
|
+
export const openaiResponsesSystemMessageOptionsSchema = lazySchema(() =>
|
|
410
|
+
zodSchema(
|
|
411
|
+
z.object({
|
|
412
|
+
/**
|
|
413
|
+
* Emit a configuration update at this position in Responses history.
|
|
414
|
+
* Requires empty system message content and the same supported
|
|
415
|
+
* configuration as the request-level reasoningEffortUpdate option.
|
|
416
|
+
* Unsupported historical updates throw instead of being omitted.
|
|
417
|
+
*
|
|
418
|
+
* @see https://developers.openai.com/api/docs/guides/reasoning#change-reasoning-mid-conversation
|
|
419
|
+
*/
|
|
420
|
+
reasoningEffortUpdate: z
|
|
421
|
+
.enum(['low', 'medium', 'high', 'xhigh', 'max'])
|
|
422
|
+
.optional(),
|
|
423
|
+
}),
|
|
424
|
+
),
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
export type OpenAIResponsesSystemMessageOptions = InferSchema<
|
|
428
|
+
typeof openaiResponsesSystemMessageOptionsSchema
|
|
429
|
+
>;
|