@ai-sdk/openai 4.0.74 → 4.0.75
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 +6 -0
- package/README.md +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +1382 -1312
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +1574 -1504
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +156 -93
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/responses/convert-to-openai-responses-input.ts +39 -0
- package/src/responses/openai-responses-language-model-options.ts +22 -0
- package/src/responses/openai-responses-language-model.ts +76 -24
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export { OpenAIRealtimeModel as Experimental_OpenAIRealtimeModel } from './realt
|
|
|
14
14
|
export type { OpenAIRealtimeModelConfig as Experimental_OpenAIRealtimeModelConfig } from './realtime/openai-realtime-model';
|
|
15
15
|
export type {
|
|
16
16
|
OpenAILanguageModelResponsesOptions,
|
|
17
|
+
OpenAIResponsesSystemMessageOptions,
|
|
17
18
|
/** @deprecated Use `OpenAILanguageModelResponsesOptions` instead. */
|
|
18
19
|
OpenAILanguageModelResponsesOptions as OpenAIResponsesProviderOptions,
|
|
19
20
|
} from './responses/openai-responses-language-model-options';
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type ToolNameMapping,
|
|
20
20
|
} from '@ai-sdk/provider-utils';
|
|
21
21
|
import { z } from 'zod/v4';
|
|
22
|
+
import { openaiResponsesSystemMessageOptionsSchema } from './openai-responses-language-model-options';
|
|
22
23
|
import {
|
|
23
24
|
applyPatchInputSchema,
|
|
24
25
|
applyPatchOutputSchema,
|
|
@@ -380,6 +381,7 @@ export async function convertToOpenAIResponsesInput({
|
|
|
380
381
|
toolSearchToolName,
|
|
381
382
|
customProviderToolNames,
|
|
382
383
|
outputSchemaToolNames,
|
|
384
|
+
configurationUpdateUnsupportedReason,
|
|
383
385
|
}: {
|
|
384
386
|
prompt: LanguageModelV4Prompt;
|
|
385
387
|
toolNameMapping: ToolNameMapping;
|
|
@@ -399,6 +401,7 @@ export async function convertToOpenAIResponsesInput({
|
|
|
399
401
|
toolSearchToolName?: string;
|
|
400
402
|
customProviderToolNames?: Set<string>;
|
|
401
403
|
outputSchemaToolNames?: Set<string>;
|
|
404
|
+
configurationUpdateUnsupportedReason?: string;
|
|
402
405
|
}): Promise<{
|
|
403
406
|
input: OpenAIResponsesInput;
|
|
404
407
|
warnings: Array<SharedV4Warning>;
|
|
@@ -420,6 +423,42 @@ export async function convertToOpenAIResponsesInput({
|
|
|
420
423
|
for (const { role, content, providerOptions } of prompt) {
|
|
421
424
|
switch (role) {
|
|
422
425
|
case 'system': {
|
|
426
|
+
// Keep effort updates at their original positions so they apply to
|
|
427
|
+
// the same parts of the conversation when the history is sent again.
|
|
428
|
+
let options = await parseProviderOptions({
|
|
429
|
+
provider: providerOptionsName,
|
|
430
|
+
providerOptions,
|
|
431
|
+
schema: openaiResponsesSystemMessageOptionsSchema,
|
|
432
|
+
});
|
|
433
|
+
if (options == null && providerOptionsName !== 'openai') {
|
|
434
|
+
options = await parseProviderOptions({
|
|
435
|
+
provider: 'openai',
|
|
436
|
+
providerOptions,
|
|
437
|
+
schema: openaiResponsesSystemMessageOptionsSchema,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
const effort = options?.reasoningEffortUpdate;
|
|
441
|
+
if (effort != null) {
|
|
442
|
+
const unsupportedReason =
|
|
443
|
+
content !== ''
|
|
444
|
+
? 'Message-level reasoningEffortUpdate requires empty system message content.'
|
|
445
|
+
: configurationUpdateUnsupportedReason;
|
|
446
|
+
|
|
447
|
+
if (unsupportedReason != null) {
|
|
448
|
+
throw new UnsupportedFunctionalityError({
|
|
449
|
+
functionality: 'Message-level reasoningEffortUpdate',
|
|
450
|
+
message: unsupportedReason,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
input.push({
|
|
455
|
+
type: 'configuration_update',
|
|
456
|
+
reasoning: { effort },
|
|
457
|
+
});
|
|
458
|
+
// The control is independent of systemMessageMode's text handling.
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
|
|
423
462
|
switch (systemMessageMode) {
|
|
424
463
|
case 'system': {
|
|
425
464
|
const promptCacheBreakpoint = getPromptCacheBreakpoint(
|
|
@@ -432,3 +432,25 @@ export const openaiLanguageModelResponsesOptionsSchema = lazySchema(() =>
|
|
|
432
432
|
export type OpenAILanguageModelResponsesOptions = InferSchema<
|
|
433
433
|
typeof openaiLanguageModelResponsesOptionsSchema
|
|
434
434
|
>;
|
|
435
|
+
|
|
436
|
+
export const openaiResponsesSystemMessageOptionsSchema = lazySchema(() =>
|
|
437
|
+
zodSchema(
|
|
438
|
+
z.object({
|
|
439
|
+
/**
|
|
440
|
+
* Emit a configuration update at this position in Responses history.
|
|
441
|
+
* Requires empty system message content and the same supported
|
|
442
|
+
* configuration as the request-level reasoningEffortUpdate option.
|
|
443
|
+
* Unsupported historical updates throw instead of being omitted.
|
|
444
|
+
*
|
|
445
|
+
* @see https://developers.openai.com/api/docs/guides/reasoning#change-reasoning-mid-conversation
|
|
446
|
+
*/
|
|
447
|
+
reasoningEffortUpdate: z
|
|
448
|
+
.enum(['low', 'medium', 'high', 'xhigh', 'max'])
|
|
449
|
+
.optional(),
|
|
450
|
+
}),
|
|
451
|
+
),
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
export type OpenAIResponsesSystemMessageOptions = InferSchema<
|
|
455
|
+
typeof openaiResponsesSystemMessageOptionsSchema
|
|
456
|
+
>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
APICallError,
|
|
3
|
+
UnsupportedFunctionalityError,
|
|
3
4
|
type JSONValue,
|
|
4
5
|
type LanguageModelV4,
|
|
5
|
-
type LanguageModelV4Prompt,
|
|
6
6
|
type LanguageModelV4CallOptions,
|
|
7
7
|
type LanguageModelV4Content,
|
|
8
8
|
type LanguageModelV4FinishReason,
|
|
9
9
|
type LanguageModelV4FunctionTool,
|
|
10
10
|
type LanguageModelV4GenerateResult,
|
|
11
|
+
type LanguageModelV4Prompt,
|
|
11
12
|
type LanguageModelV4ProviderTool,
|
|
12
13
|
type LanguageModelV4StreamPart,
|
|
13
14
|
type LanguageModelV4StreamResult,
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
type InferSchema,
|
|
31
32
|
type ParseResult,
|
|
32
33
|
} from '@ai-sdk/provider-utils';
|
|
34
|
+
import { normalizeOpenAIJsonSchema } from '../normalize-openai-json-schema';
|
|
33
35
|
import {
|
|
34
36
|
prepareOpenAIConfigForWorkflowDeserialize,
|
|
35
37
|
type OpenAIConfig,
|
|
@@ -50,15 +52,15 @@ import type { fileSearchOutputSchema } from '../tool/file-search';
|
|
|
50
52
|
import type { imageGenerationOutputSchema } from '../tool/image-generation';
|
|
51
53
|
import type { localShellInputSchema } from '../tool/local-shell';
|
|
52
54
|
import type { mcpOutputSchema } from '../tool/mcp';
|
|
55
|
+
import type {
|
|
56
|
+
programmaticToolCallingInputSchema,
|
|
57
|
+
programmaticToolCallingOutputSchema,
|
|
58
|
+
} from '../tool/programmatic-tool-calling';
|
|
53
59
|
import type { shellInputSchema, shellOutputSchema } from '../tool/shell';
|
|
54
60
|
import type {
|
|
55
61
|
toolSearchInputSchema,
|
|
56
62
|
toolSearchOutputSchema,
|
|
57
63
|
} from '../tool/tool-search';
|
|
58
|
-
import type {
|
|
59
|
-
programmaticToolCallingInputSchema,
|
|
60
|
-
programmaticToolCallingOutputSchema,
|
|
61
|
-
} from '../tool/programmatic-tool-calling';
|
|
62
64
|
import type { webSearchOutputSchema } from '../tool/web-search';
|
|
63
65
|
import {
|
|
64
66
|
convertOpenAIResponsesUsage,
|
|
@@ -70,22 +72,22 @@ import {
|
|
|
70
72
|
isUndeclaredParallelToolCall,
|
|
71
73
|
} from './expand-parallel-tool-call';
|
|
72
74
|
import { mapOpenAIResponseFinishReason } from './map-openai-responses-finish-reason';
|
|
73
|
-
import { normalizeOpenAIJsonSchema } from '../normalize-openai-json-schema';
|
|
74
75
|
import {
|
|
75
76
|
openaiResponsesChunkSchema,
|
|
76
77
|
openaiResponsesResponseSchema,
|
|
78
|
+
type OpenAIResponsesApplyPatchOperationDiffDeltaChunk,
|
|
79
|
+
type OpenAIResponsesApplyPatchOperationDiffDoneChunk,
|
|
77
80
|
type OpenAIResponsesChunk,
|
|
81
|
+
type OpenAIResponsesComputerAction,
|
|
78
82
|
type OpenAIResponsesIncludeOptions,
|
|
79
83
|
type OpenAIResponsesIncludeValue,
|
|
80
84
|
type OpenAIResponsesLogprobs,
|
|
81
85
|
type OpenAIResponsesWebSearchAction,
|
|
82
|
-
type OpenAIResponsesApplyPatchOperationDiffDeltaChunk,
|
|
83
|
-
type OpenAIResponsesApplyPatchOperationDiffDoneChunk,
|
|
84
|
-
type OpenAIResponsesComputerAction,
|
|
85
86
|
} from './openai-responses-api';
|
|
86
87
|
import {
|
|
87
88
|
openaiLanguageModelResponsesOptionsSchema,
|
|
88
89
|
TOP_LOGPROBS_MAX,
|
|
90
|
+
type OpenAILanguageModelResponsesOptions,
|
|
89
91
|
type OpenAIResponsesModelId,
|
|
90
92
|
} from './openai-responses-language-model-options';
|
|
91
93
|
import { prepareResponsesTools } from './openai-responses-prepare-tools';
|
|
@@ -209,6 +211,34 @@ export const openaiResponsesSupportedUrls: Record<string, RegExp[]> = {
|
|
|
209
211
|
'application/pdf': [/^https?:\/\/.*$/],
|
|
210
212
|
};
|
|
211
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Enforces OpenAI's configuration update restrictions for reasoningEffortUpdate,
|
|
216
|
+
* returning a string describing the unsupported reason if any.
|
|
217
|
+
*
|
|
218
|
+
* @see https://developers.openai.com/api/docs/guides/reasoning#change-reasoning-mid-conversation
|
|
219
|
+
*/
|
|
220
|
+
function getConfigurationUpdateUnsupportedReason({
|
|
221
|
+
modelCapabilities,
|
|
222
|
+
options,
|
|
223
|
+
}: {
|
|
224
|
+
modelCapabilities: { supportsConfigurationUpdate: boolean };
|
|
225
|
+
options: OpenAILanguageModelResponsesOptions | undefined;
|
|
226
|
+
}): string | undefined {
|
|
227
|
+
if (!modelCapabilities.supportsConfigurationUpdate) {
|
|
228
|
+
return 'reasoningEffortUpdate is only supported by GPT-6 and later models';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (
|
|
232
|
+
options?.reasoningMode === 'pro' ||
|
|
233
|
+
options?.contextManagement != null ||
|
|
234
|
+
options?.truncation === 'auto'
|
|
235
|
+
) {
|
|
236
|
+
return 'reasoningEffortUpdate requires standard reasoning mode without automatic compaction or automatic truncation';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
|
|
212
242
|
export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
213
243
|
readonly specificationVersion = 'v4';
|
|
214
244
|
|
|
@@ -382,6 +412,12 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
382
412
|
supportsAsyncToolCalling: modelCapabilities.supportsAsyncToolCalling,
|
|
383
413
|
});
|
|
384
414
|
|
|
415
|
+
const configurationUpdateUnsupportedReason =
|
|
416
|
+
getConfigurationUpdateUnsupportedReason({
|
|
417
|
+
modelCapabilities,
|
|
418
|
+
options: openaiOptions,
|
|
419
|
+
});
|
|
420
|
+
|
|
385
421
|
const { input, warnings: inputWarnings } =
|
|
386
422
|
await convertToOpenAIResponsesInput({
|
|
387
423
|
prompt,
|
|
@@ -392,6 +428,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
392
428
|
? 'developer'
|
|
393
429
|
: modelCapabilities.systemMessageMode),
|
|
394
430
|
providerOptionsName,
|
|
431
|
+
configurationUpdateUnsupportedReason,
|
|
395
432
|
explicitMessageItemType: config.explicitMessageItemType,
|
|
396
433
|
fileIdPrefixes: config.fileIdPrefixes,
|
|
397
434
|
passThroughUnsupportedFiles:
|
|
@@ -415,26 +452,41 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
415
452
|
warnings.push(...inputWarnings);
|
|
416
453
|
|
|
417
454
|
const reasoningEffortUpdate = openaiOptions?.reasoningEffortUpdate;
|
|
418
|
-
|
|
419
|
-
reasoningEffortUpdate
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
openaiOptions?.contextManagement == null &&
|
|
423
|
-
openaiOptions?.truncation !== 'auto');
|
|
424
|
-
|
|
425
|
-
if (reasoningEffortUpdate != null && !configurationUpdateIsSupported) {
|
|
455
|
+
if (
|
|
456
|
+
reasoningEffortUpdate != null &&
|
|
457
|
+
configurationUpdateUnsupportedReason != null
|
|
458
|
+
) {
|
|
426
459
|
warnings.push({
|
|
427
460
|
type: 'unsupported',
|
|
428
461
|
feature: 'reasoningEffortUpdate',
|
|
429
|
-
details:
|
|
430
|
-
? 'reasoningEffortUpdate is only supported by GPT-6 and later models'
|
|
431
|
-
: 'reasoningEffortUpdate requires standard reasoning mode without automatic compaction or automatic truncation',
|
|
462
|
+
details: configurationUpdateUnsupportedReason,
|
|
432
463
|
});
|
|
433
464
|
} else if (reasoningEffortUpdate != null) {
|
|
434
|
-
input
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
465
|
+
const firstItem = input[0];
|
|
466
|
+
// If the first item already sets this effort, prepending another update
|
|
467
|
+
// would create an adjacent pair that OpenAI rejects.
|
|
468
|
+
if (
|
|
469
|
+
firstItem?.type !== 'configuration_update' ||
|
|
470
|
+
firstItem.reasoning.effort !== reasoningEffortUpdate
|
|
471
|
+
) {
|
|
472
|
+
input.unshift({
|
|
473
|
+
type: 'configuration_update',
|
|
474
|
+
reasoning: { effort: reasoningEffortUpdate },
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Conversion and prepending can make updates adjacent, so check afterward
|
|
480
|
+
// to catch combinations that OpenAI would reject.
|
|
481
|
+
for (let i = 1; i < input.length; i++) {
|
|
482
|
+
if (
|
|
483
|
+
input[i - 1].type === 'configuration_update' &&
|
|
484
|
+
input[i].type === 'configuration_update'
|
|
485
|
+
) {
|
|
486
|
+
throw new UnsupportedFunctionalityError({
|
|
487
|
+
functionality: 'Adjacent reasoning effort configuration updates',
|
|
488
|
+
});
|
|
489
|
+
}
|
|
438
490
|
}
|
|
439
491
|
|
|
440
492
|
// A compaction trigger is a request control, not conversation history.
|