@ai-sdk/openai 4.0.0-beta.7 → 4.0.0-beta.74

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +636 -24
  2. package/README.md +2 -0
  3. package/dist/index.d.ts +240 -44
  4. package/dist/index.js +3345 -1683
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +390 -36
  7. package/dist/internal/index.js +2707 -1706
  8. package/dist/internal/index.js.map +1 -1
  9. package/docs/03-openai.mdx +413 -39
  10. package/package.json +16 -17
  11. package/src/chat/convert-openai-chat-usage.ts +1 -1
  12. package/src/chat/convert-to-openai-chat-messages.ts +96 -68
  13. package/src/chat/map-openai-finish-reason.ts +1 -1
  14. package/src/chat/openai-chat-api.ts +6 -2
  15. package/src/chat/{openai-chat-options.ts → openai-chat-language-model-options.ts} +11 -1
  16. package/src/chat/openai-chat-language-model.ts +82 -148
  17. package/src/chat/openai-chat-prepare-tools.ts +3 -3
  18. package/src/completion/convert-openai-completion-usage.ts +1 -1
  19. package/src/completion/convert-to-openai-completion-prompt.ts +1 -2
  20. package/src/completion/map-openai-finish-reason.ts +1 -1
  21. package/src/completion/openai-completion-api.ts +5 -2
  22. package/src/completion/{openai-completion-options.ts → openai-completion-language-model-options.ts} +5 -1
  23. package/src/completion/openai-completion-language-model.ts +53 -17
  24. package/src/embedding/{openai-embedding-options.ts → openai-embedding-model-options.ts} +5 -1
  25. package/src/embedding/openai-embedding-model.ts +22 -5
  26. package/src/files/openai-files-api.ts +17 -0
  27. package/src/files/openai-files-options.ts +22 -0
  28. package/src/files/openai-files.ts +100 -0
  29. package/src/image/openai-image-model-options.ts +123 -0
  30. package/src/image/openai-image-model.ts +62 -83
  31. package/src/index.ts +15 -6
  32. package/src/internal/index.ts +7 -6
  33. package/src/openai-config.ts +7 -7
  34. package/src/openai-language-model-capabilities.ts +5 -4
  35. package/src/openai-provider.ts +80 -9
  36. package/src/openai-stream-error.ts +181 -0
  37. package/src/openai-tools.ts +12 -1
  38. package/src/realtime/index.ts +2 -0
  39. package/src/realtime/openai-realtime-event-mapper.ts +436 -0
  40. package/src/realtime/openai-realtime-model-options.ts +3 -0
  41. package/src/realtime/openai-realtime-model.ts +111 -0
  42. package/src/responses/convert-openai-responses-usage.ts +1 -1
  43. package/src/responses/convert-to-openai-responses-input.ts +345 -90
  44. package/src/responses/map-openai-responses-finish-reason.ts +1 -1
  45. package/src/responses/openai-responses-api.ts +186 -17
  46. package/src/responses/{openai-responses-options.ts → openai-responses-language-model-options.ts} +55 -1
  47. package/src/responses/openai-responses-language-model.ts +330 -52
  48. package/src/responses/openai-responses-prepare-tools.ts +129 -18
  49. package/src/responses/openai-responses-provider-metadata.ts +12 -2
  50. package/src/skills/openai-skills-api.ts +31 -0
  51. package/src/skills/openai-skills.ts +83 -0
  52. package/src/speech/{openai-speech-options.ts → openai-speech-model-options.ts} +5 -1
  53. package/src/speech/openai-speech-model.ts +23 -7
  54. package/src/tool/apply-patch.ts +33 -32
  55. package/src/tool/code-interpreter.ts +40 -41
  56. package/src/tool/custom.ts +2 -8
  57. package/src/tool/file-search.ts +3 -3
  58. package/src/tool/image-generation.ts +2 -2
  59. package/src/tool/local-shell.ts +2 -2
  60. package/src/tool/mcp.ts +3 -3
  61. package/src/tool/shell.ts +9 -4
  62. package/src/tool/tool-search.ts +98 -0
  63. package/src/tool/web-search-preview.ts +2 -2
  64. package/src/tool/web-search.ts +10 -2
  65. package/src/transcription/{openai-transcription-options.ts → openai-transcription-model-options.ts} +5 -1
  66. package/src/transcription/openai-transcription-model.ts +35 -13
  67. package/dist/index.d.mts +0 -1107
  68. package/dist/index.mjs +0 -6509
  69. package/dist/index.mjs.map +0 -1
  70. package/dist/internal/index.d.mts +0 -1137
  71. package/dist/internal/index.mjs +0 -6322
  72. package/dist/internal/index.mjs.map +0 -1
  73. package/src/image/openai-image-options.ts +0 -31
@@ -1,5 +1,4 @@
1
- import {
2
- InvalidResponseDataError,
1
+ import type {
3
2
  LanguageModelV4,
4
3
  LanguageModelV4CallOptions,
5
4
  LanguageModelV4Content,
@@ -11,39 +10,44 @@ import {
11
10
  SharedV4Warning,
12
11
  } from '@ai-sdk/provider';
13
12
  import {
14
- FetchFunction,
15
- ParseResult,
13
+ StreamingToolCallTracker,
16
14
  combineHeaders,
17
15
  createEventSourceResponseHandler,
18
16
  createJsonResponseHandler,
19
17
  generateId,
20
- isParsableJson,
18
+ isCustomReasoning,
21
19
  parseProviderOptions,
22
20
  postJsonToApi,
21
+ serializeModelOptions,
22
+ WORKFLOW_DESERIALIZE,
23
+ WORKFLOW_SERIALIZE,
24
+ type FetchFunction,
25
+ type ParseResult,
23
26
  } from '@ai-sdk/provider-utils';
24
27
  import { openaiFailedResponseHandler } from '../openai-error';
25
28
  import { getOpenAILanguageModelCapabilities } from '../openai-language-model-capabilities';
29
+ import { throwIfOpenAIStreamErrorBeforeOutput } from '../openai-stream-error';
26
30
  import {
27
- OpenAIChatUsage,
28
31
  convertOpenAIChatUsage,
32
+ type OpenAIChatUsage,
29
33
  } from './convert-openai-chat-usage';
30
34
  import { convertToOpenAIChatMessages } from './convert-to-openai-chat-messages';
31
35
  import { getResponseMetadata } from './get-response-metadata';
32
36
  import { mapOpenAIFinishReason } from './map-openai-finish-reason';
33
37
  import {
34
- OpenAIChatChunk,
35
38
  openaiChatChunkSchema,
36
39
  openaiChatResponseSchema,
40
+ type OpenAIChatChunk,
37
41
  } from './openai-chat-api';
38
42
  import {
39
- OpenAIChatModelId,
40
43
  openaiLanguageModelChatOptions,
41
- } from './openai-chat-options';
44
+ type OpenAIChatModelId,
45
+ } from './openai-chat-language-model-options';
42
46
  import { prepareChatTools } from './openai-chat-prepare-tools';
43
47
 
44
48
  type OpenAIChatConfig = {
45
49
  provider: string;
46
- headers: () => Record<string, string | undefined>;
50
+ headers?: () => Record<string, string | undefined>;
47
51
  url: (options: { modelId: string; path: string }) => string;
48
52
  fetch?: FetchFunction;
49
53
  };
@@ -59,6 +63,20 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
59
63
 
60
64
  private readonly config: OpenAIChatConfig;
61
65
 
66
+ static [WORKFLOW_SERIALIZE](model: OpenAIChatLanguageModel) {
67
+ return serializeModelOptions({
68
+ modelId: model.modelId,
69
+ config: model.config,
70
+ });
71
+ }
72
+
73
+ static [WORKFLOW_DESERIALIZE](options: {
74
+ modelId: OpenAIChatModelId;
75
+ config: OpenAIChatConfig;
76
+ }) {
77
+ return new OpenAIChatLanguageModel(options.modelId, options.config);
78
+ }
79
+
62
80
  constructor(modelId: OpenAIChatModelId, config: OpenAIChatConfig) {
63
81
  this.modelId = modelId;
64
82
  this.config = config;
@@ -81,6 +99,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
81
99
  seed,
82
100
  tools,
83
101
  toolChoice,
102
+ reasoning,
84
103
  providerOptions,
85
104
  }: LanguageModelV4CallOptions) {
86
105
  const warnings: SharedV4Warning[] = [];
@@ -94,6 +113,12 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
94
113
  })) ?? {};
95
114
 
96
115
  const modelCapabilities = getOpenAILanguageModelCapabilities(this.modelId);
116
+
117
+ // AI SDK reasoning values map directly to the OpenAI reasoning values.
118
+ const resolvedReasoningEffort =
119
+ openaiOptions.reasoningEffort ??
120
+ (isCustomReasoning(reasoning) ? reasoning : undefined);
121
+
97
122
  const isReasoningModel =
98
123
  openaiOptions.forceReasoning ?? modelCapabilities.isReasoningModel;
99
124
 
@@ -168,7 +193,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
168
193
  store: openaiOptions.store,
169
194
  metadata: openaiOptions.metadata,
170
195
  prediction: openaiOptions.prediction,
171
- reasoning_effort: openaiOptions.reasoningEffort,
196
+ reasoning_effort: resolvedReasoningEffort,
172
197
  service_tier: openaiOptions.serviceTier,
173
198
  prompt_cache_key: openaiOptions.promptCacheKey,
174
199
  prompt_cache_retention: openaiOptions.promptCacheRetention,
@@ -184,7 +209,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
184
209
  // when reasoning effort is none, gpt-5.1 models allow temperature, topP, logprobs
185
210
  // https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
186
211
  if (
187
- openaiOptions.reasoningEffort !== 'none' ||
212
+ resolvedReasoningEffort !== 'none' ||
188
213
  !modelCapabilities.supportsNonReasoningParameters
189
214
  ) {
190
215
  if (baseArgs.temperature != null) {
@@ -327,7 +352,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
327
352
  path: '/chat/completions',
328
353
  modelId: this.modelId,
329
354
  }),
330
- headers: combineHeaders(this.config.headers(), options.headers),
355
+ headers: combineHeaders(this.config.headers?.(), options.headers),
331
356
  body,
332
357
  failedResponseHandler: openaiFailedResponseHandler,
333
358
  successfulResponseHandler: createJsonResponseHandler(
@@ -369,7 +394,6 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
369
394
 
370
395
  // provider metadata:
371
396
  const completionTokenDetails = response.usage?.completion_tokens_details;
372
- const promptTokenDetails = response.usage?.prompt_tokens_details;
373
397
  const providerMetadata: SharedV4ProviderMetadata = { openai: {} };
374
398
  if (completionTokenDetails?.accepted_prediction_tokens != null) {
375
399
  providerMetadata.openai.acceptedPredictionTokens =
@@ -414,12 +438,14 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
414
438
  },
415
439
  };
416
440
 
441
+ const url = this.config.url({
442
+ path: '/chat/completions',
443
+ modelId: this.modelId,
444
+ });
445
+
417
446
  const { responseHeaders, value: response } = await postJsonToApi({
418
- url: this.config.url({
419
- path: '/chat/completions',
420
- modelId: this.modelId,
421
- }),
422
- headers: combineHeaders(this.config.headers(), options.headers),
447
+ url,
448
+ headers: combineHeaders(this.config.headers?.(), options.headers),
423
449
  body,
424
450
  failedResponseHandler: openaiFailedResponseHandler,
425
451
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -429,15 +455,16 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
429
455
  fetch: this.config.fetch,
430
456
  });
431
457
 
432
- const toolCalls: Array<{
433
- id: string;
434
- type: 'function';
435
- function: {
436
- name: string;
437
- arguments: string;
438
- };
439
- hasFinished: boolean;
440
- }> = [];
458
+ const checkedResponse = await throwIfOpenAIStreamErrorBeforeOutput({
459
+ stream: response,
460
+ getError: chunk => ('error' in chunk ? chunk.error : undefined),
461
+ isOutputChunk: isOpenAIChatOutputChunk,
462
+ url,
463
+ requestBodyValues: body,
464
+ responseHeaders,
465
+ });
466
+
467
+ let toolCallTracker: StreamingToolCallTracker;
441
468
 
442
469
  let finishReason: LanguageModelV4FinishReason = {
443
470
  unified: 'other',
@@ -449,13 +476,17 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
449
476
 
450
477
  const providerMetadata: SharedV4ProviderMetadata = { openai: {} };
451
478
 
452
- return {
453
- stream: response.pipeThrough(
479
+ const result = {
480
+ stream: checkedResponse.pipeThrough(
454
481
  new TransformStream<
455
482
  ParseResult<OpenAIChatChunk>,
456
483
  LanguageModelV4StreamPart
457
484
  >({
458
485
  start(controller) {
486
+ toolCallTracker = new StreamingToolCallTracker(controller, {
487
+ generateId,
488
+ typeValidation: 'if-present',
489
+ });
459
490
  controller.enqueue({ type: 'stream-start', warnings });
460
491
  },
461
492
 
@@ -547,124 +578,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
547
578
 
548
579
  if (delta.tool_calls != null) {
549
580
  for (const toolCallDelta of delta.tool_calls) {
550
- const index = toolCallDelta.index;
551
-
552
- // Tool call start. OpenAI returns all information except the arguments in the first chunk.
553
- if (toolCalls[index] == null) {
554
- if (
555
- toolCallDelta.type != null &&
556
- toolCallDelta.type !== 'function'
557
- ) {
558
- throw new InvalidResponseDataError({
559
- data: toolCallDelta,
560
- message: `Expected 'function' type.`,
561
- });
562
- }
563
-
564
- if (toolCallDelta.id == null) {
565
- throw new InvalidResponseDataError({
566
- data: toolCallDelta,
567
- message: `Expected 'id' to be a string.`,
568
- });
569
- }
570
-
571
- if (toolCallDelta.function?.name == null) {
572
- throw new InvalidResponseDataError({
573
- data: toolCallDelta,
574
- message: `Expected 'function.name' to be a string.`,
575
- });
576
- }
577
-
578
- controller.enqueue({
579
- type: 'tool-input-start',
580
- id: toolCallDelta.id,
581
- toolName: toolCallDelta.function.name,
582
- });
583
-
584
- toolCalls[index] = {
585
- id: toolCallDelta.id,
586
- type: 'function',
587
- function: {
588
- name: toolCallDelta.function.name,
589
- arguments: toolCallDelta.function.arguments ?? '',
590
- },
591
- hasFinished: false,
592
- };
593
-
594
- const toolCall = toolCalls[index];
595
-
596
- if (
597
- toolCall.function?.name != null &&
598
- toolCall.function?.arguments != null
599
- ) {
600
- // send delta if the argument text has already started:
601
- if (toolCall.function.arguments.length > 0) {
602
- controller.enqueue({
603
- type: 'tool-input-delta',
604
- id: toolCall.id,
605
- delta: toolCall.function.arguments,
606
- });
607
- }
608
-
609
- // check if tool call is complete
610
- // (some providers send the full tool call in one chunk):
611
- if (isParsableJson(toolCall.function.arguments)) {
612
- controller.enqueue({
613
- type: 'tool-input-end',
614
- id: toolCall.id,
615
- });
616
-
617
- controller.enqueue({
618
- type: 'tool-call',
619
- toolCallId: toolCall.id ?? generateId(),
620
- toolName: toolCall.function.name,
621
- input: toolCall.function.arguments,
622
- });
623
- toolCall.hasFinished = true;
624
- }
625
- }
626
-
627
- continue;
628
- }
629
-
630
- // existing tool call, merge if not finished
631
- const toolCall = toolCalls[index];
632
-
633
- if (toolCall.hasFinished) {
634
- continue;
635
- }
636
-
637
- if (toolCallDelta.function?.arguments != null) {
638
- toolCall.function!.arguments +=
639
- toolCallDelta.function?.arguments ?? '';
640
- }
641
-
642
- // send delta
643
- controller.enqueue({
644
- type: 'tool-input-delta',
645
- id: toolCall.id,
646
- delta: toolCallDelta.function.arguments ?? '',
647
- });
648
-
649
- // check if tool call is complete
650
- if (
651
- toolCall.function?.name != null &&
652
- toolCall.function?.arguments != null &&
653
- isParsableJson(toolCall.function.arguments)
654
- ) {
655
- controller.enqueue({
656
- type: 'tool-input-end',
657
- id: toolCall.id,
658
- });
659
-
660
- controller.enqueue({
661
- type: 'tool-call',
662
- toolCallId: toolCall.id ?? generateId(),
663
- toolName: toolCall.function.name,
664
- input: toolCall.function.arguments,
665
- });
666
- toolCall.hasFinished = true;
667
- }
581
+ toolCallTracker.processDelta(toolCallDelta);
668
582
  }
669
583
  }
670
584
 
@@ -687,6 +601,8 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
687
601
  controller.enqueue({ type: 'text-end', id: '0' });
688
602
  }
689
603
 
604
+ toolCallTracker.flush();
605
+
690
606
  controller.enqueue({
691
607
  type: 'finish',
692
608
  finishReason,
@@ -699,5 +615,23 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
699
615
  request: { body },
700
616
  response: { headers: responseHeaders },
701
617
  };
618
+
619
+ return result;
620
+ }
621
+ }
622
+
623
+ function isOpenAIChatOutputChunk(chunk: OpenAIChatChunk): boolean {
624
+ if ('error' in chunk) {
625
+ return false;
702
626
  }
627
+
628
+ return chunk.choices.some(choice => {
629
+ const delta = choice.delta;
630
+
631
+ return (
632
+ (delta?.content != null && delta.content.length > 0) ||
633
+ (delta?.tool_calls != null && delta.tool_calls.length > 0) ||
634
+ (delta?.annotations != null && delta.annotations.length > 0)
635
+ );
636
+ });
703
637
  }
@@ -1,9 +1,9 @@
1
1
  import {
2
- LanguageModelV4CallOptions,
3
- SharedV4Warning,
4
2
  UnsupportedFunctionalityError,
3
+ type LanguageModelV4CallOptions,
4
+ type SharedV4Warning,
5
5
  } from '@ai-sdk/provider';
6
- import {
6
+ import type {
7
7
  OpenAIChatToolChoice,
8
8
  OpenAIChatFunctionTool,
9
9
  } from './openai-chat-api';
@@ -1,4 +1,4 @@
1
- import { LanguageModelV4Usage } from '@ai-sdk/provider';
1
+ import type { LanguageModelV4Usage } from '@ai-sdk/provider';
2
2
 
3
3
  export type OpenAICompletionUsage = {
4
4
  prompt_tokens?: number | null;
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  InvalidPromptError,
3
- LanguageModelV4Prompt,
4
3
  UnsupportedFunctionalityError,
4
+ type LanguageModelV4Prompt,
5
5
  } from '@ai-sdk/provider';
6
-
7
6
  export function convertToOpenAICompletionPrompt({
8
7
  prompt,
9
8
  user = 'user',
@@ -1,4 +1,4 @@
1
- import { LanguageModelV4FinishReason } from '@ai-sdk/provider';
1
+ import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';
2
2
 
3
3
  export function mapOpenAIFinishReason(
4
4
  finishReason: string | null | undefined,
@@ -1,7 +1,10 @@
1
1
  import { z } from 'zod/v4';
2
2
  import { openaiErrorDataSchema } from '../openai-error';
3
- import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
4
-
3
+ import {
4
+ lazySchema,
5
+ zodSchema,
6
+ type InferSchema,
7
+ } from '@ai-sdk/provider-utils';
5
8
  // limited version of the schema, focussed on what is needed for the implementation
6
9
  // this approach limits breakages when the API changes and increases efficiency
7
10
  export const openaiCompletionResponseSchema = lazySchema(() =>
@@ -1,4 +1,8 @@
1
- import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
2
6
  import { z } from 'zod/v4';
3
7
 
4
8
  // https://platform.openai.com/docs/models
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  LanguageModelV4,
3
3
  LanguageModelV4CallOptions,
4
4
  LanguageModelV4FinishReason,
@@ -12,32 +12,35 @@ import {
12
12
  combineHeaders,
13
13
  createEventSourceResponseHandler,
14
14
  createJsonResponseHandler,
15
- FetchFunction,
16
15
  parseProviderOptions,
17
- ParseResult,
18
16
  postJsonToApi,
17
+ serializeModelOptions,
18
+ WORKFLOW_DESERIALIZE,
19
+ WORKFLOW_SERIALIZE,
20
+ type FetchFunction,
21
+ type ParseResult,
19
22
  } from '@ai-sdk/provider-utils';
20
23
  import { openaiFailedResponseHandler } from '../openai-error';
24
+ import { throwIfOpenAIStreamErrorBeforeOutput } from '../openai-stream-error';
21
25
  import {
22
26
  convertOpenAICompletionUsage,
23
- OpenAICompletionUsage,
27
+ type OpenAICompletionUsage,
24
28
  } from './convert-openai-completion-usage';
25
29
  import { convertToOpenAICompletionPrompt } from './convert-to-openai-completion-prompt';
26
30
  import { getResponseMetadata } from './get-response-metadata';
27
31
  import { mapOpenAIFinishReason } from './map-openai-finish-reason';
28
32
  import {
29
- OpenAICompletionChunk,
30
33
  openaiCompletionChunkSchema,
31
34
  openaiCompletionResponseSchema,
35
+ type OpenAICompletionChunk,
32
36
  } from './openai-completion-api';
33
37
  import {
34
- OpenAICompletionModelId,
35
38
  openaiLanguageModelCompletionOptions,
36
- } from './openai-completion-options';
37
-
39
+ type OpenAICompletionModelId,
40
+ } from './openai-completion-language-model-options';
38
41
  type OpenAICompletionConfig = {
39
42
  provider: string;
40
- headers: () => Record<string, string | undefined>;
43
+ headers?: () => Record<string, string | undefined>;
41
44
  url: (options: { modelId: string; path: string }) => string;
42
45
  fetch?: FetchFunction;
43
46
  };
@@ -53,6 +56,20 @@ export class OpenAICompletionLanguageModel implements LanguageModelV4 {
53
56
  return this.config.provider.split('.')[0].trim();
54
57
  }
55
58
 
59
+ static [WORKFLOW_SERIALIZE](model: OpenAICompletionLanguageModel) {
60
+ return serializeModelOptions({
61
+ modelId: model.modelId,
62
+ config: model.config,
63
+ });
64
+ }
65
+
66
+ static [WORKFLOW_DESERIALIZE](options: {
67
+ modelId: OpenAICompletionModelId;
68
+ config: OpenAICompletionConfig;
69
+ }) {
70
+ return new OpenAICompletionLanguageModel(options.modelId, options.config);
71
+ }
72
+
56
73
  constructor(
57
74
  modelId: OpenAICompletionModelId,
58
75
  config: OpenAICompletionConfig,
@@ -174,7 +191,7 @@ export class OpenAICompletionLanguageModel implements LanguageModelV4 {
174
191
  path: '/completions',
175
192
  modelId: this.modelId,
176
193
  }),
177
- headers: combineHeaders(this.config.headers(), options.headers),
194
+ headers: combineHeaders(this.config.headers?.(), options.headers),
178
195
  body: args,
179
196
  failedResponseHandler: openaiFailedResponseHandler,
180
197
  successfulResponseHandler: createJsonResponseHandler(
@@ -224,12 +241,14 @@ export class OpenAICompletionLanguageModel implements LanguageModelV4 {
224
241
  },
225
242
  };
226
243
 
244
+ const url = this.config.url({
245
+ path: '/completions',
246
+ modelId: this.modelId,
247
+ });
248
+
227
249
  const { responseHeaders, value: response } = await postJsonToApi({
228
- url: this.config.url({
229
- path: '/completions',
230
- modelId: this.modelId,
231
- }),
232
- headers: combineHeaders(this.config.headers(), options.headers),
250
+ url,
251
+ headers: combineHeaders(this.config.headers?.(), options.headers),
233
252
  body,
234
253
  failedResponseHandler: openaiFailedResponseHandler,
235
254
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -239,6 +258,15 @@ export class OpenAICompletionLanguageModel implements LanguageModelV4 {
239
258
  fetch: this.config.fetch,
240
259
  });
241
260
 
261
+ const checkedResponse = await throwIfOpenAIStreamErrorBeforeOutput({
262
+ stream: response,
263
+ getError: chunk => ('error' in chunk ? chunk.error : undefined),
264
+ isOutputChunk: isOpenAICompletionOutputChunk,
265
+ url,
266
+ requestBodyValues: body,
267
+ responseHeaders,
268
+ });
269
+
242
270
  let finishReason: LanguageModelV4FinishReason = {
243
271
  unified: 'other',
244
272
  raw: undefined,
@@ -247,8 +275,8 @@ export class OpenAICompletionLanguageModel implements LanguageModelV4 {
247
275
  let usage: OpenAICompletionUsage | undefined = undefined;
248
276
  let isFirstChunk = true;
249
277
 
250
- return {
251
- stream: response.pipeThrough(
278
+ const result = {
279
+ stream: checkedResponse.pipeThrough(
252
280
  new TransformStream<
253
281
  ParseResult<OpenAICompletionChunk>,
254
282
  LanguageModelV4StreamPart
@@ -332,5 +360,13 @@ export class OpenAICompletionLanguageModel implements LanguageModelV4 {
332
360
  request: { body },
333
361
  response: { headers: responseHeaders },
334
362
  };
363
+
364
+ return result;
335
365
  }
336
366
  }
367
+
368
+ function isOpenAICompletionOutputChunk(chunk: OpenAICompletionChunk): boolean {
369
+ return (
370
+ !('error' in chunk) && chunk.choices.some(choice => choice.text.length > 0)
371
+ );
372
+ }
@@ -1,4 +1,8 @@
1
- import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
2
6
  import { z } from 'zod/v4';
3
7
 
4
8
  export type OpenAIEmbeddingModelId =
@@ -1,19 +1,22 @@
1
1
  import {
2
- EmbeddingModelV4,
3
2
  TooManyEmbeddingValuesForCallError,
3
+ type EmbeddingModelV4,
4
4
  } from '@ai-sdk/provider';
5
5
  import {
6
6
  combineHeaders,
7
7
  createJsonResponseHandler,
8
8
  parseProviderOptions,
9
9
  postJsonToApi,
10
+ serializeModelOptions,
11
+ WORKFLOW_DESERIALIZE,
12
+ WORKFLOW_SERIALIZE,
10
13
  } from '@ai-sdk/provider-utils';
11
- import { OpenAIConfig } from '../openai-config';
14
+ import type { OpenAIConfig } from '../openai-config';
12
15
  import { openaiFailedResponseHandler } from '../openai-error';
13
16
  import {
14
- OpenAIEmbeddingModelId,
15
17
  openaiEmbeddingModelOptions,
16
- } from './openai-embedding-options';
18
+ type OpenAIEmbeddingModelId,
19
+ } from './openai-embedding-model-options';
17
20
  import { openaiTextEmbeddingResponseSchema } from './openai-embedding-api';
18
21
 
19
22
  export class OpenAIEmbeddingModel implements EmbeddingModelV4 {
@@ -24,6 +27,20 @@ export class OpenAIEmbeddingModel implements EmbeddingModelV4 {
24
27
 
25
28
  private readonly config: OpenAIConfig;
26
29
 
30
+ static [WORKFLOW_SERIALIZE](model: OpenAIEmbeddingModel) {
31
+ return serializeModelOptions({
32
+ modelId: model.modelId,
33
+ config: model.config,
34
+ });
35
+ }
36
+
37
+ static [WORKFLOW_DESERIALIZE](options: {
38
+ modelId: OpenAIEmbeddingModelId;
39
+ config: OpenAIConfig;
40
+ }) {
41
+ return new OpenAIEmbeddingModel(options.modelId, options.config);
42
+ }
43
+
27
44
  get provider(): string {
28
45
  return this.config.provider;
29
46
  }
@@ -67,7 +84,7 @@ export class OpenAIEmbeddingModel implements EmbeddingModelV4 {
67
84
  path: '/embeddings',
68
85
  modelId: this.modelId,
69
86
  }),
70
- headers: combineHeaders(this.config.headers(), headers),
87
+ headers: combineHeaders(this.config.headers?.(), headers),
71
88
  body: {
72
89
  model: this.modelId,
73
90
  input: values,
@@ -0,0 +1,17 @@
1
+ import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
2
+ import { z } from 'zod/v4';
3
+
4
+ export const openaiFilesResponseSchema = lazySchema(() =>
5
+ zodSchema(
6
+ z.object({
7
+ id: z.string(),
8
+ object: z.string().nullish(),
9
+ bytes: z.number().nullish(),
10
+ created_at: z.number().nullish(),
11
+ filename: z.string().nullish(),
12
+ purpose: z.string().nullish(),
13
+ status: z.string().nullish(),
14
+ expires_at: z.number().nullish(),
15
+ }),
16
+ ),
17
+ );
@@ -0,0 +1,22 @@
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
6
+ import { z } from 'zod/v4';
7
+
8
+ export const openaiFilesOptionsSchema = lazySchema(() =>
9
+ zodSchema(
10
+ z.object({
11
+ /*
12
+ * Required by the OpenAI API, but optional here because
13
+ * the SDK defaults to "assistants" — by far the most common
14
+ * purpose when uploading files in this context.
15
+ */
16
+ purpose: z.string().optional(),
17
+ expiresAfter: z.number().optional(),
18
+ }),
19
+ ),
20
+ );
21
+
22
+ export type OpenAIFilesOptions = InferSchema<typeof openaiFilesOptionsSchema>;