@ai-sdk/openai 4.0.108 → 4.0.110
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 +54 -0
- package/dist/index.d.ts +79 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +788 -423
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +389 -9
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +629 -380
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +224 -18
- package/package.json +3 -3
- package/src/chat/convert-to-openai-chat-messages.ts +29 -4
- 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-options.ts +13 -0
- package/src/chat/openai-chat-language-model.ts +111 -22
- package/src/chat/openai-chat-prompt.ts +4 -0
- package/src/image/openai-image-model-options.ts +48 -0
- package/src/image/openai-image-model.ts +49 -4
- package/src/index.ts +1 -0
- package/src/internal/index.ts +4 -0
- package/src/live/openai-realtime-model-live.ts +21 -2
- package/src/openai-batch.ts +8 -0
- package/src/openai-model-catalog.ts +3 -1
- package/src/openai-tools.ts +4 -3
- package/src/realtime/openai-realtime-event-mapper.ts +172 -11
- package/src/realtime/openai-realtime-model.ts +57 -3
- package/src/responses/map-openai-responses-finish-reason.ts +7 -1
- package/src/responses/openai-responses-api.ts +90 -49
- package/src/responses/openai-responses-language-model.ts +56 -1
- package/src/responses/openai-responses-prepare-tools.ts +18 -1
- package/src/tool/code-interpreter.ts +38 -2
- package/src/tool/mcp.ts +27 -8
- package/src/transcription/openai-transcription-model.ts +43 -8
|
@@ -53,6 +53,15 @@ import {
|
|
|
53
53
|
import { prepareChatTools } from './openai-chat-prepare-tools';
|
|
54
54
|
import { normalizeOpenAIJsonSchema } from '../normalize-openai-json-schema';
|
|
55
55
|
|
|
56
|
+
type OpenAIChatLogprob = NonNullable<
|
|
57
|
+
NonNullable<
|
|
58
|
+
Extract<
|
|
59
|
+
OpenAIChatChunk,
|
|
60
|
+
{ choices: unknown }
|
|
61
|
+
>['choices'][number]['logprobs']
|
|
62
|
+
>['content']
|
|
63
|
+
>[number];
|
|
64
|
+
|
|
56
65
|
type OpenAIChatConfig = {
|
|
57
66
|
provider: string;
|
|
58
67
|
/** Model ID sent to the API. Defaults to the model's reported ID. */
|
|
@@ -178,16 +187,15 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
178
187
|
});
|
|
179
188
|
}
|
|
180
189
|
|
|
181
|
-
const { messages, warnings: messageWarnings } =
|
|
182
|
-
{
|
|
190
|
+
const { messages, warnings: messageWarnings } =
|
|
191
|
+
await convertToOpenAIChatMessages({
|
|
183
192
|
prompt,
|
|
184
193
|
systemMessageMode:
|
|
185
194
|
openaiOptions.systemMessageMode ??
|
|
186
195
|
(isReasoningModel
|
|
187
196
|
? 'developer'
|
|
188
197
|
: modelRequestConfig.systemMessageMode),
|
|
189
|
-
}
|
|
190
|
-
);
|
|
198
|
+
});
|
|
191
199
|
|
|
192
200
|
warnings.push(...messageWarnings);
|
|
193
201
|
|
|
@@ -414,16 +422,17 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
414
422
|
options: LanguageModelV4CallOptions,
|
|
415
423
|
): Promise<LanguageModelV4GenerateResult> {
|
|
416
424
|
const { args: body, warnings } = await this.getArgs(options);
|
|
425
|
+
const url = this.config.url({
|
|
426
|
+
path: '/chat/completions',
|
|
427
|
+
modelId: this.config.requestModelId ?? this.modelId,
|
|
428
|
+
});
|
|
417
429
|
|
|
418
430
|
const {
|
|
419
431
|
responseHeaders,
|
|
420
432
|
value: response,
|
|
421
433
|
rawValue: rawResponse,
|
|
422
434
|
} = await postJsonToApi({
|
|
423
|
-
url
|
|
424
|
-
path: '/chat/completions',
|
|
425
|
-
modelId: this.config.requestModelId ?? this.modelId,
|
|
426
|
-
}),
|
|
435
|
+
url,
|
|
427
436
|
headers: combineHeaders(
|
|
428
437
|
await resolve(this.config.headers),
|
|
429
438
|
options.headers,
|
|
@@ -447,7 +456,10 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
447
456
|
|
|
448
457
|
const content: Array<LanguageModelV4Content> = [];
|
|
449
458
|
|
|
450
|
-
// text content
|
|
459
|
+
// text content. When the model refuses to answer, the API returns the
|
|
460
|
+
// refusal in `refusal` and sets `content` to null, so surface it as text
|
|
461
|
+
// instead of dropping it. Content and refusal are both returned when the
|
|
462
|
+
// model refuses after answering, so keep both:
|
|
451
463
|
const text =
|
|
452
464
|
choice.message.content != null && choice.message.content.length > 0
|
|
453
465
|
? choice.message.content
|
|
@@ -456,6 +468,12 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
456
468
|
content.push({ type: 'text', text });
|
|
457
469
|
}
|
|
458
470
|
|
|
471
|
+
const refusal = choice.message.refusal;
|
|
472
|
+
const hasRefusal = refusal != null && refusal.length > 0;
|
|
473
|
+
if (hasRefusal) {
|
|
474
|
+
content.push({ type: 'text', text: refusal });
|
|
475
|
+
}
|
|
476
|
+
|
|
459
477
|
// tool calls:
|
|
460
478
|
for (const toolCall of choice.message.tool_calls ?? []) {
|
|
461
479
|
content.push({
|
|
@@ -488,18 +506,29 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
488
506
|
providerMetadata.openai.rejectedPredictionTokens =
|
|
489
507
|
completionTokenDetails?.rejected_prediction_tokens;
|
|
490
508
|
}
|
|
491
|
-
|
|
492
|
-
|
|
509
|
+
// logprobs either follow the text content (content) or the refusal, so
|
|
510
|
+
// combine them in the order the text is emitted above. Explicitly empty
|
|
511
|
+
// logprobs are reported as an empty array, as before:
|
|
512
|
+
const contentLogprobs = choice.logprobs?.content;
|
|
513
|
+
const refusalLogprobs = choice.logprobs?.refusal;
|
|
514
|
+
if (contentLogprobs != null || refusalLogprobs != null) {
|
|
515
|
+
providerMetadata.openai.logprobs = [
|
|
516
|
+
...(contentLogprobs ?? []),
|
|
517
|
+
...(refusalLogprobs ?? []),
|
|
518
|
+
];
|
|
493
519
|
}
|
|
494
520
|
|
|
495
521
|
return {
|
|
496
522
|
content,
|
|
497
523
|
finishReason: {
|
|
498
|
-
unified: mapOpenAIFinishReason(
|
|
524
|
+
unified: mapOpenAIFinishReason({
|
|
525
|
+
finishReason: choice.finish_reason,
|
|
526
|
+
hasRefusal,
|
|
527
|
+
}),
|
|
499
528
|
raw: choice.finish_reason ?? undefined,
|
|
500
529
|
},
|
|
501
530
|
usage: convertOpenAIChatUsage(response.usage),
|
|
502
|
-
request: { body },
|
|
531
|
+
request: { body, url },
|
|
503
532
|
response: {
|
|
504
533
|
...getResponseMetadata(response),
|
|
505
534
|
headers: responseHeaders,
|
|
@@ -562,6 +591,13 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
562
591
|
let usage: OpenAIChatUsage | undefined = undefined;
|
|
563
592
|
let metadataExtracted = false;
|
|
564
593
|
let isActiveText = false;
|
|
594
|
+
let hasRefusal = false;
|
|
595
|
+
let rawFinishReason: string | undefined = undefined;
|
|
596
|
+
let hasLogprobs = false;
|
|
597
|
+
|
|
598
|
+
// logprobs of all streamed chunks (content token logprobs followed by
|
|
599
|
+
// refusal token logprobs), collected across chunks:
|
|
600
|
+
const streamedLogprobs: OpenAIChatLogprob[] = [];
|
|
565
601
|
|
|
566
602
|
const providerMetadata: SharedV4ProviderMetadata = { openai: {} };
|
|
567
603
|
|
|
@@ -587,6 +623,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
587
623
|
// handle failed chunk parsing / validation:
|
|
588
624
|
if (!chunk.success) {
|
|
589
625
|
finishReason = { unified: 'error', raw: undefined };
|
|
626
|
+
rawFinishReason = undefined;
|
|
590
627
|
controller.enqueue({ type: 'error', error: chunk.error });
|
|
591
628
|
return;
|
|
592
629
|
}
|
|
@@ -596,6 +633,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
596
633
|
// handle error chunks:
|
|
597
634
|
if ('error' in value) {
|
|
598
635
|
finishReason = { unified: 'error', raw: undefined };
|
|
636
|
+
rawFinishReason = undefined;
|
|
599
637
|
controller.enqueue({
|
|
600
638
|
type: 'error',
|
|
601
639
|
error:
|
|
@@ -639,15 +677,29 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
639
677
|
|
|
640
678
|
const choice = value.choices[0];
|
|
641
679
|
|
|
680
|
+
// store the raw finish reason and map it in `flush`, after all
|
|
681
|
+
// chunks have been read: some servers send the last delta (which
|
|
682
|
+
// can contain a refusal) in the same chunk as the finish reason,
|
|
683
|
+
// and a refusal turns `stop` into `content-filter`:
|
|
642
684
|
if (choice?.finish_reason != null) {
|
|
643
|
-
|
|
644
|
-
unified: mapOpenAIFinishReason(choice.finish_reason),
|
|
645
|
-
raw: choice.finish_reason,
|
|
646
|
-
};
|
|
685
|
+
rawFinishReason = choice.finish_reason;
|
|
647
686
|
}
|
|
648
687
|
|
|
649
|
-
|
|
650
|
-
|
|
688
|
+
// logprobs either follow the text content (content) or the refusal.
|
|
689
|
+
// They are streamed token by token, so collect the logprobs of all
|
|
690
|
+
// chunks (content logprobs followed by refusal logprobs) and report
|
|
691
|
+
// the complete list at the end of the stream:
|
|
692
|
+
if (choice?.logprobs != null) {
|
|
693
|
+
const contentLogprobs = choice.logprobs.content;
|
|
694
|
+
const refusalLogprobs = choice.logprobs.refusal;
|
|
695
|
+
|
|
696
|
+
if (contentLogprobs != null || refusalLogprobs != null) {
|
|
697
|
+
hasLogprobs = true;
|
|
698
|
+
streamedLogprobs.push(
|
|
699
|
+
...(contentLogprobs ?? []),
|
|
700
|
+
...(refusalLogprobs ?? []),
|
|
701
|
+
);
|
|
702
|
+
}
|
|
651
703
|
}
|
|
652
704
|
|
|
653
705
|
if (choice?.delta == null) {
|
|
@@ -656,7 +708,24 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
656
708
|
|
|
657
709
|
const delta = choice.delta;
|
|
658
710
|
|
|
659
|
-
|
|
711
|
+
// When the model refuses to answer, the API streams the refusal in
|
|
712
|
+
// `refusal` instead of `content`. Content and refusal deltas can
|
|
713
|
+
// both occur, so append each to the same text part instead of
|
|
714
|
+
// dropping one:
|
|
715
|
+
const textDeltas: string[] = [];
|
|
716
|
+
if (delta.content != null && delta.content.length > 0) {
|
|
717
|
+
textDeltas.push(delta.content);
|
|
718
|
+
}
|
|
719
|
+
if (delta.refusal != null && delta.refusal.length > 0) {
|
|
720
|
+
hasRefusal = true;
|
|
721
|
+
textDeltas.push(delta.refusal);
|
|
722
|
+
}
|
|
723
|
+
// empty `content: ""` deltas keep their previous behavior:
|
|
724
|
+
if (textDeltas.length === 0 && delta.content === '') {
|
|
725
|
+
textDeltas.push(delta.content);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
for (const textDelta of textDeltas) {
|
|
660
729
|
if (!isActiveText) {
|
|
661
730
|
controller.enqueue({ type: 'text-start', id: '0' });
|
|
662
731
|
isActiveText = true;
|
|
@@ -665,7 +734,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
665
734
|
controller.enqueue({
|
|
666
735
|
type: 'text-delta',
|
|
667
736
|
id: '0',
|
|
668
|
-
delta:
|
|
737
|
+
delta: textDelta,
|
|
669
738
|
});
|
|
670
739
|
}
|
|
671
740
|
|
|
@@ -696,6 +765,25 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
696
765
|
|
|
697
766
|
toolCallTracker.flush();
|
|
698
767
|
|
|
768
|
+
// map the stored finish reason now that all deltas (including
|
|
769
|
+
// refusals that arrived in the same chunk as the finish reason)
|
|
770
|
+
// have been read:
|
|
771
|
+
if (rawFinishReason != null) {
|
|
772
|
+
finishReason = {
|
|
773
|
+
unified: mapOpenAIFinishReason({
|
|
774
|
+
finishReason: rawFinishReason,
|
|
775
|
+
hasRefusal,
|
|
776
|
+
}),
|
|
777
|
+
raw: rawFinishReason,
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// report the logprobs of all streamed chunks (content logprobs
|
|
782
|
+
// followed by refusal logprobs), not only the last chunk:
|
|
783
|
+
if (hasLogprobs) {
|
|
784
|
+
providerMetadata.openai.logprobs = streamedLogprobs;
|
|
785
|
+
}
|
|
786
|
+
|
|
699
787
|
controller.enqueue({
|
|
700
788
|
type: 'finish',
|
|
701
789
|
finishReason,
|
|
@@ -705,7 +793,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
705
793
|
},
|
|
706
794
|
}),
|
|
707
795
|
),
|
|
708
|
-
request: { body },
|
|
796
|
+
request: { body, url },
|
|
709
797
|
response: { headers: responseHeaders },
|
|
710
798
|
};
|
|
711
799
|
|
|
@@ -723,6 +811,7 @@ function isOpenAIChatOutputChunk(chunk: OpenAIChatChunk): boolean {
|
|
|
723
811
|
|
|
724
812
|
return (
|
|
725
813
|
(delta?.content != null && delta.content.length > 0) ||
|
|
814
|
+
(delta?.refusal != null && delta.refusal.length > 0) ||
|
|
726
815
|
(delta?.tool_calls != null && delta.tool_calls.length > 0) ||
|
|
727
816
|
(delta?.annotations != null && delta.annotations.length > 0)
|
|
728
817
|
);
|
|
@@ -8,16 +8,19 @@ export type ChatCompletionMessage =
|
|
|
8
8
|
| ChatCompletionToolMessage;
|
|
9
9
|
|
|
10
10
|
export interface ChatCompletionSystemMessage {
|
|
11
|
+
name?: string;
|
|
11
12
|
role: 'system';
|
|
12
13
|
content: string | Array<ChatCompletionContentPartText>;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export interface ChatCompletionDeveloperMessage {
|
|
17
|
+
name?: string;
|
|
16
18
|
role: 'developer';
|
|
17
19
|
content: string | Array<ChatCompletionContentPartText>;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export interface ChatCompletionUserMessage {
|
|
23
|
+
name?: string;
|
|
21
24
|
role: 'user';
|
|
22
25
|
content: string | Array<ChatCompletionContentPart>;
|
|
23
26
|
}
|
|
@@ -53,6 +56,7 @@ export interface ChatCompletionContentPartFile {
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
export interface ChatCompletionAssistantMessage {
|
|
59
|
+
name?: string;
|
|
56
60
|
role: 'assistant';
|
|
57
61
|
content?: string | null | Array<ChatCompletionContentPartText>;
|
|
58
62
|
tool_calls?: Array<ChatCompletionMessageToolCall>;
|
|
@@ -16,6 +16,8 @@ export type OpenAIImageModelId =
|
|
|
16
16
|
| 'gpt-image-1-mini'
|
|
17
17
|
| 'gpt-image-1.5'
|
|
18
18
|
| 'gpt-image-2'
|
|
19
|
+
| 'gpt-image-2.6-flare'
|
|
20
|
+
| 'gpt-image-2.6-sunburst'
|
|
19
21
|
| 'gpt-image-2.5-flare'
|
|
20
22
|
| 'gpt-image-2.5-flare-2026-09-08'
|
|
21
23
|
| 'gpt-image-2.5-sunburst'
|
|
@@ -64,6 +66,12 @@ const baseImageModelOptionsObject = () =>
|
|
|
64
66
|
*/
|
|
65
67
|
outputFormat: z.enum(['png', 'jpeg', 'webp']).optional(),
|
|
66
68
|
|
|
69
|
+
/**
|
|
70
|
+
* How much detail the model puts into the image. Supported by GPT Image
|
|
71
|
+
* 2.6 models. The API defaults to `high`.
|
|
72
|
+
*/
|
|
73
|
+
complexity: z.enum(['low', 'medium', 'high']).optional(),
|
|
74
|
+
|
|
67
75
|
/**
|
|
68
76
|
* Compression level (0-100) for the generated image(s). Applies to the
|
|
69
77
|
* `jpeg` and `webp` output formats.
|
|
@@ -75,6 +83,13 @@ const baseImageModelOptionsObject = () =>
|
|
|
75
83
|
* to monitor and detect abuse.
|
|
76
84
|
*/
|
|
77
85
|
user: z.string().optional(),
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A stable identifier used to help detect users of your application that
|
|
89
|
+
* may be violating OpenAI's usage policies. Should be a hashed username
|
|
90
|
+
* or email address.
|
|
91
|
+
*/
|
|
92
|
+
safetyIdentifier: z.string().max(128).optional(),
|
|
78
93
|
});
|
|
79
94
|
|
|
80
95
|
export const openaiImageModelOptions = lazySchema(() =>
|
|
@@ -95,6 +110,25 @@ export const openaiImageModelGenerationOptions = lazySchema(() =>
|
|
|
95
110
|
*/
|
|
96
111
|
style: z.enum(['vivid', 'natural']).optional(),
|
|
97
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Format in which the generated image(s) are returned. `gif` produces
|
|
115
|
+
* an animated GIF and is supported by GPT Image 2.6 models.
|
|
116
|
+
*/
|
|
117
|
+
outputFormat: z.enum(['png', 'jpeg', 'webp', 'gif']).optional(),
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Number of frames (2-64) of an animated GIF. Only applies to the `gif`
|
|
121
|
+
* output format. The API defaults to 9.
|
|
122
|
+
*/
|
|
123
|
+
frameCount: z.number().int().min(2).max(64).optional(),
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Duration of each frame in milliseconds (20-2000) of an animated GIF.
|
|
127
|
+
* The API rounds it to the nearest 10 ms. Only applies to the `gif`
|
|
128
|
+
* output format. The API defaults to 200.
|
|
129
|
+
*/
|
|
130
|
+
frameDurationMs: z.number().int().min(20).max(2000).optional(),
|
|
131
|
+
|
|
98
132
|
/**
|
|
99
133
|
* Content moderation level for the generated image(s). `low` applies
|
|
100
134
|
* less restrictive filtering.
|
|
@@ -115,6 +149,20 @@ export const openaiImageModelEditOptions = lazySchema(() =>
|
|
|
115
149
|
* Fidelity of the output image(s) to the input image(s).
|
|
116
150
|
*/
|
|
117
151
|
inputFidelity: z.enum(['high', 'low']).optional(),
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* A transparent PNG overlaid on the first input image to mark up the
|
|
155
|
+
* regions to change, as an HTTPS URL or a base64 data URL. It must match
|
|
156
|
+
* the dimensions of the first image. Supported by GPT Image 2.5 and 2.6
|
|
157
|
+
* models. Cannot be combined with a `mask`.
|
|
158
|
+
*/
|
|
159
|
+
markup: z.string().optional(),
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Content moderation level for the edited image(s). `low` applies
|
|
163
|
+
* less restrictive filtering. Supported by GPT Image models.
|
|
164
|
+
*/
|
|
165
|
+
moderation: z.enum(['auto', 'low']).optional(),
|
|
118
166
|
}),
|
|
119
167
|
),
|
|
120
168
|
);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
InvalidArgumentError,
|
|
3
|
+
type ImageModelV4,
|
|
4
|
+
type ImageModelV4File,
|
|
5
|
+
type SharedV4Warning,
|
|
5
6
|
} from '@ai-sdk/provider';
|
|
6
7
|
import {
|
|
7
8
|
resolve,
|
|
@@ -128,6 +129,13 @@ export class OpenAIImageModel implements ImageModelV4 {
|
|
|
128
129
|
schema: openaiImageModelEditOptions,
|
|
129
130
|
})) ?? {};
|
|
130
131
|
|
|
132
|
+
if (mask != null && openaiOptions.markup != null) {
|
|
133
|
+
throw new InvalidArgumentError({
|
|
134
|
+
argument: 'providerOptions.openai.markup',
|
|
135
|
+
message: '`markup` cannot be combined with `mask`.',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
131
139
|
const { value: response, responseHeaders } = await postFormDataToApi({
|
|
132
140
|
url: this.config.url({
|
|
133
141
|
path: '/images/edits',
|
|
@@ -156,14 +164,21 @@ export class OpenAIImageModel implements ImageModelV4 {
|
|
|
156
164
|
),
|
|
157
165
|
),
|
|
158
166
|
mask: mask != null ? await fileToBlob(mask, abortSignal) : undefined,
|
|
167
|
+
markup:
|
|
168
|
+
openaiOptions.markup != null
|
|
169
|
+
? await markupToBlob(openaiOptions.markup, abortSignal)
|
|
170
|
+
: undefined,
|
|
159
171
|
n,
|
|
160
172
|
size,
|
|
161
173
|
quality: openaiOptions.quality,
|
|
162
174
|
background: openaiOptions.background,
|
|
163
175
|
output_format: openaiOptions.outputFormat,
|
|
164
176
|
output_compression: openaiOptions.outputCompression,
|
|
177
|
+
complexity: openaiOptions.complexity,
|
|
165
178
|
input_fidelity: openaiOptions.inputFidelity,
|
|
179
|
+
moderation: openaiOptions.moderation,
|
|
166
180
|
user: openaiOptions.user,
|
|
181
|
+
safety_identifier: openaiOptions.safetyIdentifier,
|
|
167
182
|
}),
|
|
168
183
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
169
184
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -235,7 +250,11 @@ export class OpenAIImageModel implements ImageModelV4 {
|
|
|
235
250
|
moderation: openaiOptions.moderation,
|
|
236
251
|
output_format: openaiOptions.outputFormat,
|
|
237
252
|
output_compression: openaiOptions.outputCompression,
|
|
253
|
+
complexity: openaiOptions.complexity,
|
|
254
|
+
frame_count: openaiOptions.frameCount,
|
|
255
|
+
frame_duration_ms: openaiOptions.frameDurationMs,
|
|
238
256
|
user: openaiOptions.user,
|
|
257
|
+
safety_identifier: openaiOptions.safetyIdentifier,
|
|
239
258
|
...(!hasDefaultResponseFormat(this.modelId)
|
|
240
259
|
? { response_format: 'b64_json' }
|
|
241
260
|
: {}),
|
|
@@ -326,16 +345,42 @@ type OpenAIImageEditInput = {
|
|
|
326
345
|
prompt?: string;
|
|
327
346
|
image: Blob | Blob[];
|
|
328
347
|
mask?: Blob;
|
|
348
|
+
markup?: Blob;
|
|
329
349
|
n?: number;
|
|
330
350
|
size?: `${number}x${number}`;
|
|
331
351
|
quality?: OpenAIImageModelEditOptions['quality'];
|
|
332
352
|
background?: OpenAIImageModelEditOptions['background'];
|
|
333
353
|
output_format?: OpenAIImageModelEditOptions['outputFormat'];
|
|
334
354
|
output_compression?: OpenAIImageModelEditOptions['outputCompression'];
|
|
355
|
+
complexity?: OpenAIImageModelEditOptions['complexity'];
|
|
335
356
|
input_fidelity?: OpenAIImageModelEditOptions['inputFidelity'];
|
|
357
|
+
moderation?: OpenAIImageModelEditOptions['moderation'];
|
|
336
358
|
user?: OpenAIImageModelEditOptions['user'];
|
|
359
|
+
safety_identifier?: OpenAIImageModelEditOptions['safetyIdentifier'];
|
|
337
360
|
};
|
|
338
361
|
|
|
362
|
+
async function markupToBlob(
|
|
363
|
+
markup: string,
|
|
364
|
+
abortSignal: AbortSignal | undefined,
|
|
365
|
+
): Promise<Blob> {
|
|
366
|
+
const dataUrl = /^data:([^;,]*)(;base64)?,(.*)$/s.exec(markup);
|
|
367
|
+
|
|
368
|
+
if (dataUrl == null) {
|
|
369
|
+
return downloadBlob(markup, { abortSignal });
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (dataUrl[2] == null) {
|
|
373
|
+
throw new InvalidArgumentError({
|
|
374
|
+
argument: 'providerOptions.openai.markup',
|
|
375
|
+
message: '`markup` data URLs must be base64 encoded.',
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return new Blob([convertBase64ToUint8Array(dataUrl[3]) as BlobPart], {
|
|
380
|
+
type: dataUrl[1] || 'image/png',
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
339
384
|
async function fileToBlob(
|
|
340
385
|
file: ImageModelV4File | undefined,
|
|
341
386
|
abortSignal: AbortSignal | undefined,
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type {
|
|
|
22
22
|
export type { OpenAIToolOptions } from './responses/openai-responses-prepare-tools';
|
|
23
23
|
export type {
|
|
24
24
|
OpenAILanguageModelChatOptions,
|
|
25
|
+
OpenAIChatMessageProviderOptions,
|
|
25
26
|
/** @deprecated Use `OpenAILanguageModelChatOptions` instead. */
|
|
26
27
|
OpenAILanguageModelChatOptions as OpenAIChatLanguageModelOptions,
|
|
27
28
|
} from './chat/openai-chat-language-model-options';
|
package/src/internal/index.ts
CHANGED
|
@@ -13,10 +13,14 @@ export * from '../speech/openai-speech-model-options';
|
|
|
13
13
|
export * from '../responses/openai-responses-language-model';
|
|
14
14
|
export * from '../responses/openai-responses-provider-metadata';
|
|
15
15
|
export * from '../tool/apply-patch';
|
|
16
|
+
export * from '../tool/custom';
|
|
16
17
|
export * from '../tool/code-interpreter';
|
|
17
18
|
export * from '../tool/file-search';
|
|
18
19
|
export * from '../tool/image-generation';
|
|
19
20
|
export * from '../tool/programmatic-tool-calling';
|
|
21
|
+
export * from '../tool/shell';
|
|
20
22
|
export * from '../tool/tool-search';
|
|
21
23
|
export * from '../tool/web-search';
|
|
22
24
|
export * from '../tool/web-search-preview';
|
|
25
|
+
|
|
26
|
+
export { openaiModelCatalog } from '../openai-model-catalog';
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
type Experimental_RealtimeModelV4 as RealtimeModelV4,
|
|
3
3
|
type Experimental_RealtimeModelV4ClientEvent as RealtimeModelV4ClientEvent,
|
|
4
4
|
type Experimental_RealtimeModelV4SessionConfig as RealtimeModelV4SessionConfig,
|
|
5
|
+
type Experimental_RealtimeModelV4SessionIntent as RealtimeModelV4SessionIntent,
|
|
5
6
|
} from '@ai-sdk/provider';
|
|
6
7
|
import {
|
|
7
8
|
resolveWith,
|
|
@@ -54,7 +55,10 @@ export class OpenAIRealtimeModelLive implements RealtimeModelV4 {
|
|
|
54
55
|
return { dataChannelLabel: 'oai-events' };
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
getServerWebSocketConfig(
|
|
58
|
+
getServerWebSocketConfig(options?: {
|
|
59
|
+
intent?: RealtimeModelV4SessionIntent;
|
|
60
|
+
}) {
|
|
61
|
+
this.assertConversationIntent(options);
|
|
58
62
|
return resolveWith(this.config.headers, configHeaders => {
|
|
59
63
|
const url = new URL(`${this.config.baseURL}/live/sessions`);
|
|
60
64
|
url.protocol = url.protocol === 'http:' ? 'ws:' : 'wss:';
|
|
@@ -103,13 +107,28 @@ export class OpenAIRealtimeModelLive implements RealtimeModelV4 {
|
|
|
103
107
|
return createOpenAILiveServerEventParser();
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
|
|
110
|
+
private assertConversationIntent(options?: {
|
|
111
|
+
intent?: RealtimeModelV4SessionIntent;
|
|
112
|
+
}): void {
|
|
113
|
+
if (options?.intent != null && options.intent !== 'conversation')
|
|
114
|
+
throw new Error(
|
|
115
|
+
`OpenAI Live session intent '${options.intent}' is not supported.`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
serializeClientEvent(
|
|
120
|
+
event: RealtimeModelV4ClientEvent,
|
|
121
|
+
options?: { intent?: RealtimeModelV4SessionIntent },
|
|
122
|
+
): unknown {
|
|
123
|
+
this.assertConversationIntent(options);
|
|
107
124
|
return serializeOpenAILiveClientEvent(event, this.modelId);
|
|
108
125
|
}
|
|
109
126
|
|
|
110
127
|
buildSessionConfig(
|
|
111
128
|
config: RealtimeModelV4SessionConfig,
|
|
129
|
+
options?: { intent?: RealtimeModelV4SessionIntent },
|
|
112
130
|
): Record<string, unknown> {
|
|
131
|
+
this.assertConversationIntent(options);
|
|
113
132
|
return buildOpenAILiveSessionConfig(config, this.modelId);
|
|
114
133
|
}
|
|
115
134
|
}
|
package/src/openai-batch.ts
CHANGED
|
@@ -748,6 +748,7 @@ async function convertOpenAIBatchResult(
|
|
|
748
748
|
const content: LanguageModelV4GenerateResult['content'] = [];
|
|
749
749
|
const logprobs: Array<NonNullable<OpenAIResponsesLogprobs>> = [];
|
|
750
750
|
let hasFunctionCall = false;
|
|
751
|
+
let hasRefusal = false;
|
|
751
752
|
|
|
752
753
|
for (const part of response.output) {
|
|
753
754
|
switch (part.type) {
|
|
@@ -774,6 +775,12 @@ async function convertOpenAIBatchResult(
|
|
|
774
775
|
|
|
775
776
|
case 'message': {
|
|
776
777
|
for (const contentPart of part.content) {
|
|
778
|
+
if (contentPart.type === 'refusal') {
|
|
779
|
+
hasRefusal = true;
|
|
780
|
+
content.push({ type: 'text', text: contentPart.refusal });
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
|
|
777
784
|
content.push({ type: 'text', text: contentPart.text });
|
|
778
785
|
if (contentPart.logprobs != null) {
|
|
779
786
|
logprobs.push(contentPart.logprobs);
|
|
@@ -916,6 +923,7 @@ async function convertOpenAIBatchResult(
|
|
|
916
923
|
unified: mapOpenAIResponseFinishReason({
|
|
917
924
|
finishReason: response.incomplete_details?.reason,
|
|
918
925
|
hasFunctionCall,
|
|
926
|
+
hasRefusal,
|
|
919
927
|
}),
|
|
920
928
|
raw: response.incomplete_details?.reason ?? undefined,
|
|
921
929
|
},
|
|
@@ -461,6 +461,8 @@ function createGptImageEntry(
|
|
|
461
461
|
|
|
462
462
|
const openaiImageModelEntries: Readonly<Record<string, OpenAIImageModelEntry>> =
|
|
463
463
|
{
|
|
464
|
+
'gpt-image-2.6-flare': createGptImageEntry(customImageSizes),
|
|
465
|
+
'gpt-image-2.6-sunburst': createGptImageEntry(customImageSizes),
|
|
464
466
|
'gpt-image-2.5-flare': createGptImageEntry(customImageSizes),
|
|
465
467
|
'gpt-image-2.5-sunburst': createGptImageEntry(customImageSizes),
|
|
466
468
|
'gpt-image-2': createGptImageEntry(gptImageSizes),
|
|
@@ -757,7 +759,7 @@ const realtimeModelEntry = {
|
|
|
757
759
|
realtime: {
|
|
758
760
|
conversation: 'turn-based',
|
|
759
761
|
transports: ['websocket'],
|
|
760
|
-
connections: ['client-secret-websocket'],
|
|
762
|
+
connections: ['client-secret-websocket', 'server-websocket'],
|
|
761
763
|
},
|
|
762
764
|
},
|
|
763
765
|
requestConfig: {
|
package/src/openai-tools.ts
CHANGED
|
@@ -126,16 +126,17 @@ export const openaiTools = {
|
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
128
|
* MCP (Model Context Protocol) allows models to call tools exposed by
|
|
129
|
-
* remote MCP servers or
|
|
129
|
+
* remote MCP servers, service connectors, or Secure MCP tunnels.
|
|
130
130
|
*
|
|
131
131
|
* @param serverLabel - Label to identify the MCP server.
|
|
132
132
|
* @param allowedTools - Allowed tool names or filter object.
|
|
133
|
-
* @param authorization - OAuth access token for the MCP server
|
|
133
|
+
* @param authorization - OAuth access token for the MCP server or connector (including tunneled servers).
|
|
134
134
|
* @param connectorId - Identifier for a service connector.
|
|
135
135
|
* @param headers - Optional headers to include in MCP requests.
|
|
136
|
-
*
|
|
136
|
+
* @param requireApproval - Approval policy ('always'|'never'|filter object).
|
|
137
137
|
* @param serverDescription - Optional description of the server.
|
|
138
138
|
* @param serverUrl - URL for the MCP server.
|
|
139
|
+
* @param tunnelId - Identifier for an OpenAI Secure MCP tunnel.
|
|
139
140
|
*/
|
|
140
141
|
mcp,
|
|
141
142
|
|