@ai-sdk/openai 4.0.6 → 4.0.8

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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @ai-sdk/openai
2
2
 
3
+ ## 4.0.8
4
+
5
+ ### Patch Changes
6
+
7
+ - b51ed36: Send inline image file parts in OpenAI chat requests as data URLs instead of bare base64 strings.
8
+
9
+ ## 4.0.7
10
+
11
+ ### Patch Changes
12
+
13
+ - 5c5c0f5: Add experimental streaming transcription support for transcription models, including OpenAI `gpt-realtime-whisper` and xAI WebSocket STT.
14
+ - Updated dependencies [5c5c0f5]
15
+ - @ai-sdk/provider@4.0.2
16
+ - @ai-sdk/provider-utils@5.0.5
17
+
3
18
  ## 4.0.6
4
19
 
5
20
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _ai_sdk_provider from '@ai-sdk/provider';
2
2
  import { JSONValue, ProviderV4, LanguageModelV4, EmbeddingModelV4, ImageModelV4, TranscriptionModelV4, SpeechModelV4, Experimental_RealtimeFactoryV4, FilesV4, SkillsV4, Experimental_RealtimeModelV4, Experimental_RealtimeModelV4ClientSecretOptions, Experimental_RealtimeModelV4ClientSecretResult, Experimental_RealtimeModelV4ServerEvent, Experimental_RealtimeModelV4ClientEvent, Experimental_RealtimeModelV4SessionConfig } from '@ai-sdk/provider';
3
3
  import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';
4
- import { InferSchema, FetchFunction } from '@ai-sdk/provider-utils';
4
+ import { InferSchema, FetchFunction, WebSocketConstructor } from '@ai-sdk/provider-utils';
5
5
 
6
6
  type OpenAIChatModelId = 'o1' | 'o1-2024-12-17' | 'o3-mini' | 'o3-mini-2025-01-31' | 'o3' | 'o3-2025-04-16' | 'o4-mini' | 'o4-mini-2025-04-16' | 'gpt-4.1' | 'gpt-4.1-2025-04-14' | 'gpt-4.1-mini' | 'gpt-4.1-mini-2025-04-14' | 'gpt-4.1-nano' | 'gpt-4.1-nano-2025-04-14' | 'gpt-4o' | 'gpt-4o-2024-05-13' | 'gpt-4o-2024-08-06' | 'gpt-4o-2024-11-20' | 'gpt-4o-audio-preview' | 'gpt-4o-audio-preview-2024-12-17' | 'gpt-4o-audio-preview-2025-06-03' | 'gpt-4o-mini' | 'gpt-4o-mini-2024-07-18' | 'gpt-4o-mini-audio-preview' | 'gpt-4o-mini-audio-preview-2024-12-17' | 'gpt-4o-search-preview' | 'gpt-4o-search-preview-2025-03-11' | 'gpt-4o-mini-search-preview' | 'gpt-4o-mini-search-preview-2025-03-11' | 'gpt-3.5-turbo-0125' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-1106' | 'gpt-3.5-turbo-16k' | 'gpt-5' | 'gpt-5-2025-08-07' | 'gpt-5-mini' | 'gpt-5-mini-2025-08-07' | 'gpt-5-nano' | 'gpt-5-nano-2025-08-07' | 'gpt-5-chat-latest' | 'gpt-5.1' | 'gpt-5.1-2025-11-13' | 'gpt-5.1-chat-latest' | 'gpt-5.2' | 'gpt-5.2-2025-12-11' | 'gpt-5.2-chat-latest' | 'gpt-5.2-pro' | 'gpt-5.2-pro-2025-12-11' | 'gpt-5.3-chat-latest' | 'gpt-5.4' | 'gpt-5.4-2026-03-05' | 'gpt-5.4-mini' | 'gpt-5.4-mini-2026-03-17' | 'gpt-5.4-nano' | 'gpt-5.4-nano-2026-03-17' | 'gpt-5.4-pro' | 'gpt-5.4-pro-2026-03-05' | 'gpt-5.5' | 'gpt-5.5-2026-04-23' | (string & {});
7
7
  declare const openaiLanguageModelChatOptions: _ai_sdk_provider_utils.LazySchema<{
@@ -1103,13 +1103,17 @@ declare const openaiSpeechModelOptionsSchema: _ai_sdk_provider_utils.LazySchema<
1103
1103
  }>;
1104
1104
  type OpenAISpeechModelOptions = InferSchema<typeof openaiSpeechModelOptionsSchema>;
1105
1105
 
1106
- type OpenAITranscriptionModelId = 'whisper-1' | 'gpt-4o-mini-transcribe' | 'gpt-4o-mini-transcribe-2025-03-20' | 'gpt-4o-mini-transcribe-2025-12-15' | 'gpt-4o-transcribe' | 'gpt-4o-transcribe-diarize' | (string & {});
1106
+ type OpenAITranscriptionModelId = 'whisper-1' | 'gpt-4o-mini-transcribe' | 'gpt-4o-mini-transcribe-2025-03-20' | 'gpt-4o-mini-transcribe-2025-12-15' | 'gpt-4o-transcribe' | 'gpt-4o-transcribe-diarize' | 'gpt-realtime-whisper' | (string & {});
1107
1107
  declare const openAITranscriptionModelOptions: _ai_sdk_provider_utils.LazySchema<{
1108
1108
  include?: string[] | undefined;
1109
1109
  language?: string | undefined;
1110
1110
  prompt?: string | undefined;
1111
1111
  temperature?: number | undefined;
1112
1112
  timestampGranularities?: ("word" | "segment")[] | undefined;
1113
+ streaming?: {
1114
+ delay?: "minimal" | "low" | "medium" | "high" | "xhigh" | undefined;
1115
+ include?: string[] | undefined;
1116
+ } | undefined;
1113
1117
  }>;
1114
1118
  type OpenAITranscriptionModelOptions = InferSchema<typeof openAITranscriptionModelOptions>;
1115
1119
 
@@ -1211,6 +1215,11 @@ interface OpenAIProviderSettings {
1211
1215
  * or to provide a custom fetch implementation for e.g. testing.
1212
1216
  */
1213
1217
  fetch?: FetchFunction;
1218
+ /**
1219
+ * Custom WebSocket implementation. This is useful for testing or for
1220
+ * runtimes that need a WebSocket constructor with header support.
1221
+ */
1222
+ webSocket?: WebSocketConstructor;
1214
1223
  }
1215
1224
  /**
1216
1225
  * Create an OpenAI provider instance.
package/dist/index.js CHANGED
@@ -304,7 +304,7 @@ function convertToOpenAIChatMessages({
304
304
  return {
305
305
  type: "image_url",
306
306
  image_url: {
307
- url: part.data.type === "url" ? part.data.url.toString() : convertToBase64(part.data.data),
307
+ url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
308
308
  detail: (_b = (_a2 = part.providerOptions) == null ? void 0 : _a2.openai) == null ? void 0 : _b.imageDetail
309
309
  }
310
310
  };
@@ -7645,14 +7645,22 @@ var OpenAISpeechModel = class _OpenAISpeechModel {
7645
7645
  };
7646
7646
 
7647
7647
  // src/transcription/openai-transcription-model.ts
7648
+ import {
7649
+ UnsupportedFunctionalityError as UnsupportedFunctionalityError6
7650
+ } from "@ai-sdk/provider";
7648
7651
  import {
7649
7652
  combineHeaders as combineHeaders8,
7650
7653
  convertBase64ToUint8Array as convertBase64ToUint8Array2,
7654
+ convertToBase64 as convertToBase643,
7651
7655
  createJsonResponseHandler as createJsonResponseHandler7,
7656
+ getWebSocketConstructor,
7652
7657
  mediaTypeToExtension,
7653
7658
  parseProviderOptions as parseProviderOptions9,
7654
7659
  postFormDataToApi as postFormDataToApi3,
7660
+ readWebSocketMessageText,
7661
+ safeParseJSON,
7655
7662
  serializeModelOptions as serializeModelOptions7,
7663
+ toWebSocketUrl,
7656
7664
  WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE7,
7657
7665
  WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE7
7658
7666
  } from "@ai-sdk/provider-utils";
@@ -7721,12 +7729,28 @@ var openAITranscriptionModelOptions = lazySchema26(
7721
7729
  * The timestamp granularities to populate for this transcription.
7722
7730
  * @default ['segment']
7723
7731
  */
7724
- timestampGranularities: z28.array(z28.enum(["word", "segment"])).default(["segment"]).optional()
7732
+ timestampGranularities: z28.array(z28.enum(["word", "segment"])).default(["segment"]).optional(),
7733
+ /**
7734
+ * Options for streaming transcription models such as `gpt-realtime-whisper`.
7735
+ */
7736
+ streaming: z28.object({
7737
+ /**
7738
+ * Latency/accuracy tradeoff for realtime transcription.
7739
+ */
7740
+ delay: z28.enum(["minimal", "low", "medium", "high", "xhigh"]).optional(),
7741
+ /**
7742
+ * Additional fields to include in realtime transcription events.
7743
+ */
7744
+ include: z28.array(z28.string()).optional()
7745
+ }).optional()
7725
7746
  })
7726
7747
  )
7727
7748
  );
7728
7749
 
7729
7750
  // src/transcription/openai-transcription-model.ts
7751
+ function isRealtimeTranscriptionModelId(modelId) {
7752
+ return modelId === "gpt-realtime-whisper" || modelId.startsWith("gpt-realtime-whisper-");
7753
+ }
7730
7754
  var languageMap = {
7731
7755
  afrikaans: "af",
7732
7756
  arabic: "ar",
@@ -7863,6 +7887,11 @@ var OpenAITranscriptionModel = class _OpenAITranscriptionModel {
7863
7887
  }
7864
7888
  async doGenerate(options) {
7865
7889
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7890
+ if (isRealtimeTranscriptionModelId(this.modelId)) {
7891
+ throw new UnsupportedFunctionalityError6({
7892
+ functionality: `non-streaming transcription with ${this.modelId}`
7893
+ });
7894
+ }
7866
7895
  const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
7867
7896
  const { formData, warnings } = await this.getArgs(options);
7868
7897
  const {
@@ -7906,7 +7935,235 @@ var OpenAITranscriptionModel = class _OpenAITranscriptionModel {
7906
7935
  }
7907
7936
  };
7908
7937
  }
7938
+ async doStream(options) {
7939
+ var _a, _b, _c, _d, _e, _f, _g;
7940
+ if (!isRealtimeTranscriptionModelId(this.modelId)) {
7941
+ throw new UnsupportedFunctionalityError6({
7942
+ functionality: `streaming transcription with ${this.modelId}`
7943
+ });
7944
+ }
7945
+ const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
7946
+ const openAIOptions = await parseProviderOptions9({
7947
+ provider: "openai",
7948
+ providerOptions: options.providerOptions,
7949
+ schema: openAITranscriptionModelOptions
7950
+ });
7951
+ const warnings = [];
7952
+ const rawOpenAIOptions = (_e = (_d = options.providerOptions) == null ? void 0 : _d.openai) != null ? _e : {};
7953
+ for (const option of [
7954
+ "include",
7955
+ "prompt",
7956
+ "temperature",
7957
+ "timestampGranularities"
7958
+ ]) {
7959
+ if (rawOpenAIOptions[option] != null) {
7960
+ warnings.push({
7961
+ type: "unsupported",
7962
+ feature: `providerOptions.openai.${option}`,
7963
+ details: `OpenAI streaming transcription does not support ${option}.`
7964
+ });
7965
+ }
7966
+ }
7967
+ const headers = combineHeaders8((_g = (_f = this.config).headers) == null ? void 0 : _g.call(_f), options.headers);
7968
+ const sessionUpdate = buildOpenAIRealtimeTranscriptionSession({
7969
+ modelId: this.modelId,
7970
+ inputAudioFormat: options.inputAudioFormat,
7971
+ providerOptions: openAIOptions
7972
+ });
7973
+ return {
7974
+ request: { body: sessionUpdate },
7975
+ response: {
7976
+ timestamp: currentDate,
7977
+ modelId: this.modelId
7978
+ },
7979
+ stream: createOpenAIRealtimeTranscriptionStream({
7980
+ webSocket: this.config.webSocket,
7981
+ url: toWebSocketUrl(
7982
+ this.config.url({
7983
+ path: "/realtime?intent=transcription",
7984
+ modelId: this.modelId
7985
+ })
7986
+ ),
7987
+ headers,
7988
+ sessionUpdate,
7989
+ language: openAIOptions == null ? void 0 : openAIOptions.language,
7990
+ warnings,
7991
+ audio: options.audio,
7992
+ abortSignal: options.abortSignal,
7993
+ includeRawChunks: options.includeRawChunks
7994
+ })
7995
+ };
7996
+ }
7909
7997
  };
7998
+ function createOpenAIRealtimeTranscriptionStream({
7999
+ webSocket,
8000
+ url,
8001
+ headers,
8002
+ sessionUpdate,
8003
+ language,
8004
+ warnings,
8005
+ audio,
8006
+ abortSignal,
8007
+ includeRawChunks
8008
+ }) {
8009
+ let finished = false;
8010
+ let cleanup = () => {
8011
+ };
8012
+ return new ReadableStream({
8013
+ start: (controller) => {
8014
+ const WebSocketConstructor = getWebSocketConstructor(webSocket);
8015
+ const ws = new WebSocketConstructor(
8016
+ url,
8017
+ getOpenAIRealtimeProtocols(headers),
8018
+ { headers }
8019
+ );
8020
+ let audioReader;
8021
+ cleanup = (closeCode) => {
8022
+ abortSignal == null ? void 0 : abortSignal.removeEventListener("abort", abort);
8023
+ void (audioReader == null ? void 0 : audioReader.cancel().catch(() => {
8024
+ }));
8025
+ try {
8026
+ ws.close(closeCode);
8027
+ } catch (e) {
8028
+ }
8029
+ };
8030
+ const finishWithError = (error) => {
8031
+ if (finished) return;
8032
+ finished = true;
8033
+ cleanup();
8034
+ controller.error(error);
8035
+ };
8036
+ const finish = (text, id) => {
8037
+ if (finished) return;
8038
+ finished = true;
8039
+ if (id != null) {
8040
+ controller.enqueue({ type: "transcript-final", id, text });
8041
+ }
8042
+ controller.enqueue({
8043
+ type: "finish",
8044
+ text,
8045
+ segments: [],
8046
+ language
8047
+ });
8048
+ controller.close();
8049
+ cleanup(1e3);
8050
+ };
8051
+ const abort = () => {
8052
+ var _a;
8053
+ finishWithError((_a = abortSignal == null ? void 0 : abortSignal.reason) != null ? _a : new Error("Aborted"));
8054
+ };
8055
+ if (abortSignal == null ? void 0 : abortSignal.aborted) {
8056
+ abort();
8057
+ return;
8058
+ }
8059
+ abortSignal == null ? void 0 : abortSignal.addEventListener("abort", abort, { once: true });
8060
+ const sendAudio = async () => {
8061
+ audioReader = audio.getReader();
8062
+ try {
8063
+ while (true) {
8064
+ const { done, value } = await audioReader.read();
8065
+ if (done || finished) break;
8066
+ ws.send(
8067
+ JSON.stringify({
8068
+ type: "input_audio_buffer.append",
8069
+ audio: convertToBase643(value)
8070
+ })
8071
+ );
8072
+ }
8073
+ } finally {
8074
+ audioReader.releaseLock();
8075
+ }
8076
+ if (!finished) {
8077
+ ws.send(JSON.stringify({ type: "input_audio_buffer.commit" }));
8078
+ }
8079
+ };
8080
+ ws.onopen = () => {
8081
+ controller.enqueue({ type: "stream-start", warnings });
8082
+ ws.send(JSON.stringify(sessionUpdate));
8083
+ void sendAudio().catch(finishWithError);
8084
+ };
8085
+ ws.onmessage = (event) => {
8086
+ void readWebSocketMessageText(event.data).then(async (text) => {
8087
+ var _a, _b, _c, _d;
8088
+ const parsed = await safeParseJSON({ text });
8089
+ if (!parsed.success) return;
8090
+ const raw = parsed.value;
8091
+ if (includeRawChunks) {
8092
+ controller.enqueue({ type: "raw", rawValue: raw });
8093
+ }
8094
+ switch (raw.type) {
8095
+ case "conversation.item.input_audio_transcription.delta": {
8096
+ controller.enqueue({
8097
+ type: "transcript-delta",
8098
+ id: raw.item_id,
8099
+ delta: (_a = raw.delta) != null ? _a : ""
8100
+ });
8101
+ break;
8102
+ }
8103
+ case "conversation.item.input_audio_transcription.completed": {
8104
+ finish((_b = raw.transcript) != null ? _b : "", raw.item_id);
8105
+ break;
8106
+ }
8107
+ case "error": {
8108
+ finishWithError(
8109
+ new Error((_d = (_c = raw.error) == null ? void 0 : _c.message) != null ? _d : "OpenAI realtime error")
8110
+ );
8111
+ break;
8112
+ }
8113
+ }
8114
+ }).catch(finishWithError);
8115
+ };
8116
+ ws.onerror = () => {
8117
+ finishWithError(new Error("OpenAI realtime transcription error"));
8118
+ };
8119
+ ws.onclose = () => {
8120
+ if (finished) return;
8121
+ finished = true;
8122
+ cleanup();
8123
+ controller.close();
8124
+ };
8125
+ },
8126
+ cancel: () => {
8127
+ if (finished) return;
8128
+ finished = true;
8129
+ cleanup();
8130
+ }
8131
+ });
8132
+ }
8133
+ function buildOpenAIRealtimeTranscriptionSession({
8134
+ modelId,
8135
+ inputAudioFormat,
8136
+ providerOptions
8137
+ }) {
8138
+ var _a, _b;
8139
+ return {
8140
+ type: "session.update",
8141
+ session: {
8142
+ type: "transcription",
8143
+ audio: {
8144
+ input: {
8145
+ format: {
8146
+ type: inputAudioFormat.type,
8147
+ ...inputAudioFormat.rate != null ? { rate: inputAudioFormat.rate } : {}
8148
+ },
8149
+ transcription: {
8150
+ model: modelId,
8151
+ ...(providerOptions == null ? void 0 : providerOptions.language) != null ? { language: providerOptions.language } : {},
8152
+ ...((_a = providerOptions == null ? void 0 : providerOptions.streaming) == null ? void 0 : _a.delay) != null ? { delay: providerOptions.streaming.delay } : {}
8153
+ },
8154
+ turn_detection: null
8155
+ }
8156
+ },
8157
+ ...((_b = providerOptions == null ? void 0 : providerOptions.streaming) == null ? void 0 : _b.include) != null ? { include: providerOptions.streaming.include } : {}
8158
+ }
8159
+ };
8160
+ }
8161
+ function getOpenAIRealtimeProtocols(headers) {
8162
+ var _a;
8163
+ const authorization = (_a = headers.Authorization) != null ? _a : headers.authorization;
8164
+ const token = (authorization == null ? void 0 : authorization.startsWith("Bearer ")) ? authorization.slice("Bearer ".length) : void 0;
8165
+ return token == null ? ["realtime"] : ["realtime", `openai-insecure-api-key.${token}`];
8166
+ }
7910
8167
 
7911
8168
  // src/skills/openai-skills.ts
7912
8169
  import {
@@ -7993,7 +8250,7 @@ var OpenAISkills = class {
7993
8250
  };
7994
8251
 
7995
8252
  // src/version.ts
7996
- var VERSION = true ? "4.0.6" : "0.0.0-test";
8253
+ var VERSION = true ? "4.0.8" : "0.0.0-test";
7997
8254
 
7998
8255
  // src/openai-provider.ts
7999
8256
  function createOpenAI(options = {}) {
@@ -8046,7 +8303,8 @@ function createOpenAI(options = {}) {
8046
8303
  provider: `${providerName}.transcription`,
8047
8304
  url: ({ path }) => `${baseURL}${path}`,
8048
8305
  headers: getHeaders,
8049
- fetch: options.fetch
8306
+ fetch: options.fetch,
8307
+ webSocket: options.webSocket
8050
8308
  });
8051
8309
  const createSpeechModel = (modelId) => new OpenAISpeechModel(modelId, {
8052
8310
  provider: `${providerName}.speech`,