@ai-sdk/openai 4.0.109 → 4.0.111
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 +35 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +206 -68
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +316 -6
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +195 -67
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +68 -0
- package/package.json +3 -3
- package/src/chat/map-openai-finish-reason.ts +11 -4
- package/src/chat/openai-chat-api.ts +36 -0
- package/src/chat/openai-chat-language-model.ts +120 -20
- package/src/completion/openai-completion-language-model.ts +11 -13
- package/src/image/openai-image-model-options.ts +68 -57
- package/src/internal/index.ts +2 -0
- package/src/openai-batch.ts +8 -0
- package/src/responses/map-openai-responses-finish-reason.ts +7 -1
- package/src/responses/openai-responses-api.ts +71 -48
- package/src/responses/openai-responses-language-model.ts +62 -12
- package/src/speech/openai-speech-model-options.ts +11 -6
- package/src/transcription/openai-transcription-model-options.ts +71 -70
|
@@ -869,6 +869,8 @@ const openaiResponsesModeledChunkTypes = new Set([
|
|
|
869
869
|
'response.output_item.done',
|
|
870
870
|
'response.output_text.annotation.added',
|
|
871
871
|
'response.output_text.delta',
|
|
872
|
+
'response.refusal.delta',
|
|
873
|
+
'response.refusal.done',
|
|
872
874
|
'response.reasoning_summary_part.added',
|
|
873
875
|
'response.reasoning_summary_part.done',
|
|
874
876
|
'response.reasoning_summary_text.delta',
|
|
@@ -948,6 +950,20 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
|
|
|
948
950
|
)
|
|
949
951
|
.nullish(),
|
|
950
952
|
}),
|
|
953
|
+
z.object({
|
|
954
|
+
type: z.literal('response.refusal.delta'),
|
|
955
|
+
item_id: z.string(),
|
|
956
|
+
output_index: z.number().nullish(),
|
|
957
|
+
content_index: z.number().nullish(),
|
|
958
|
+
delta: z.string(),
|
|
959
|
+
}),
|
|
960
|
+
z.object({
|
|
961
|
+
type: z.literal('response.refusal.done'),
|
|
962
|
+
item_id: z.string(),
|
|
963
|
+
output_index: z.number().nullish(),
|
|
964
|
+
content_index: z.number().nullish(),
|
|
965
|
+
refusal: z.string(),
|
|
966
|
+
}),
|
|
951
967
|
z.object({
|
|
952
968
|
type: z.enum(['response.completed', 'response.incomplete']),
|
|
953
969
|
response: z.object({
|
|
@@ -1548,55 +1564,62 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
|
|
|
1548
1564
|
role: z.literal('assistant'),
|
|
1549
1565
|
id: z.string(),
|
|
1550
1566
|
phase: z.enum(['commentary', 'final_answer']).nullish(),
|
|
1567
|
+
// the API returns either text or a refusal as content parts:
|
|
1551
1568
|
content: z.array(
|
|
1552
|
-
z.
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
z.
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
z.
|
|
1571
|
-
z.
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1569
|
+
z.discriminatedUnion('type', [
|
|
1570
|
+
z.object({
|
|
1571
|
+
type: z.literal('output_text'),
|
|
1572
|
+
text: z.string(),
|
|
1573
|
+
logprobs: z
|
|
1574
|
+
.array(
|
|
1575
|
+
z.object({
|
|
1576
|
+
token: z.string(),
|
|
1577
|
+
logprob: z.number(),
|
|
1578
|
+
top_logprobs: z.array(
|
|
1579
|
+
z.object({
|
|
1580
|
+
token: z.string(),
|
|
1581
|
+
logprob: z.number(),
|
|
1582
|
+
}),
|
|
1583
|
+
),
|
|
1584
|
+
}),
|
|
1585
|
+
)
|
|
1586
|
+
.nullish(),
|
|
1587
|
+
annotations: z.array(
|
|
1588
|
+
z.discriminatedUnion('type', [
|
|
1589
|
+
z.object({
|
|
1590
|
+
type: z.literal('url_citation'),
|
|
1591
|
+
start_index: z.number(),
|
|
1592
|
+
end_index: z.number(),
|
|
1593
|
+
url: z.string(),
|
|
1594
|
+
title: z.string(),
|
|
1595
|
+
}),
|
|
1596
|
+
z.object({
|
|
1597
|
+
type: z.literal('file_citation'),
|
|
1598
|
+
file_id: z.string(),
|
|
1599
|
+
filename: z.string(),
|
|
1600
|
+
index: z.number(),
|
|
1601
|
+
}),
|
|
1602
|
+
z.object({
|
|
1603
|
+
type: z.literal('container_file_citation'),
|
|
1604
|
+
container_id: z.string(),
|
|
1605
|
+
file_id: z.string(),
|
|
1606
|
+
filename: z.string(),
|
|
1607
|
+
start_index: z.number(),
|
|
1608
|
+
end_index: z.number(),
|
|
1609
|
+
}),
|
|
1610
|
+
z.object({
|
|
1611
|
+
type: z.literal('file_path'),
|
|
1612
|
+
file_id: z.string(),
|
|
1613
|
+
index: z.number(),
|
|
1614
|
+
}),
|
|
1615
|
+
]),
|
|
1616
|
+
),
|
|
1617
|
+
}),
|
|
1618
|
+
z.object({
|
|
1619
|
+
type: z.literal('refusal'),
|
|
1620
|
+
refusal: z.string(),
|
|
1621
|
+
}),
|
|
1622
|
+
]),
|
|
1600
1623
|
),
|
|
1601
1624
|
}),
|
|
1602
1625
|
z.object({
|
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
createJsonResponseHandler,
|
|
24
24
|
createToolNameMapping,
|
|
25
25
|
generateId,
|
|
26
|
-
parseProviderOptions,
|
|
27
26
|
postJsonToApi,
|
|
27
|
+
resolveProviderOptions,
|
|
28
28
|
serializeModelOptions,
|
|
29
29
|
WORKFLOW_DESERIALIZE,
|
|
30
30
|
WORKFLOW_SERIALIZE,
|
|
@@ -331,20 +331,15 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
331
331
|
const providerOptionsName = config.provider.includes('azure')
|
|
332
332
|
? 'azure'
|
|
333
333
|
: 'openai';
|
|
334
|
-
|
|
335
|
-
|
|
334
|
+
// The provider key (`azure`) and `openai` are alternatives: the first
|
|
335
|
+
// present one is used as a whole.
|
|
336
|
+
const { options: openaiOptions } = await resolveProviderOptions({
|
|
336
337
|
providerOptions,
|
|
338
|
+
namespaces: [providerOptionsName, 'openai'],
|
|
339
|
+
resolution: 'first',
|
|
337
340
|
schema: openaiLanguageModelResponsesOptionsSchema,
|
|
338
341
|
});
|
|
339
342
|
|
|
340
|
-
if (openaiOptions == null && providerOptionsName !== 'openai') {
|
|
341
|
-
openaiOptions = await parseProviderOptions({
|
|
342
|
-
provider: 'openai',
|
|
343
|
-
providerOptions,
|
|
344
|
-
schema: openaiLanguageModelResponsesOptionsSchema,
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
|
|
348
343
|
const resolvedReasoningEffort = resolveOpenAIReasoningEffort({
|
|
349
344
|
providerReasoningEffort: openaiOptions?.reasoningEffort,
|
|
350
345
|
reasoning,
|
|
@@ -923,6 +918,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
923
918
|
let hasFunctionCall = false;
|
|
924
919
|
const hostedToolSearchCallIds: string[] = [];
|
|
925
920
|
|
|
921
|
+
// flag that checks if the model refused to answer:
|
|
922
|
+
let hasRefusal = false;
|
|
923
|
+
|
|
926
924
|
let structuredOutputText: string | undefined;
|
|
927
925
|
|
|
928
926
|
// map response content to content array
|
|
@@ -1092,11 +1090,32 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1092
1090
|
part.phase === 'final_answer'
|
|
1093
1091
|
) {
|
|
1094
1092
|
structuredOutputText = part.content
|
|
1095
|
-
.map(contentPart =>
|
|
1093
|
+
.map(contentPart =>
|
|
1094
|
+
contentPart.type === 'output_text'
|
|
1095
|
+
? contentPart.text
|
|
1096
|
+
: contentPart.refusal,
|
|
1097
|
+
)
|
|
1096
1098
|
.join('');
|
|
1097
1099
|
}
|
|
1098
1100
|
|
|
1099
1101
|
for (const contentPart of part.content) {
|
|
1102
|
+
if (contentPart.type === 'refusal') {
|
|
1103
|
+
hasRefusal = true;
|
|
1104
|
+
|
|
1105
|
+
content.push({
|
|
1106
|
+
type: 'text',
|
|
1107
|
+
text: contentPart.refusal,
|
|
1108
|
+
providerMetadata: {
|
|
1109
|
+
[providerOptionsName]: {
|
|
1110
|
+
itemId: part.id,
|
|
1111
|
+
...(part.phase != null && { phase: part.phase }),
|
|
1112
|
+
} satisfies ResponsesTextProviderMetadata,
|
|
1113
|
+
},
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1100
1119
|
if (
|
|
1101
1120
|
options.providerOptions?.[providerOptionsName]?.logprobs &&
|
|
1102
1121
|
contentPart.logprobs
|
|
@@ -1529,6 +1548,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1529
1548
|
unified: mapOpenAIResponseFinishReason({
|
|
1530
1549
|
finishReason: response.incomplete_details?.reason,
|
|
1531
1550
|
hasFunctionCall,
|
|
1551
|
+
hasRefusal,
|
|
1532
1552
|
}),
|
|
1533
1553
|
raw: response.incomplete_details?.reason ?? undefined,
|
|
1534
1554
|
},
|
|
@@ -1658,6 +1678,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1658
1678
|
// flag that checks if there have been client-side tool calls (not executed by openai)
|
|
1659
1679
|
let hasFunctionCall = false;
|
|
1660
1680
|
|
|
1681
|
+
// flag that checks if the model refused to answer:
|
|
1682
|
+
let hasRefusal = false;
|
|
1683
|
+
|
|
1661
1684
|
const activeReasoning: Record<
|
|
1662
1685
|
string,
|
|
1663
1686
|
{
|
|
@@ -2772,6 +2795,20 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
2772
2795
|
) {
|
|
2773
2796
|
logprobs.push(value.logprobs);
|
|
2774
2797
|
}
|
|
2798
|
+
} else if (isRefusalDeltaChunk(value)) {
|
|
2799
|
+
hasRefusal = true;
|
|
2800
|
+
|
|
2801
|
+
const itemId = resolveOutputItemId({
|
|
2802
|
+
itemId: value.item_id,
|
|
2803
|
+
outputIndex: value.output_index,
|
|
2804
|
+
});
|
|
2805
|
+
controller.enqueue({
|
|
2806
|
+
type: 'text-delta',
|
|
2807
|
+
id: itemId,
|
|
2808
|
+
delta: value.delta,
|
|
2809
|
+
});
|
|
2810
|
+
} else if (isRefusalDoneChunk(value)) {
|
|
2811
|
+
hasRefusal = true;
|
|
2775
2812
|
} else if (value.type === 'response.reasoning_summary_part.added') {
|
|
2776
2813
|
const itemId = resolveOutputItemId({
|
|
2777
2814
|
itemId: value.item_id,
|
|
@@ -2872,6 +2909,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
2872
2909
|
unified: mapOpenAIResponseFinishReason({
|
|
2873
2910
|
finishReason: value.response.incomplete_details?.reason,
|
|
2874
2911
|
hasFunctionCall,
|
|
2912
|
+
hasRefusal,
|
|
2875
2913
|
}),
|
|
2876
2914
|
raw: value.response.incomplete_details?.reason ?? undefined,
|
|
2877
2915
|
};
|
|
@@ -3014,6 +3052,18 @@ function isTextDeltaChunk(
|
|
|
3014
3052
|
return chunk.type === 'response.output_text.delta';
|
|
3015
3053
|
}
|
|
3016
3054
|
|
|
3055
|
+
function isRefusalDeltaChunk(
|
|
3056
|
+
chunk: OpenAIResponsesChunk,
|
|
3057
|
+
): chunk is OpenAIResponsesChunk & { type: 'response.refusal.delta' } {
|
|
3058
|
+
return chunk.type === 'response.refusal.delta';
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
function isRefusalDoneChunk(
|
|
3062
|
+
chunk: OpenAIResponsesChunk,
|
|
3063
|
+
): chunk is OpenAIResponsesChunk & { type: 'response.refusal.done' } {
|
|
3064
|
+
return chunk.type === 'response.refusal.done';
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3017
3067
|
function isOpenAIChatCompletionChunk(value: unknown): boolean {
|
|
3018
3068
|
const chunk = asRecord(value);
|
|
3019
3069
|
|
|
@@ -16,13 +16,18 @@ export type OpenAISpeechModelId =
|
|
|
16
16
|
| (string & {});
|
|
17
17
|
|
|
18
18
|
// https://platform.openai.com/docs/api-reference/audio/createSpeech
|
|
19
|
+
/**
|
|
20
|
+
* Shape of the OpenAI speech model options. Exported so that routers that
|
|
21
|
+
* also call the OpenAI speech API (e.g. Azure) can accept these options under
|
|
22
|
+
* their own providerOptions key.
|
|
23
|
+
*/
|
|
24
|
+
export const openaiSpeechModelOptionsShape = () => ({
|
|
25
|
+
instructions: z.string().nullish(),
|
|
26
|
+
speed: z.number().min(0.25).max(4.0).nullish(),
|
|
27
|
+
});
|
|
28
|
+
|
|
19
29
|
export const openaiSpeechModelOptionsSchema = lazySchema(() =>
|
|
20
|
-
zodSchema(
|
|
21
|
-
z.object({
|
|
22
|
-
instructions: z.string().nullish(),
|
|
23
|
-
speed: z.number().min(0.25).max(4.0).nullish(),
|
|
24
|
-
}),
|
|
25
|
-
),
|
|
30
|
+
zodSchema(z.object(openaiSpeechModelOptionsShape())),
|
|
26
31
|
);
|
|
27
32
|
|
|
28
33
|
export type OpenAISpeechModelOptions = InferSchema<
|
|
@@ -18,91 +18,92 @@ export type OpenAITranscriptionModelId =
|
|
|
18
18
|
| (string & {});
|
|
19
19
|
|
|
20
20
|
// https://platform.openai.com/docs/api-reference/audio/createTranscription
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Shape of the OpenAI transcription model options. Exported so that routers
|
|
23
|
+
* that also call the OpenAI transcription API (e.g. Azure) can accept these
|
|
24
|
+
* options under their own providerOptions key.
|
|
25
|
+
*/
|
|
26
|
+
export const openAITranscriptionModelOptionsShape = () => ({
|
|
27
|
+
/**
|
|
28
|
+
* Additional information to include in the transcription response.
|
|
29
|
+
*/
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
include: z.array(z.string()).optional(),
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The language of the input audio in ISO-639-1 format.
|
|
35
|
+
*/
|
|
36
|
+
language: z.string().optional(),
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Expected languages for REST transcription, e.g. with gpt-transcribe.
|
|
40
|
+
* Use this instead of language. The API validates model support.
|
|
41
|
+
*/
|
|
42
|
+
languages: z.array(z.string()).optional(),
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Vocabulary hints for REST transcription, e.g. names or technical terms.
|
|
46
|
+
* The API validates model support.
|
|
47
|
+
*/
|
|
48
|
+
keywords: z.array(z.string()).optional(),
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* An optional text to guide the model's style or continue a previous audio segment.
|
|
52
|
+
*/
|
|
53
|
+
prompt: z.string().optional(),
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
/**
|
|
56
|
+
* The sampling temperature, between 0 and 1.
|
|
57
|
+
* @default 0
|
|
58
|
+
*/
|
|
59
|
+
temperature: z.number().min(0).max(1).default(0).optional(),
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
/**
|
|
62
|
+
* The timestamp granularities to populate for this transcription.
|
|
63
|
+
* Defaults to ['segment'] when the response format is verbose_json.
|
|
64
|
+
*/
|
|
65
|
+
timestampGranularities: z.array(z.enum(['word', 'segment'])).optional(),
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
/**
|
|
68
|
+
* The format of the transcription response.
|
|
69
|
+
*/
|
|
70
|
+
responseFormat: z.enum(['json', 'verbose_json', 'diarized_json']).optional(),
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Controls how the audio is split into chunks before transcription.
|
|
74
|
+
*/
|
|
75
|
+
chunkingStrategy: z
|
|
76
|
+
.union([
|
|
77
|
+
z.literal('auto'),
|
|
78
|
+
z.object({
|
|
79
|
+
type: z.literal('server_vad'),
|
|
80
|
+
threshold: z.number().min(0).max(1).optional(),
|
|
81
|
+
prefixPaddingMs: z.number().int().min(0).optional(),
|
|
82
|
+
silenceDurationMs: z.number().int().min(0).optional(),
|
|
83
|
+
}),
|
|
84
|
+
])
|
|
85
|
+
.optional(),
|
|
70
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Options for streaming transcription models such as `gpt-live-transcribe`.
|
|
89
|
+
*/
|
|
90
|
+
streaming: z
|
|
91
|
+
.object({
|
|
71
92
|
/**
|
|
72
|
-
*
|
|
93
|
+
* Latency/accuracy tradeoff for realtime transcription.
|
|
73
94
|
*/
|
|
74
|
-
|
|
75
|
-
.union([
|
|
76
|
-
z.literal('auto'),
|
|
77
|
-
z.object({
|
|
78
|
-
type: z.literal('server_vad'),
|
|
79
|
-
threshold: z.number().min(0).max(1).optional(),
|
|
80
|
-
prefixPaddingMs: z.number().int().min(0).optional(),
|
|
81
|
-
silenceDurationMs: z.number().int().min(0).optional(),
|
|
82
|
-
}),
|
|
83
|
-
])
|
|
84
|
-
.optional(),
|
|
95
|
+
delay: z.enum(['minimal', 'low', 'medium', 'high', 'xhigh']).optional(),
|
|
85
96
|
|
|
86
97
|
/**
|
|
87
|
-
*
|
|
98
|
+
* Additional fields to include in realtime transcription events.
|
|
88
99
|
*/
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
*/
|
|
94
|
-
delay: z
|
|
95
|
-
.enum(['minimal', 'low', 'medium', 'high', 'xhigh'])
|
|
96
|
-
.optional(),
|
|
100
|
+
include: z.array(z.string()).optional(),
|
|
101
|
+
})
|
|
102
|
+
.optional(),
|
|
103
|
+
});
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*/
|
|
101
|
-
include: z.array(z.string()).optional(),
|
|
102
|
-
})
|
|
103
|
-
.optional(),
|
|
104
|
-
}),
|
|
105
|
-
),
|
|
105
|
+
export const openAITranscriptionModelOptions = lazySchema(() =>
|
|
106
|
+
zodSchema(z.object(openAITranscriptionModelOptionsShape())),
|
|
106
107
|
);
|
|
107
108
|
|
|
108
109
|
export type OpenAITranscriptionModelOptions = InferSchema<
|