@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.
Files changed (34) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/index.d.ts +79 -15
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +788 -423
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +389 -9
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +629 -380
  9. package/dist/internal/index.js.map +1 -1
  10. package/docs/03-openai.mdx +224 -18
  11. package/package.json +3 -3
  12. package/src/chat/convert-to-openai-chat-messages.ts +29 -4
  13. package/src/chat/map-openai-finish-reason.ts +11 -4
  14. package/src/chat/openai-chat-api.ts +36 -0
  15. package/src/chat/openai-chat-language-model-options.ts +13 -0
  16. package/src/chat/openai-chat-language-model.ts +111 -22
  17. package/src/chat/openai-chat-prompt.ts +4 -0
  18. package/src/image/openai-image-model-options.ts +48 -0
  19. package/src/image/openai-image-model.ts +49 -4
  20. package/src/index.ts +1 -0
  21. package/src/internal/index.ts +4 -0
  22. package/src/live/openai-realtime-model-live.ts +21 -2
  23. package/src/openai-batch.ts +8 -0
  24. package/src/openai-model-catalog.ts +3 -1
  25. package/src/openai-tools.ts +4 -3
  26. package/src/realtime/openai-realtime-event-mapper.ts +172 -11
  27. package/src/realtime/openai-realtime-model.ts +57 -3
  28. package/src/responses/map-openai-responses-finish-reason.ts +7 -1
  29. package/src/responses/openai-responses-api.ts +90 -49
  30. package/src/responses/openai-responses-language-model.ts +56 -1
  31. package/src/responses/openai-responses-prepare-tools.ts +18 -1
  32. package/src/tool/code-interpreter.ts +38 -2
  33. package/src/tool/mcp.ts +27 -8
  34. package/src/transcription/openai-transcription-model.ts +43 -8
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL, StreamingToolCallTracker, WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE, combineHeaders, connectToWebSocket, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertInlineFileDataToUint8Array, convertToBase64, convertToFormData, convertUint8ArrayToBase64, createBinaryResponseHandler, createBinaryStreamResponseHandler, createEventSourceResponseHandler, createJsonErrorResponseHandler, createJsonLinesResponseHandler, createJsonResponseHandler, createLanguageModelResponseMetadata, createLanguageModelResponseMetadata as getResponseMetadata, createNullLanguageModelUsage, createProviderDefinedToolFactory, createProviderDefinedToolFactoryWithOutputSchema, createProviderExecutedToolFactory, createProviderStreamError, createToolNameMapping, deleteFromApi, detectMediaType, downloadBlob, encodePathSegment, experimental_toolCaller, generateId, getFromApi, getTopLevelMediaType, isCustomReasoning, isFullMediaType, isNonNullable, isRecord, lazySchema, loadApiKey, loadOptionalSetting, mapReasoningToProviderEffort, mediaTypeToExtension, normalizeBatchRequestCounts, parseJSON, parseProviderOptions, postFormDataToApi, postJsonToApi, postMultipartStreamToApi, postToApi, resolve, resolveFullMediaType, resolveProviderReference, resolveWith, safeParseJSON, safeValidateTypes, secureJsonParse, serializeModelOptions, stringifyWithBigIntAndCycles, toWebSocketUrl, validateBaseURL, validateTypes, waitForWebSocketBufferDrain, withUserAgentSuffix, withoutTrailingSlash, zodSchema } from "@ai-sdk/provider-utils";
1
+ import { EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL, StreamingToolCallTracker, WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE, combineHeaders, connectToWebSocket, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertInlineFileDataToUint8Array, convertToBase64, convertToFormData, convertUint8ArrayToBase64, createBinaryResponseHandler, createBinaryStreamResponseHandler, createEventSourceResponseHandler, createJsonErrorResponseHandler, createJsonLinesResponseHandler, createJsonResponseHandler, createLanguageModelResponseMetadata, createLanguageModelResponseMetadata as getResponseMetadata, createNullLanguageModelUsage, createProviderDefinedToolFactory, createProviderDefinedToolFactoryWithOutputSchema, createProviderExecutedToolFactory, createProviderStreamError, createToolNameMapping, deleteFromApi, detectMediaType, downloadBlob, encodePathSegment, experimental_toolCaller, generateId, getFromApi, getTopLevelMediaType, isCustomReasoning, isFullMediaType, isNonNullable, isRecord, lazySchema, loadApiKey, loadOptionalSetting, mapReasoningToProviderEffort, mediaTypeToExtension, normalizeBatchRequestCounts, parseJSON, parseProviderOptions, postFormDataToApi, postJsonToApi, postMultipartStreamToApi, postToApi, removeUndefinedEntries, resolve, resolveFullMediaType, resolveProviderReference, resolveWith, safeParseJSON, safeValidateTypes, secureJsonParse, serializeModelOptions, stringifyWithBigIntAndCycles, toWebSocketUrl, validateBaseURL, validateTypes, waitForWebSocketBufferDrain, withUserAgentSuffix, withoutTrailingSlash, zodSchema } from "@ai-sdk/provider-utils";
2
2
  import { APICallError, InvalidArgumentError, InvalidPromptError, InvalidResponseDataError, TooManyEmbeddingValuesForCallError, UnsupportedFunctionalityError, isJSONObject } from "@ai-sdk/provider";
3
3
  import { z } from "zod/v4";
4
4
  //#region src/openai-config.ts
@@ -537,6 +537,8 @@ function createGptImageEntry(sizes) {
537
537
  };
538
538
  }
539
539
  const openaiImageModelEntries = {
540
+ "gpt-image-2.6-flare": createGptImageEntry(customImageSizes),
541
+ "gpt-image-2.6-sunburst": createGptImageEntry(customImageSizes),
540
542
  "gpt-image-2.5-flare": createGptImageEntry(customImageSizes),
541
543
  "gpt-image-2.5-sunburst": createGptImageEntry(customImageSizes),
542
544
  "gpt-image-2": createGptImageEntry(gptImageSizes),
@@ -725,7 +727,7 @@ const realtimeModelEntry = {
725
727
  capabilities: { realtime: {
726
728
  conversation: "turn-based",
727
729
  transports: ["websocket"],
728
- connections: ["client-secret-websocket"]
730
+ connections: ["client-secret-websocket", "server-websocket"]
729
731
  } },
730
732
  requestConfig: { realtime: {
731
733
  api: "realtime",
@@ -1004,347 +1006,6 @@ function convertOpenAIChatUsage(usage) {
1004
1006
  };
1005
1007
  }
1006
1008
  //#endregion
1007
- //#region src/chat/convert-to-openai-chat-messages.ts
1008
- function serializeToolCallArguments$1(input) {
1009
- if (typeof input === "string") try {
1010
- const parsed = secureJsonParse(input);
1011
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return "{}";
1012
- return input;
1013
- } catch {
1014
- return "{}";
1015
- }
1016
- return stringifyWithBigIntAndCycles(typeof input === "object" && input !== null && !Array.isArray(input) ? input : {});
1017
- }
1018
- function getPromptCacheBreakpoint$1(providerOptions) {
1019
- return providerOptions?.openai?.promptCacheBreakpoint;
1020
- }
1021
- function convertToOpenAIChatMessages({ prompt, systemMessageMode = "system" }) {
1022
- const messages = [];
1023
- const warnings = [];
1024
- for (const { role, content, providerOptions } of prompt) switch (role) {
1025
- case "system":
1026
- switch (systemMessageMode) {
1027
- case "system": {
1028
- const promptCacheBreakpoint = getPromptCacheBreakpoint$1(providerOptions);
1029
- messages.push({
1030
- role: "system",
1031
- content: promptCacheBreakpoint == null ? content : [{
1032
- type: "text",
1033
- text: content,
1034
- prompt_cache_breakpoint: promptCacheBreakpoint
1035
- }]
1036
- });
1037
- break;
1038
- }
1039
- case "developer": {
1040
- const promptCacheBreakpoint = getPromptCacheBreakpoint$1(providerOptions);
1041
- messages.push({
1042
- role: "developer",
1043
- content: promptCacheBreakpoint == null ? content : [{
1044
- type: "text",
1045
- text: content,
1046
- prompt_cache_breakpoint: promptCacheBreakpoint
1047
- }]
1048
- });
1049
- break;
1050
- }
1051
- case "remove":
1052
- warnings.push({
1053
- type: "other",
1054
- message: "system messages are removed for this model"
1055
- });
1056
- break;
1057
- default: throw new Error(`Unsupported system message mode: ${systemMessageMode}`);
1058
- }
1059
- break;
1060
- case "user":
1061
- if (content.length === 1 && content[0].type === "text" && getPromptCacheBreakpoint$1(content[0].providerOptions) == null) {
1062
- messages.push({
1063
- role: "user",
1064
- content: content[0].text
1065
- });
1066
- break;
1067
- }
1068
- messages.push({
1069
- role: "user",
1070
- content: content.map((part, index) => {
1071
- switch (part.type) {
1072
- case "text": {
1073
- const promptCacheBreakpoint = getPromptCacheBreakpoint$1(part.providerOptions);
1074
- return {
1075
- type: "text",
1076
- text: part.text,
1077
- ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1078
- };
1079
- }
1080
- case "file": {
1081
- const promptCacheBreakpoint = getPromptCacheBreakpoint$1(part.providerOptions);
1082
- switch (part.data.type) {
1083
- case "reference": return {
1084
- type: "file",
1085
- file: { file_id: resolveProviderReference({
1086
- reference: part.data.reference,
1087
- provider: "openai"
1088
- }) },
1089
- ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1090
- };
1091
- case "text": throw new UnsupportedFunctionalityError({ functionality: "text file parts" });
1092
- case "url":
1093
- case "data": {
1094
- const topLevel = getTopLevelMediaType(part.mediaType);
1095
- if (topLevel === "image") return {
1096
- type: "image_url",
1097
- image_url: {
1098
- url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
1099
- detail: part.providerOptions?.openai?.imageDetail
1100
- },
1101
- ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1102
- };
1103
- else if (topLevel === "audio") {
1104
- if (part.data.type === "url") throw new UnsupportedFunctionalityError({ functionality: "audio file parts with URLs" });
1105
- const fullMediaType = resolveFullMediaType({ part });
1106
- switch (fullMediaType) {
1107
- case "audio/wav": return {
1108
- type: "input_audio",
1109
- input_audio: {
1110
- data: convertToBase64(part.data.data),
1111
- format: "wav"
1112
- },
1113
- ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1114
- };
1115
- case "audio/mp3":
1116
- case "audio/mpeg": return {
1117
- type: "input_audio",
1118
- input_audio: {
1119
- data: convertToBase64(part.data.data),
1120
- format: "mp3"
1121
- },
1122
- ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1123
- };
1124
- default: throw new UnsupportedFunctionalityError({ functionality: `audio content parts with media type ${fullMediaType}` });
1125
- }
1126
- }
1127
- {
1128
- const fullMediaType = resolveFullMediaType({ part });
1129
- if (fullMediaType !== "application/pdf") throw new UnsupportedFunctionalityError({ functionality: `file part media type ${fullMediaType}` });
1130
- if (part.data.type === "url") throw new UnsupportedFunctionalityError({ functionality: "PDF file parts with URLs" });
1131
- return {
1132
- type: "file",
1133
- file: {
1134
- filename: part.filename ?? `part-${index}.pdf`,
1135
- file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`
1136
- },
1137
- ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1138
- };
1139
- }
1140
- }
1141
- }
1142
- }
1143
- }
1144
- })
1145
- });
1146
- break;
1147
- case "assistant": {
1148
- let text = "";
1149
- const textParts = [];
1150
- let hasPromptCacheBreakpoint = false;
1151
- const toolCalls = [];
1152
- for (const part of content) switch (part.type) {
1153
- case "text": {
1154
- const promptCacheBreakpoint = getPromptCacheBreakpoint$1(part.providerOptions);
1155
- text += part.text;
1156
- textParts.push({
1157
- type: "text",
1158
- text: part.text,
1159
- ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1160
- });
1161
- hasPromptCacheBreakpoint ||= promptCacheBreakpoint != null;
1162
- break;
1163
- }
1164
- case "tool-call": toolCalls.push({
1165
- id: part.toolCallId,
1166
- type: "function",
1167
- function: {
1168
- name: part.toolName,
1169
- arguments: serializeToolCallArguments$1(part.input)
1170
- }
1171
- });
1172
- }
1173
- messages.push({
1174
- role: "assistant",
1175
- content: hasPromptCacheBreakpoint ? textParts : toolCalls.length > 0 ? text || null : text,
1176
- tool_calls: toolCalls.length > 0 ? toolCalls : void 0
1177
- });
1178
- break;
1179
- }
1180
- case "tool":
1181
- for (const toolResponse of content) {
1182
- if (toolResponse.type === "tool-approval-response") continue;
1183
- const output = toolResponse.output;
1184
- const promptCacheBreakpoint = (output.type === "content" ? output.value.map((part) => getPromptCacheBreakpoint$1(part.providerOptions)).find((breakpoint) => breakpoint != null) : getPromptCacheBreakpoint$1(output.providerOptions)) ?? getPromptCacheBreakpoint$1(toolResponse.providerOptions);
1185
- let contentValue;
1186
- switch (output.type) {
1187
- case "text":
1188
- contentValue = output.value;
1189
- break;
1190
- case "error-text":
1191
- case "error-json":
1192
- contentValue = stringifyWithBigIntAndCycles({ error: output.value });
1193
- break;
1194
- case "execution-denied":
1195
- contentValue = output.reason ?? "Tool call execution denied.";
1196
- break;
1197
- case "content":
1198
- case "json": contentValue = stringifyWithBigIntAndCycles(output.value);
1199
- }
1200
- messages.push({
1201
- role: "tool",
1202
- tool_call_id: toolResponse.toolCallId,
1203
- content: promptCacheBreakpoint == null ? contentValue : [{
1204
- type: "text",
1205
- text: contentValue,
1206
- prompt_cache_breakpoint: promptCacheBreakpoint
1207
- }]
1208
- });
1209
- }
1210
- break;
1211
- default: throw new Error(`Unsupported role: ${role}`);
1212
- }
1213
- return {
1214
- messages,
1215
- warnings
1216
- };
1217
- }
1218
- //#endregion
1219
- //#region src/chat/get-response-metadata.ts
1220
- function getResponseMetadata$1({ id, model, created }) {
1221
- return createLanguageModelResponseMetadata({
1222
- id,
1223
- model,
1224
- created: created || void 0
1225
- });
1226
- }
1227
- //#endregion
1228
- //#region src/chat/map-openai-finish-reason.ts
1229
- function mapOpenAIFinishReason$1(finishReason) {
1230
- switch (finishReason) {
1231
- case "stop": return "stop";
1232
- case "length": return "length";
1233
- case "content_filter": return "content-filter";
1234
- case "function_call":
1235
- case "tool_calls": return "tool-calls";
1236
- default: return "other";
1237
- }
1238
- }
1239
- //#endregion
1240
- //#region src/chat/openai-chat-api.ts
1241
- const openaiChatResponseSchema = lazySchema(() => zodSchema(z.object({
1242
- id: z.string().nullish(),
1243
- created: z.number().nullish(),
1244
- model: z.string().nullish(),
1245
- choices: z.array(z.object({
1246
- message: z.object({
1247
- role: z.literal("assistant").nullish(),
1248
- content: z.string().nullish(),
1249
- audio: z.object({ transcript: z.string().nullish() }).nullish(),
1250
- tool_calls: z.array(z.object({
1251
- id: z.string().nullish(),
1252
- type: z.literal("function"),
1253
- function: z.object({
1254
- name: z.string(),
1255
- arguments: z.string()
1256
- })
1257
- })).nullish(),
1258
- annotations: z.array(z.object({
1259
- type: z.literal("url_citation"),
1260
- url_citation: z.object({
1261
- start_index: z.number(),
1262
- end_index: z.number(),
1263
- url: z.string(),
1264
- title: z.string()
1265
- })
1266
- })).nullish()
1267
- }),
1268
- index: z.number(),
1269
- logprobs: z.object({ content: z.array(z.object({
1270
- token: z.string(),
1271
- logprob: z.number(),
1272
- top_logprobs: z.array(z.object({
1273
- token: z.string(),
1274
- logprob: z.number()
1275
- }))
1276
- })).nullish() }).nullish(),
1277
- finish_reason: z.string().nullish()
1278
- })),
1279
- usage: z.object({
1280
- prompt_tokens: z.number().nullish(),
1281
- completion_tokens: z.number().nullish(),
1282
- total_tokens: z.number().nullish(),
1283
- prompt_tokens_details: z.object({
1284
- cached_tokens: z.number().nullish(),
1285
- cache_write_tokens: z.number().nullish()
1286
- }).nullish(),
1287
- completion_tokens_details: z.object({
1288
- reasoning_tokens: z.number().nullish(),
1289
- accepted_prediction_tokens: z.number().nullish(),
1290
- rejected_prediction_tokens: z.number().nullish()
1291
- }).nullish()
1292
- }).nullish()
1293
- })));
1294
- const openaiChatChunkSchema = lazySchema(() => zodSchema(z.union([z.object({
1295
- id: z.string().nullish(),
1296
- created: z.number().nullish(),
1297
- model: z.string().nullish(),
1298
- choices: z.array(z.object({
1299
- delta: z.object({
1300
- role: z.enum(["assistant"]).nullish(),
1301
- content: z.string().nullish(),
1302
- tool_calls: z.array(z.object({
1303
- index: z.number(),
1304
- id: z.string().nullish(),
1305
- type: z.literal("function").nullish(),
1306
- function: z.object({
1307
- name: z.string().nullish(),
1308
- arguments: z.string().nullish()
1309
- })
1310
- })).nullish(),
1311
- annotations: z.array(z.object({
1312
- type: z.literal("url_citation"),
1313
- url_citation: z.object({
1314
- start_index: z.number(),
1315
- end_index: z.number(),
1316
- url: z.string(),
1317
- title: z.string()
1318
- })
1319
- })).nullish()
1320
- }).nullish(),
1321
- logprobs: z.object({ content: z.array(z.object({
1322
- token: z.string(),
1323
- logprob: z.number(),
1324
- top_logprobs: z.array(z.object({
1325
- token: z.string(),
1326
- logprob: z.number()
1327
- }))
1328
- })).nullish() }).nullish(),
1329
- finish_reason: z.string().nullish(),
1330
- index: z.number()
1331
- })),
1332
- usage: z.object({
1333
- prompt_tokens: z.number().nullish(),
1334
- completion_tokens: z.number().nullish(),
1335
- total_tokens: z.number().nullish(),
1336
- prompt_tokens_details: z.object({
1337
- cached_tokens: z.number().nullish(),
1338
- cache_write_tokens: z.number().nullish()
1339
- }).nullish(),
1340
- completion_tokens_details: z.object({
1341
- reasoning_tokens: z.number().nullish(),
1342
- accepted_prediction_tokens: z.number().nullish(),
1343
- rejected_prediction_tokens: z.number().nullish()
1344
- }).nullish()
1345
- }).nullish()
1346
- }), openaiErrorDataSchema()])));
1347
- //#endregion
1348
1009
  //#region src/chat/openai-chat-language-model-options.ts
1349
1010
  const openaiLanguageModelChatOptions = lazySchema(() => zodSchema(z.object({
1350
1011
  /**
@@ -1493,6 +1154,388 @@ const openaiLanguageModelChatOptions = lazySchema(() => zodSchema(z.object({
1493
1154
  */
1494
1155
  forceReasoning: z.boolean().optional()
1495
1156
  })));
1157
+ const openaiChatMessageProviderOptions = lazySchema(() => zodSchema(z.object({
1158
+ /** The participant name on a system, developer, user, or assistant message. */
1159
+ name: z.string().optional() })));
1160
+ //#endregion
1161
+ //#region src/chat/convert-to-openai-chat-messages.ts
1162
+ function serializeToolCallArguments$1(input) {
1163
+ if (typeof input === "string") try {
1164
+ const parsed = secureJsonParse(input);
1165
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return "{}";
1166
+ return input;
1167
+ } catch {
1168
+ return "{}";
1169
+ }
1170
+ return stringifyWithBigIntAndCycles(typeof input === "object" && input !== null && !Array.isArray(input) ? input : {});
1171
+ }
1172
+ function getPromptCacheBreakpoint$1(providerOptions) {
1173
+ return providerOptions?.openai?.promptCacheBreakpoint;
1174
+ }
1175
+ async function convertToOpenAIChatMessages({ prompt, systemMessageMode = "system" }) {
1176
+ const messages = [];
1177
+ const warnings = [];
1178
+ for (const { role, content, providerOptions } of prompt) {
1179
+ const name = (await parseProviderOptions({
1180
+ provider: "openai",
1181
+ providerOptions,
1182
+ schema: openaiChatMessageProviderOptions
1183
+ }))?.name;
1184
+ switch (role) {
1185
+ case "system":
1186
+ switch (systemMessageMode) {
1187
+ case "system": {
1188
+ const promptCacheBreakpoint = getPromptCacheBreakpoint$1(providerOptions);
1189
+ messages.push({
1190
+ role: "system",
1191
+ ...name != null && { name },
1192
+ content: promptCacheBreakpoint == null ? content : [{
1193
+ type: "text",
1194
+ text: content,
1195
+ prompt_cache_breakpoint: promptCacheBreakpoint
1196
+ }]
1197
+ });
1198
+ break;
1199
+ }
1200
+ case "developer": {
1201
+ const promptCacheBreakpoint = getPromptCacheBreakpoint$1(providerOptions);
1202
+ messages.push({
1203
+ role: "developer",
1204
+ ...name != null && { name },
1205
+ content: promptCacheBreakpoint == null ? content : [{
1206
+ type: "text",
1207
+ text: content,
1208
+ prompt_cache_breakpoint: promptCacheBreakpoint
1209
+ }]
1210
+ });
1211
+ break;
1212
+ }
1213
+ case "remove":
1214
+ warnings.push({
1215
+ type: "other",
1216
+ message: "system messages are removed for this model"
1217
+ });
1218
+ break;
1219
+ default: throw new Error(`Unsupported system message mode: ${systemMessageMode}`);
1220
+ }
1221
+ break;
1222
+ case "user":
1223
+ if (content.length === 1 && content[0].type === "text" && getPromptCacheBreakpoint$1(content[0].providerOptions) == null) {
1224
+ messages.push({
1225
+ role: "user",
1226
+ content: content[0].text,
1227
+ ...name != null && { name }
1228
+ });
1229
+ break;
1230
+ }
1231
+ messages.push({
1232
+ role: "user",
1233
+ ...name != null && { name },
1234
+ content: content.map((part, index) => {
1235
+ switch (part.type) {
1236
+ case "text": {
1237
+ const promptCacheBreakpoint = getPromptCacheBreakpoint$1(part.providerOptions);
1238
+ return {
1239
+ type: "text",
1240
+ text: part.text,
1241
+ ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1242
+ };
1243
+ }
1244
+ case "file": {
1245
+ const promptCacheBreakpoint = getPromptCacheBreakpoint$1(part.providerOptions);
1246
+ switch (part.data.type) {
1247
+ case "reference": return {
1248
+ type: "file",
1249
+ file: { file_id: resolveProviderReference({
1250
+ reference: part.data.reference,
1251
+ provider: "openai"
1252
+ }) },
1253
+ ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1254
+ };
1255
+ case "text": throw new UnsupportedFunctionalityError({ functionality: "text file parts" });
1256
+ case "url":
1257
+ case "data": {
1258
+ const topLevel = getTopLevelMediaType(part.mediaType);
1259
+ if (topLevel === "image") return {
1260
+ type: "image_url",
1261
+ image_url: {
1262
+ url: part.data.type === "url" ? part.data.url.toString() : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
1263
+ detail: part.providerOptions?.openai?.imageDetail
1264
+ },
1265
+ ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1266
+ };
1267
+ else if (topLevel === "audio") {
1268
+ if (part.data.type === "url") throw new UnsupportedFunctionalityError({ functionality: "audio file parts with URLs" });
1269
+ const fullMediaType = resolveFullMediaType({ part });
1270
+ switch (fullMediaType) {
1271
+ case "audio/wav": return {
1272
+ type: "input_audio",
1273
+ input_audio: {
1274
+ data: convertToBase64(part.data.data),
1275
+ format: "wav"
1276
+ },
1277
+ ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1278
+ };
1279
+ case "audio/mp3":
1280
+ case "audio/mpeg": return {
1281
+ type: "input_audio",
1282
+ input_audio: {
1283
+ data: convertToBase64(part.data.data),
1284
+ format: "mp3"
1285
+ },
1286
+ ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1287
+ };
1288
+ default: throw new UnsupportedFunctionalityError({ functionality: `audio content parts with media type ${fullMediaType}` });
1289
+ }
1290
+ }
1291
+ {
1292
+ const fullMediaType = resolveFullMediaType({ part });
1293
+ if (fullMediaType !== "application/pdf") throw new UnsupportedFunctionalityError({ functionality: `file part media type ${fullMediaType}` });
1294
+ if (part.data.type === "url") throw new UnsupportedFunctionalityError({ functionality: "PDF file parts with URLs" });
1295
+ return {
1296
+ type: "file",
1297
+ file: {
1298
+ filename: part.filename ?? `part-${index}.pdf`,
1299
+ file_data: `data:application/pdf;base64,${convertToBase64(part.data.data)}`
1300
+ },
1301
+ ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1302
+ };
1303
+ }
1304
+ }
1305
+ }
1306
+ }
1307
+ }
1308
+ })
1309
+ });
1310
+ break;
1311
+ case "assistant": {
1312
+ let text = "";
1313
+ const textParts = [];
1314
+ let hasPromptCacheBreakpoint = false;
1315
+ const toolCalls = [];
1316
+ for (const part of content) switch (part.type) {
1317
+ case "text": {
1318
+ const promptCacheBreakpoint = getPromptCacheBreakpoint$1(part.providerOptions);
1319
+ text += part.text;
1320
+ textParts.push({
1321
+ type: "text",
1322
+ text: part.text,
1323
+ ...promptCacheBreakpoint != null && { prompt_cache_breakpoint: promptCacheBreakpoint }
1324
+ });
1325
+ hasPromptCacheBreakpoint ||= promptCacheBreakpoint != null;
1326
+ break;
1327
+ }
1328
+ case "tool-call": toolCalls.push({
1329
+ id: part.toolCallId,
1330
+ type: "function",
1331
+ function: {
1332
+ name: part.toolName,
1333
+ arguments: serializeToolCallArguments$1(part.input)
1334
+ }
1335
+ });
1336
+ }
1337
+ messages.push({
1338
+ role: "assistant",
1339
+ ...name != null && { name },
1340
+ content: hasPromptCacheBreakpoint ? textParts : toolCalls.length > 0 ? text || null : text,
1341
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0
1342
+ });
1343
+ break;
1344
+ }
1345
+ case "tool":
1346
+ if (name != null) warnings.push({
1347
+ type: "unsupported",
1348
+ feature: "message name on tool messages"
1349
+ });
1350
+ for (const toolResponse of content) {
1351
+ if (toolResponse.type === "tool-approval-response") continue;
1352
+ const output = toolResponse.output;
1353
+ const promptCacheBreakpoint = (output.type === "content" ? output.value.map((part) => getPromptCacheBreakpoint$1(part.providerOptions)).find((breakpoint) => breakpoint != null) : getPromptCacheBreakpoint$1(output.providerOptions)) ?? getPromptCacheBreakpoint$1(toolResponse.providerOptions);
1354
+ let contentValue;
1355
+ switch (output.type) {
1356
+ case "text":
1357
+ contentValue = output.value;
1358
+ break;
1359
+ case "error-text":
1360
+ case "error-json":
1361
+ contentValue = stringifyWithBigIntAndCycles({ error: output.value });
1362
+ break;
1363
+ case "execution-denied":
1364
+ contentValue = output.reason ?? "Tool call execution denied.";
1365
+ break;
1366
+ case "content":
1367
+ case "json": contentValue = stringifyWithBigIntAndCycles(output.value);
1368
+ }
1369
+ messages.push({
1370
+ role: "tool",
1371
+ tool_call_id: toolResponse.toolCallId,
1372
+ content: promptCacheBreakpoint == null ? contentValue : [{
1373
+ type: "text",
1374
+ text: contentValue,
1375
+ prompt_cache_breakpoint: promptCacheBreakpoint
1376
+ }]
1377
+ });
1378
+ }
1379
+ break;
1380
+ default: throw new Error(`Unsupported role: ${role}`);
1381
+ }
1382
+ }
1383
+ return {
1384
+ messages,
1385
+ warnings
1386
+ };
1387
+ }
1388
+ //#endregion
1389
+ //#region src/chat/get-response-metadata.ts
1390
+ function getResponseMetadata$1({ id, model, created }) {
1391
+ return createLanguageModelResponseMetadata({
1392
+ id,
1393
+ model,
1394
+ created: created || void 0
1395
+ });
1396
+ }
1397
+ //#endregion
1398
+ //#region src/chat/map-openai-finish-reason.ts
1399
+ function mapOpenAIFinishReason$1({ finishReason, hasRefusal = false }) {
1400
+ switch (finishReason) {
1401
+ case "stop": return hasRefusal ? "content-filter" : "stop";
1402
+ case "length": return "length";
1403
+ case "content_filter": return "content-filter";
1404
+ case "function_call":
1405
+ case "tool_calls": return "tool-calls";
1406
+ default: return "other";
1407
+ }
1408
+ }
1409
+ //#endregion
1410
+ //#region src/chat/openai-chat-api.ts
1411
+ const openaiChatResponseSchema = lazySchema(() => zodSchema(z.object({
1412
+ id: z.string().nullish(),
1413
+ created: z.number().nullish(),
1414
+ model: z.string().nullish(),
1415
+ choices: z.array(z.object({
1416
+ message: z.object({
1417
+ role: z.literal("assistant").nullish(),
1418
+ content: z.string().nullish(),
1419
+ refusal: z.string().nullish(),
1420
+ audio: z.object({ transcript: z.string().nullish() }).nullish(),
1421
+ tool_calls: z.array(z.object({
1422
+ id: z.string().nullish(),
1423
+ type: z.literal("function"),
1424
+ function: z.object({
1425
+ name: z.string(),
1426
+ arguments: z.string()
1427
+ })
1428
+ })).nullish(),
1429
+ annotations: z.array(z.object({
1430
+ type: z.literal("url_citation"),
1431
+ url_citation: z.object({
1432
+ start_index: z.number(),
1433
+ end_index: z.number(),
1434
+ url: z.string(),
1435
+ title: z.string()
1436
+ })
1437
+ })).nullish()
1438
+ }),
1439
+ index: z.number(),
1440
+ logprobs: z.object({
1441
+ content: z.array(z.object({
1442
+ token: z.string(),
1443
+ logprob: z.number(),
1444
+ top_logprobs: z.array(z.object({
1445
+ token: z.string(),
1446
+ logprob: z.number()
1447
+ }))
1448
+ })).nullish(),
1449
+ refusal: z.array(z.object({
1450
+ token: z.string(),
1451
+ logprob: z.number(),
1452
+ top_logprobs: z.array(z.object({
1453
+ token: z.string(),
1454
+ logprob: z.number()
1455
+ }))
1456
+ })).nullish()
1457
+ }).nullish(),
1458
+ finish_reason: z.string().nullish()
1459
+ })),
1460
+ usage: z.object({
1461
+ prompt_tokens: z.number().nullish(),
1462
+ completion_tokens: z.number().nullish(),
1463
+ total_tokens: z.number().nullish(),
1464
+ prompt_tokens_details: z.object({
1465
+ cached_tokens: z.number().nullish(),
1466
+ cache_write_tokens: z.number().nullish()
1467
+ }).nullish(),
1468
+ completion_tokens_details: z.object({
1469
+ reasoning_tokens: z.number().nullish(),
1470
+ accepted_prediction_tokens: z.number().nullish(),
1471
+ rejected_prediction_tokens: z.number().nullish()
1472
+ }).nullish()
1473
+ }).nullish()
1474
+ })));
1475
+ const openaiChatChunkSchema = lazySchema(() => zodSchema(z.union([z.object({
1476
+ id: z.string().nullish(),
1477
+ created: z.number().nullish(),
1478
+ model: z.string().nullish(),
1479
+ choices: z.array(z.object({
1480
+ delta: z.object({
1481
+ role: z.enum(["assistant"]).nullish(),
1482
+ content: z.string().nullish(),
1483
+ refusal: z.string().nullish(),
1484
+ tool_calls: z.array(z.object({
1485
+ index: z.number(),
1486
+ id: z.string().nullish(),
1487
+ type: z.literal("function").nullish(),
1488
+ function: z.object({
1489
+ name: z.string().nullish(),
1490
+ arguments: z.string().nullish()
1491
+ })
1492
+ })).nullish(),
1493
+ annotations: z.array(z.object({
1494
+ type: z.literal("url_citation"),
1495
+ url_citation: z.object({
1496
+ start_index: z.number(),
1497
+ end_index: z.number(),
1498
+ url: z.string(),
1499
+ title: z.string()
1500
+ })
1501
+ })).nullish()
1502
+ }).nullish(),
1503
+ logprobs: z.object({
1504
+ content: z.array(z.object({
1505
+ token: z.string(),
1506
+ logprob: z.number(),
1507
+ top_logprobs: z.array(z.object({
1508
+ token: z.string(),
1509
+ logprob: z.number()
1510
+ }))
1511
+ })).nullish(),
1512
+ refusal: z.array(z.object({
1513
+ token: z.string(),
1514
+ logprob: z.number(),
1515
+ top_logprobs: z.array(z.object({
1516
+ token: z.string(),
1517
+ logprob: z.number()
1518
+ }))
1519
+ })).nullish()
1520
+ }).nullish(),
1521
+ finish_reason: z.string().nullish(),
1522
+ index: z.number()
1523
+ })),
1524
+ usage: z.object({
1525
+ prompt_tokens: z.number().nullish(),
1526
+ completion_tokens: z.number().nullish(),
1527
+ total_tokens: z.number().nullish(),
1528
+ prompt_tokens_details: z.object({
1529
+ cached_tokens: z.number().nullish(),
1530
+ cache_write_tokens: z.number().nullish()
1531
+ }).nullish(),
1532
+ completion_tokens_details: z.object({
1533
+ reasoning_tokens: z.number().nullish(),
1534
+ accepted_prediction_tokens: z.number().nullish(),
1535
+ rejected_prediction_tokens: z.number().nullish()
1536
+ }).nullish()
1537
+ }).nullish()
1538
+ }), openaiErrorDataSchema()])));
1496
1539
  //#endregion
1497
1540
  //#region src/normalize-openai-json-schema.ts
1498
1541
  /**
@@ -1732,7 +1775,7 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
1732
1775
  feature: "reasoningSummary",
1733
1776
  details: "reasoningSummary is only supported by the Responses API, not the Chat Completions API"
1734
1777
  });
1735
- const { messages, warnings: messageWarnings } = convertToOpenAIChatMessages({
1778
+ const { messages, warnings: messageWarnings } = await convertToOpenAIChatMessages({
1736
1779
  prompt,
1737
1780
  systemMessageMode: openaiOptions.systemMessageMode ?? (isReasoningModel ? "developer" : modelRequestConfig.systemMessageMode)
1738
1781
  });
@@ -1885,11 +1928,12 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
1885
1928
  }
1886
1929
  async doGenerate(options) {
1887
1930
  const { args: body, warnings } = await this.getArgs(options);
1931
+ const url = this.config.url({
1932
+ path: "/chat/completions",
1933
+ modelId: this.config.requestModelId ?? this.modelId
1934
+ });
1888
1935
  const { responseHeaders, value: response, rawValue: rawResponse } = await postJsonToApi({
1889
- url: this.config.url({
1890
- path: "/chat/completions",
1891
- modelId: this.config.requestModelId ?? this.modelId
1892
- }),
1936
+ url,
1893
1937
  headers: combineHeaders(await resolve(this.config.headers), options.headers),
1894
1938
  body,
1895
1939
  failedResponseHandler: openaiFailedResponseHandler,
@@ -1908,6 +1952,12 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
1908
1952
  type: "text",
1909
1953
  text
1910
1954
  });
1955
+ const refusal = choice.message.refusal;
1956
+ const hasRefusal = refusal != null && refusal.length > 0;
1957
+ if (hasRefusal) content.push({
1958
+ type: "text",
1959
+ text: refusal
1960
+ });
1911
1961
  for (const toolCall of choice.message.tool_calls ?? []) content.push({
1912
1962
  type: "tool-call",
1913
1963
  toolCallId: toolCall.id || generateId(),
@@ -1925,15 +1975,23 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
1925
1975
  const providerMetadata = { openai: {} };
1926
1976
  if (completionTokenDetails?.accepted_prediction_tokens != null) providerMetadata.openai.acceptedPredictionTokens = completionTokenDetails?.accepted_prediction_tokens;
1927
1977
  if (completionTokenDetails?.rejected_prediction_tokens != null) providerMetadata.openai.rejectedPredictionTokens = completionTokenDetails?.rejected_prediction_tokens;
1928
- if (choice.logprobs?.content != null) providerMetadata.openai.logprobs = choice.logprobs.content;
1978
+ const contentLogprobs = choice.logprobs?.content;
1979
+ const refusalLogprobs = choice.logprobs?.refusal;
1980
+ if (contentLogprobs != null || refusalLogprobs != null) providerMetadata.openai.logprobs = [...contentLogprobs ?? [], ...refusalLogprobs ?? []];
1929
1981
  return {
1930
1982
  content,
1931
1983
  finishReason: {
1932
- unified: mapOpenAIFinishReason$1(choice.finish_reason),
1984
+ unified: mapOpenAIFinishReason$1({
1985
+ finishReason: choice.finish_reason,
1986
+ hasRefusal
1987
+ }),
1933
1988
  raw: choice.finish_reason ?? void 0
1934
1989
  },
1935
1990
  usage: convertOpenAIChatUsage(response.usage),
1936
- request: { body },
1991
+ request: {
1992
+ body,
1993
+ url
1994
+ },
1937
1995
  response: {
1938
1996
  ...getResponseMetadata$1(response),
1939
1997
  headers: responseHeaders,
@@ -1979,6 +2037,10 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
1979
2037
  let usage = void 0;
1980
2038
  let metadataExtracted = false;
1981
2039
  let isActiveText = false;
2040
+ let hasRefusal = false;
2041
+ let rawFinishReason = void 0;
2042
+ let hasLogprobs = false;
2043
+ const streamedLogprobs = [];
1982
2044
  const providerMetadata = { openai: {} };
1983
2045
  return {
1984
2046
  stream: checkedResponse.pipeThrough(new TransformStream({
@@ -2002,6 +2064,7 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
2002
2064
  unified: "error",
2003
2065
  raw: void 0
2004
2066
  };
2067
+ rawFinishReason = void 0;
2005
2068
  controller.enqueue({
2006
2069
  type: "error",
2007
2070
  error: chunk.error
@@ -2014,6 +2077,7 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
2014
2077
  unified: "error",
2015
2078
  raw: void 0
2016
2079
  };
2080
+ rawFinishReason = void 0;
2017
2081
  controller.enqueue({
2018
2082
  type: "error",
2019
2083
  error: createOpenAIProviderStreamError(value.error) ?? value.error
@@ -2036,14 +2100,25 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
2036
2100
  if (value.usage.completion_tokens_details?.rejected_prediction_tokens != null) providerMetadata.openai.rejectedPredictionTokens = value.usage.completion_tokens_details?.rejected_prediction_tokens;
2037
2101
  }
2038
2102
  const choice = value.choices[0];
2039
- if (choice?.finish_reason != null) finishReason = {
2040
- unified: mapOpenAIFinishReason$1(choice.finish_reason),
2041
- raw: choice.finish_reason
2042
- };
2043
- if (choice?.logprobs?.content != null) providerMetadata.openai.logprobs = choice.logprobs.content;
2103
+ if (choice?.finish_reason != null) rawFinishReason = choice.finish_reason;
2104
+ if (choice?.logprobs != null) {
2105
+ const contentLogprobs = choice.logprobs.content;
2106
+ const refusalLogprobs = choice.logprobs.refusal;
2107
+ if (contentLogprobs != null || refusalLogprobs != null) {
2108
+ hasLogprobs = true;
2109
+ streamedLogprobs.push(...contentLogprobs ?? [], ...refusalLogprobs ?? []);
2110
+ }
2111
+ }
2044
2112
  if (choice?.delta == null) return;
2045
2113
  const delta = choice.delta;
2046
- if (delta.content != null) {
2114
+ const textDeltas = [];
2115
+ if (delta.content != null && delta.content.length > 0) textDeltas.push(delta.content);
2116
+ if (delta.refusal != null && delta.refusal.length > 0) {
2117
+ hasRefusal = true;
2118
+ textDeltas.push(delta.refusal);
2119
+ }
2120
+ if (textDeltas.length === 0 && delta.content === "") textDeltas.push(delta.content);
2121
+ for (const textDelta of textDeltas) {
2047
2122
  if (!isActiveText) {
2048
2123
  controller.enqueue({
2049
2124
  type: "text-start",
@@ -2054,7 +2129,7 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
2054
2129
  controller.enqueue({
2055
2130
  type: "text-delta",
2056
2131
  id: "0",
2057
- delta: delta.content
2132
+ delta: textDelta
2058
2133
  });
2059
2134
  }
2060
2135
  if (delta.tool_calls != null) for (const toolCallDelta of delta.tool_calls) toolCallTracker.processDelta(toolCallDelta);
@@ -2072,6 +2147,14 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
2072
2147
  id: "0"
2073
2148
  });
2074
2149
  toolCallTracker.flush();
2150
+ if (rawFinishReason != null) finishReason = {
2151
+ unified: mapOpenAIFinishReason$1({
2152
+ finishReason: rawFinishReason,
2153
+ hasRefusal
2154
+ }),
2155
+ raw: rawFinishReason
2156
+ };
2157
+ if (hasLogprobs) providerMetadata.openai.logprobs = streamedLogprobs;
2075
2158
  controller.enqueue({
2076
2159
  type: "finish",
2077
2160
  finishReason,
@@ -2080,7 +2163,10 @@ var OpenAIChatLanguageModel = class OpenAIChatLanguageModel {
2080
2163
  });
2081
2164
  }
2082
2165
  })),
2083
- request: { body },
2166
+ request: {
2167
+ body,
2168
+ url
2169
+ },
2084
2170
  response: { headers: responseHeaders }
2085
2171
  };
2086
2172
  }
@@ -2089,7 +2175,7 @@ function isOpenAIChatOutputChunk(chunk) {
2089
2175
  if ("error" in chunk) return false;
2090
2176
  return chunk.choices.some((choice) => {
2091
2177
  const delta = choice.delta;
2092
- return delta?.content != null && delta.content.length > 0 || delta?.tool_calls != null && delta.tool_calls.length > 0 || delta?.annotations != null && delta.annotations.length > 0;
2178
+ return delta?.content != null && delta.content.length > 0 || delta?.refusal != null && delta.refusal.length > 0 || delta?.tool_calls != null && delta.tool_calls.length > 0 || delta?.annotations != null && delta.annotations.length > 0;
2093
2179
  });
2094
2180
  }
2095
2181
  //#endregion
@@ -2819,6 +2905,15 @@ const baseImageModelOptionsObject = () => z.object({
2819
2905
  "webp"
2820
2906
  ]).optional(),
2821
2907
  /**
2908
+ * How much detail the model puts into the image. Supported by GPT Image
2909
+ * 2.6 models. The API defaults to `high`.
2910
+ */
2911
+ complexity: z.enum([
2912
+ "low",
2913
+ "medium",
2914
+ "high"
2915
+ ]).optional(),
2916
+ /**
2822
2917
  * Compression level (0-100) for the generated image(s). Applies to the
2823
2918
  * `jpeg` and `webp` output formats.
2824
2919
  */
@@ -2827,7 +2922,13 @@ const baseImageModelOptionsObject = () => z.object({
2827
2922
  * A unique identifier representing your end-user, which can help OpenAI
2828
2923
  * to monitor and detect abuse.
2829
2924
  */
2830
- user: z.string().optional()
2925
+ user: z.string().optional(),
2926
+ /**
2927
+ * A stable identifier used to help detect users of your application that
2928
+ * may be violating OpenAI's usage policies. Should be a hashed username
2929
+ * or email address.
2930
+ */
2931
+ safetyIdentifier: z.string().max(128).optional()
2831
2932
  });
2832
2933
  lazySchema(() => zodSchema(baseImageModelOptionsObject()));
2833
2934
  const openaiImageModelGenerationOptions = lazySchema(() => zodSchema(baseImageModelOptionsObject().extend({
@@ -2838,16 +2939,50 @@ const openaiImageModelGenerationOptions = lazySchema(() => zodSchema(baseImageMo
2838
2939
  */
2839
2940
  style: z.enum(["vivid", "natural"]).optional(),
2840
2941
  /**
2942
+ * Format in which the generated image(s) are returned. `gif` produces
2943
+ * an animated GIF and is supported by GPT Image 2.6 models.
2944
+ */
2945
+ outputFormat: z.enum([
2946
+ "png",
2947
+ "jpeg",
2948
+ "webp",
2949
+ "gif"
2950
+ ]).optional(),
2951
+ /**
2952
+ * Number of frames (2-64) of an animated GIF. Only applies to the `gif`
2953
+ * output format. The API defaults to 9.
2954
+ */
2955
+ frameCount: z.number().int().min(2).max(64).optional(),
2956
+ /**
2957
+ * Duration of each frame in milliseconds (20-2000) of an animated GIF.
2958
+ * The API rounds it to the nearest 10 ms. Only applies to the `gif`
2959
+ * output format. The API defaults to 200.
2960
+ */
2961
+ frameDurationMs: z.number().int().min(20).max(2e3).optional(),
2962
+ /**
2841
2963
  * Content moderation level for the generated image(s). `low` applies
2842
2964
  * less restrictive filtering.
2843
2965
  */
2844
2966
  moderation: z.enum(["auto", "low"]).optional()
2845
2967
  })));
2846
- const openaiImageModelEditOptions = lazySchema(() => zodSchema(baseImageModelOptionsObject().extend({
2847
- /**
2848
- * Fidelity of the output image(s) to the input image(s).
2849
- */
2850
- inputFidelity: z.enum(["high", "low"]).optional() })));
2968
+ const openaiImageModelEditOptions = lazySchema(() => zodSchema(baseImageModelOptionsObject().extend({
2969
+ /**
2970
+ * Fidelity of the output image(s) to the input image(s).
2971
+ */
2972
+ inputFidelity: z.enum(["high", "low"]).optional(),
2973
+ /**
2974
+ * A transparent PNG overlaid on the first input image to mark up the
2975
+ * regions to change, as an HTTPS URL or a base64 data URL. It must match
2976
+ * the dimensions of the first image. Supported by GPT Image 2.5 and 2.6
2977
+ * models. Cannot be combined with a `mask`.
2978
+ */
2979
+ markup: z.string().optional(),
2980
+ /**
2981
+ * Content moderation level for the edited image(s). `low` applies
2982
+ * less restrictive filtering. Supported by GPT Image models.
2983
+ */
2984
+ moderation: z.enum(["auto", "low"]).optional()
2985
+ })));
2851
2986
  //#endregion
2852
2987
  //#region src/image/openai-image-model.ts
2853
2988
  var OpenAIImageModel = class OpenAIImageModel {
@@ -2899,6 +3034,10 @@ var OpenAIImageModel = class OpenAIImageModel {
2899
3034
  providerOptions,
2900
3035
  schema: openaiImageModelEditOptions
2901
3036
  }) ?? {};
3037
+ if (mask != null && openaiOptions.markup != null) throw new InvalidArgumentError({
3038
+ argument: "providerOptions.openai.markup",
3039
+ message: "`markup` cannot be combined with `mask`."
3040
+ });
2902
3041
  const { value: response, responseHeaders } = await postFormDataToApi({
2903
3042
  url: this.config.url({
2904
3043
  path: "/images/edits",
@@ -2910,14 +3049,18 @@ var OpenAIImageModel = class OpenAIImageModel {
2910
3049
  prompt,
2911
3050
  image: await Promise.all(files.map((file) => file.type === "file" ? new Blob([file.data instanceof Uint8Array ? new Blob([file.data], { type: file.mediaType }) : new Blob([convertBase64ToUint8Array(file.data)], { type: file.mediaType })], { type: file.mediaType }) : downloadBlob(file.url, { abortSignal }))),
2912
3051
  mask: mask != null ? await fileToBlob(mask, abortSignal) : void 0,
3052
+ markup: openaiOptions.markup != null ? await markupToBlob(openaiOptions.markup, abortSignal) : void 0,
2913
3053
  n,
2914
3054
  size,
2915
3055
  quality: openaiOptions.quality,
2916
3056
  background: openaiOptions.background,
2917
3057
  output_format: openaiOptions.outputFormat,
2918
3058
  output_compression: openaiOptions.outputCompression,
3059
+ complexity: openaiOptions.complexity,
2919
3060
  input_fidelity: openaiOptions.inputFidelity,
2920
- user: openaiOptions.user
3061
+ moderation: openaiOptions.moderation,
3062
+ user: openaiOptions.user,
3063
+ safety_identifier: openaiOptions.safetyIdentifier
2921
3064
  }),
2922
3065
  failedResponseHandler: openaiFailedResponseHandler,
2923
3066
  successfulResponseHandler: createJsonResponseHandler(openaiImageResponseSchema),
@@ -2970,7 +3113,11 @@ var OpenAIImageModel = class OpenAIImageModel {
2970
3113
  moderation: openaiOptions.moderation,
2971
3114
  output_format: openaiOptions.outputFormat,
2972
3115
  output_compression: openaiOptions.outputCompression,
3116
+ complexity: openaiOptions.complexity,
3117
+ frame_count: openaiOptions.frameCount,
3118
+ frame_duration_ms: openaiOptions.frameDurationMs,
2973
3119
  user: openaiOptions.user,
3120
+ safety_identifier: openaiOptions.safetyIdentifier,
2974
3121
  ...!hasDefaultResponseFormat(this.modelId) ? { response_format: "b64_json" } : {}
2975
3122
  },
2976
3123
  failedResponseHandler: openaiFailedResponseHandler,
@@ -3023,6 +3170,15 @@ function distributeTokenDetails(details, index, total) {
3023
3170
  }
3024
3171
  return result;
3025
3172
  }
3173
+ async function markupToBlob(markup, abortSignal) {
3174
+ const dataUrl = /^data:([^;,]*)(;base64)?,(.*)$/s.exec(markup);
3175
+ if (dataUrl == null) return downloadBlob(markup, { abortSignal });
3176
+ if (dataUrl[2] == null) throw new InvalidArgumentError({
3177
+ argument: "providerOptions.openai.markup",
3178
+ message: "`markup` data URLs must be base64 encoded."
3179
+ });
3180
+ return new Blob([convertBase64ToUint8Array(dataUrl[3])], { type: dataUrl[1] || "image/png" });
3181
+ }
3026
3182
  async function fileToBlob(file, abortSignal) {
3027
3183
  if (!file) return void 0;
3028
3184
  if (file.type === "url") return downloadBlob(file.url, { abortSignal });
@@ -3088,7 +3244,24 @@ const codeInterpreterOutputSchema = lazySchema(() => zodSchema(z.object({ output
3088
3244
  type: z.literal("image"),
3089
3245
  url: z.string()
3090
3246
  })])).nullish() })));
3091
- const codeInterpreterArgsSchema = lazySchema(() => zodSchema(z.object({ container: z.union([z.string(), z.object({ fileIds: z.array(z.string()).optional() })]).optional() })));
3247
+ const codeInterpreterArgsSchema = lazySchema(() => zodSchema(z.object({ container: z.union([z.string(), z.object({
3248
+ fileIds: z.array(z.string()).optional(),
3249
+ memoryLimit: z.enum([
3250
+ "1g",
3251
+ "4g",
3252
+ "16g",
3253
+ "64g"
3254
+ ]).optional(),
3255
+ networkPolicy: z.discriminatedUnion("type", [z.object({ type: z.literal("disabled") }), z.object({
3256
+ type: z.literal("allowlist"),
3257
+ allowedDomains: z.array(z.string()),
3258
+ domainSecrets: z.array(z.object({
3259
+ domain: z.string(),
3260
+ name: z.string(),
3261
+ value: z.string()
3262
+ })).optional()
3263
+ })]).optional()
3264
+ })]).optional() })));
3092
3265
  const codeInterpreterToolFactory = createProviderExecutedToolFactory({
3093
3266
  id: "openai.code_interpreter",
3094
3267
  inputSchema: codeInterpreterInputSchema,
@@ -3532,8 +3705,9 @@ const mcpArgsSchema = lazySchema(() => zodSchema(z.object({
3532
3705
  headers: z.record(z.string(), z.string()).optional(),
3533
3706
  requireApproval: z.union([z.enum(["always", "never"]), z.object({ never: z.object({ toolNames: z.array(z.string()).optional() }).optional() })]).optional(),
3534
3707
  serverDescription: z.string().optional(),
3535
- serverUrl: z.string().optional()
3536
- }).refine((v) => v.serverUrl != null || v.connectorId != null, "One of serverUrl or connectorId must be provided.")));
3708
+ serverUrl: z.string().optional(),
3709
+ tunnelId: z.string().optional()
3710
+ }).refine((v) => v.serverUrl != null || v.connectorId != null || v.tunnelId != null, "One of serverUrl, connectorId, or tunnelId must be provided.").refine((v) => v.tunnelId == null || v.serverUrl == null && v.connectorId == null, "tunnelId cannot be combined with serverUrl or connectorId.")));
3537
3711
  const mcpInputSchema = lazySchema(() => zodSchema(z.object({})));
3538
3712
  const mcpOutputSchema = lazySchema(() => zodSchema(z.object({
3539
3713
  type: z.literal("call"),
@@ -3684,16 +3858,17 @@ const openaiTools = {
3684
3858
  webSearch,
3685
3859
  /**
3686
3860
  * MCP (Model Context Protocol) allows models to call tools exposed by
3687
- * remote MCP servers or service connectors.
3861
+ * remote MCP servers, service connectors, or Secure MCP tunnels.
3688
3862
  *
3689
3863
  * @param serverLabel - Label to identify the MCP server.
3690
3864
  * @param allowedTools - Allowed tool names or filter object.
3691
- * @param authorization - OAuth access token for the MCP server/connector.
3865
+ * @param authorization - OAuth access token for the MCP server or connector (including tunneled servers).
3692
3866
  * @param connectorId - Identifier for a service connector.
3693
3867
  * @param headers - Optional headers to include in MCP requests.
3694
- * // param requireApproval - Approval policy ('always'|'never'|filter object). (Removed - always 'never')
3868
+ * @param requireApproval - Approval policy ('always'|'never'|filter object).
3695
3869
  * @param serverDescription - Optional description of the server.
3696
3870
  * @param serverUrl - URL for the MCP server.
3871
+ * @param tunnelId - Identifier for an OpenAI Secure MCP tunnel.
3697
3872
  */
3698
3873
  mcp,
3699
3874
  /**
@@ -3738,10 +3913,12 @@ function convertOpenAIResponsesUsage(usage) {
3738
3913
  }
3739
3914
  //#endregion
3740
3915
  //#region src/responses/map-openai-responses-finish-reason.ts
3741
- function mapOpenAIResponseFinishReason({ finishReason, hasFunctionCall }) {
3916
+ function mapOpenAIResponseFinishReason({ finishReason, hasFunctionCall, hasRefusal = false }) {
3742
3917
  switch (finishReason) {
3743
3918
  case void 0:
3744
- case null: return hasFunctionCall ? "tool-calls" : "stop";
3919
+ case null:
3920
+ if (hasFunctionCall) return "tool-calls";
3921
+ return hasRefusal ? "content-filter" : "stop";
3745
3922
  case "max_output_tokens": return "length";
3746
3923
  case "content_filter": return "content-filter";
3747
3924
  default: return hasFunctionCall ? "tool-calls" : "other";
@@ -3921,6 +4098,8 @@ const openaiResponsesModeledChunkTypes = /* @__PURE__ */ new Set([
3921
4098
  "response.output_item.done",
3922
4099
  "response.output_text.annotation.added",
3923
4100
  "response.output_text.delta",
4101
+ "response.refusal.delta",
4102
+ "response.refusal.done",
3924
4103
  "response.reasoning_summary_part.added",
3925
4104
  "response.reasoning_summary_part.done",
3926
4105
  "response.reasoning_summary_text.delta"
@@ -3974,6 +4153,20 @@ const openaiResponsesChunkSchema = lazySchema(() => zodSchema(z.union([
3974
4153
  }))
3975
4154
  })).nullish()
3976
4155
  }),
4156
+ z.object({
4157
+ type: z.literal("response.refusal.delta"),
4158
+ item_id: z.string(),
4159
+ output_index: z.number().nullish(),
4160
+ content_index: z.number().nullish(),
4161
+ delta: z.string()
4162
+ }),
4163
+ z.object({
4164
+ type: z.literal("response.refusal.done"),
4165
+ item_id: z.string(),
4166
+ output_index: z.number().nullish(),
4167
+ content_index: z.number().nullish(),
4168
+ refusal: z.string()
4169
+ }),
3977
4170
  z.object({
3978
4171
  type: z.enum(["response.completed", "response.incomplete"]),
3979
4172
  response: z.object({
@@ -4531,7 +4724,7 @@ const openaiResponsesResponseSchema = lazySchema(() => zodSchema(z.object({
4531
4724
  role: z.literal("assistant"),
4532
4725
  id: z.string(),
4533
4726
  phase: z.enum(["commentary", "final_answer"]).nullish(),
4534
- content: z.array(z.object({
4727
+ content: z.array(z.discriminatedUnion("type", [z.object({
4535
4728
  type: z.literal("output_text"),
4536
4729
  text: z.string(),
4537
4730
  logprobs: z.array(z.object({
@@ -4570,7 +4763,10 @@ const openaiResponsesResponseSchema = lazySchema(() => zodSchema(z.object({
4570
4763
  index: z.number()
4571
4764
  })
4572
4765
  ]))
4573
- }))
4766
+ }), z.object({
4767
+ type: z.literal("refusal"),
4768
+ refusal: z.string()
4769
+ })]))
4574
4770
  }),
4575
4771
  z.object({
4576
4772
  type: z.literal("web_search_call"),
@@ -6353,7 +6549,13 @@ async function prepareResponsesTools({ tools, toolChoice, allowedTools, toolName
6353
6549
  file_ids: void 0
6354
6550
  } : typeof args.container === "string" ? args.container : {
6355
6551
  type: "auto",
6356
- file_ids: args.container.fileIds
6552
+ file_ids: args.container.fileIds,
6553
+ memory_limit: args.container.memoryLimit,
6554
+ network_policy: args.container.networkPolicy == null ? void 0 : args.container.networkPolicy.type === "disabled" ? { type: "disabled" } : {
6555
+ type: "allowlist",
6556
+ allowed_domains: args.container.networkPolicy.allowedDomains,
6557
+ domain_secrets: args.container.networkPolicy.domainSecrets
6558
+ }
6357
6559
  }
6358
6560
  });
6359
6561
  break;
@@ -6402,7 +6604,8 @@ async function prepareResponsesTools({ tools, toolChoice, allowedTools, toolName
6402
6604
  headers: args.headers,
6403
6605
  require_approval: requireApprovalParam ?? "never",
6404
6606
  server_description: args.serverDescription,
6405
- server_url: args.serverUrl
6607
+ server_url: args.serverUrl,
6608
+ tunnel_id: args.tunnelId
6406
6609
  });
6407
6610
  break;
6408
6611
  }
@@ -7096,6 +7299,7 @@ var OpenAIResponsesLanguageModel = class OpenAIResponsesLanguageModel {
7096
7299
  const retrievedFileIds = /* @__PURE__ */ new Set();
7097
7300
  let hasFunctionCall = false;
7098
7301
  const hostedToolSearchCallIds = [];
7302
+ let hasRefusal = false;
7099
7303
  let structuredOutputText;
7100
7304
  for (const part of response.output) switch (part.type) {
7101
7305
  case "reasoning":
@@ -7197,8 +7401,20 @@ var OpenAIResponsesLanguageModel = class OpenAIResponsesLanguageModel {
7197
7401
  });
7198
7402
  break;
7199
7403
  case "message":
7200
- if (options.responseFormat?.type === "json" && part.phase === "final_answer") structuredOutputText = part.content.map((contentPart) => contentPart.text).join("");
7404
+ if (options.responseFormat?.type === "json" && part.phase === "final_answer") structuredOutputText = part.content.map((contentPart) => contentPart.type === "output_text" ? contentPart.text : contentPart.refusal).join("");
7201
7405
  for (const contentPart of part.content) {
7406
+ if (contentPart.type === "refusal") {
7407
+ hasRefusal = true;
7408
+ content.push({
7409
+ type: "text",
7410
+ text: contentPart.refusal,
7411
+ providerMetadata: { [providerOptionsName]: {
7412
+ itemId: part.id,
7413
+ ...part.phase != null && { phase: part.phase }
7414
+ } }
7415
+ });
7416
+ continue;
7417
+ }
7202
7418
  if (options.providerOptions?.[providerOptionsName]?.logprobs && contentPart.logprobs) logprobs.push(contentPart.logprobs);
7203
7419
  const providerMetadata = {
7204
7420
  itemId: part.id,
@@ -7485,7 +7701,8 @@ var OpenAIResponsesLanguageModel = class OpenAIResponsesLanguageModel {
7485
7701
  finishReason: {
7486
7702
  unified: mapOpenAIResponseFinishReason({
7487
7703
  finishReason: response.incomplete_details?.reason,
7488
- hasFunctionCall
7704
+ hasFunctionCall,
7705
+ hasRefusal
7489
7706
  }),
7490
7707
  raw: response.incomplete_details?.reason ?? void 0
7491
7708
  },
@@ -7548,6 +7765,7 @@ var OpenAIResponsesLanguageModel = class OpenAIResponsesLanguageModel {
7548
7765
  const fallbackAnnotations = [];
7549
7766
  let activeMessagePhase;
7550
7767
  let hasFunctionCall = false;
7768
+ let hasRefusal = false;
7551
7769
  const activeReasoning = {};
7552
7770
  const activeOutputItemIds = {};
7553
7771
  const resolveOutputItemId = ({ itemId, outputIndex }) => outputIndex == null ? itemId : activeOutputItemIds[outputIndex] ?? itemId;
@@ -8314,7 +8532,19 @@ var OpenAIResponsesLanguageModel = class OpenAIResponsesLanguageModel {
8314
8532
  delta: value.delta
8315
8533
  });
8316
8534
  if (options.providerOptions?.[providerOptionsName]?.logprobs && value.logprobs) logprobs.push(value.logprobs);
8317
- } else if (value.type === "response.reasoning_summary_part.added") {
8535
+ } else if (isRefusalDeltaChunk(value)) {
8536
+ hasRefusal = true;
8537
+ const itemId = resolveOutputItemId({
8538
+ itemId: value.item_id,
8539
+ outputIndex: value.output_index
8540
+ });
8541
+ controller.enqueue({
8542
+ type: "text-delta",
8543
+ id: itemId,
8544
+ delta: value.delta
8545
+ });
8546
+ } else if (isRefusalDoneChunk(value)) hasRefusal = true;
8547
+ else if (value.type === "response.reasoning_summary_part.added") {
8318
8548
  const itemId = resolveOutputItemId({
8319
8549
  itemId: value.item_id,
8320
8550
  outputIndex: value.output_index
@@ -8372,7 +8602,8 @@ var OpenAIResponsesLanguageModel = class OpenAIResponsesLanguageModel {
8372
8602
  if (!encounteredStreamError) finishReason = {
8373
8603
  unified: mapOpenAIResponseFinishReason({
8374
8604
  finishReason: value.response.incomplete_details?.reason,
8375
- hasFunctionCall
8605
+ hasFunctionCall,
8606
+ hasRefusal
8376
8607
  }),
8377
8608
  raw: value.response.incomplete_details?.reason ?? void 0
8378
8609
  };
@@ -8462,6 +8693,12 @@ var OpenAIResponsesLanguageModel = class OpenAIResponsesLanguageModel {
8462
8693
  function isTextDeltaChunk(chunk) {
8463
8694
  return chunk.type === "response.output_text.delta";
8464
8695
  }
8696
+ function isRefusalDeltaChunk(chunk) {
8697
+ return chunk.type === "response.refusal.delta";
8698
+ }
8699
+ function isRefusalDoneChunk(chunk) {
8700
+ return chunk.type === "response.refusal.done";
8701
+ }
8465
8702
  function isOpenAIChatCompletionChunk(value) {
8466
8703
  const chunk = asRecord(value);
8467
8704
  return chunk != null && Array.isArray(chunk.choices) && typeof chunk.type !== "string";
@@ -8982,6 +9219,7 @@ async function convertOpenAIBatchResult(body) {
8982
9219
  const content = [];
8983
9220
  const logprobs = [];
8984
9221
  let hasFunctionCall = false;
9222
+ let hasRefusal = false;
8985
9223
  for (const part of response.output) switch (part.type) {
8986
9224
  case "reasoning": {
8987
9225
  const summaries = part.summary.length > 0 ? part.summary : [{
@@ -9000,6 +9238,14 @@ async function convertOpenAIBatchResult(body) {
9000
9238
  }
9001
9239
  case "message":
9002
9240
  for (const contentPart of part.content) {
9241
+ if (contentPart.type === "refusal") {
9242
+ hasRefusal = true;
9243
+ content.push({
9244
+ type: "text",
9245
+ text: contentPart.refusal
9246
+ });
9247
+ continue;
9248
+ }
9003
9249
  content.push({
9004
9250
  type: "text",
9005
9251
  text: contentPart.text
@@ -9118,7 +9364,8 @@ async function convertOpenAIBatchResult(body) {
9118
9364
  finishReason: {
9119
9365
  unified: mapOpenAIResponseFinishReason({
9120
9366
  finishReason: response.incomplete_details?.reason,
9121
- hasFunctionCall
9367
+ hasFunctionCall,
9368
+ hasRefusal
9122
9369
  }),
9123
9370
  raw: response.incomplete_details?.reason ?? void 0
9124
9371
  },
@@ -9471,7 +9718,8 @@ var OpenAIRealtimeModelLive = class {
9471
9718
  getWebRTCConfig() {
9472
9719
  return { dataChannelLabel: "oai-events" };
9473
9720
  }
9474
- getServerWebSocketConfig() {
9721
+ getServerWebSocketConfig(options) {
9722
+ this.assertConversationIntent(options);
9475
9723
  return resolveWith(this.config.headers, (configHeaders) => {
9476
9724
  const url = new URL(`${this.config.baseURL}/live/sessions`);
9477
9725
  url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
@@ -9511,10 +9759,15 @@ var OpenAIRealtimeModelLive = class {
9511
9759
  createServerEventParser() {
9512
9760
  return createOpenAILiveServerEventParser();
9513
9761
  }
9514
- serializeClientEvent(event) {
9762
+ assertConversationIntent(options) {
9763
+ if (options?.intent != null && options.intent !== "conversation") throw new Error(`OpenAI Live session intent '${options.intent}' is not supported.`);
9764
+ }
9765
+ serializeClientEvent(event, options) {
9766
+ this.assertConversationIntent(options);
9515
9767
  return serializeOpenAILiveClientEvent(event, this.modelId);
9516
9768
  }
9517
- buildSessionConfig(config) {
9769
+ buildSessionConfig(config, options) {
9770
+ this.assertConversationIntent(options);
9518
9771
  return buildOpenAILiveSessionConfig(config, this.modelId);
9519
9772
  }
9520
9773
  };
@@ -9558,23 +9811,31 @@ function parseOpenAIRealtimeServerEvent(raw) {
9558
9811
  item: event.item,
9559
9812
  raw
9560
9813
  };
9561
- case "conversation.item.input_audio_transcription.completed": return {
9562
- type: "input-transcription-completed",
9563
- itemId: event.item_id,
9564
- transcript: event.transcript ?? "",
9565
- raw
9566
- };
9814
+ case "conversation.item.input_audio_transcription.completed": {
9815
+ const usage = mapTranscriptionUsage(event.usage);
9816
+ return {
9817
+ type: "input-transcription-completed",
9818
+ itemId: event.item_id,
9819
+ transcript: event.transcript ?? "",
9820
+ ...usage != null ? { usage } : {},
9821
+ raw
9822
+ };
9823
+ }
9567
9824
  case "response.created": return {
9568
9825
  type: "response-created",
9569
9826
  responseId: event.response?.id ?? event.response_id,
9570
9827
  raw
9571
9828
  };
9572
- case "response.done": return {
9573
- type: "response-done",
9574
- responseId: event.response?.id ?? event.response_id,
9575
- status: event.response?.status ?? "completed",
9576
- raw
9577
- };
9829
+ case "response.done": {
9830
+ const usage = mapResponseUsage(event.response?.usage);
9831
+ return {
9832
+ type: "response-done",
9833
+ responseId: event.response?.id ?? event.response_id,
9834
+ status: event.response?.status ?? "completed",
9835
+ ...usage != null ? { usage } : {},
9836
+ raw
9837
+ };
9838
+ }
9578
9839
  case "response.output_item.added": return {
9579
9840
  type: "output-item-added",
9580
9841
  responseId: event.response_id,
@@ -9672,13 +9933,60 @@ function parseOpenAIRealtimeServerEvent(raw) {
9672
9933
  }
9673
9934
  }
9674
9935
  /**
9936
+ * Maps OpenAI realtime `response.done` usage to normalized usage.
9937
+ *
9938
+ * Input buckets are reported gross (cache-inclusive) with the cached portion
9939
+ * surfaced separately, so consumers can apply their own cached-vs-uncached
9940
+ * billing split without re-reading `raw`.
9941
+ */
9942
+ function mapResponseUsage(usage) {
9943
+ if (usage == null) return void 0;
9944
+ const input = usage.input_token_details;
9945
+ const output = usage.output_token_details;
9946
+ const cached = input?.cached_tokens_details;
9947
+ return compactUsage({
9948
+ inputTokens: usage.input_tokens,
9949
+ outputTokens: usage.output_tokens,
9950
+ cachedInputTokens: input?.cached_tokens,
9951
+ inputAudioTokens: input?.audio_tokens,
9952
+ inputTextTokens: input?.text_tokens,
9953
+ outputAudioTokens: output?.audio_tokens,
9954
+ outputTextTokens: output?.text_tokens,
9955
+ cachedInputAudioTokens: cached?.audio_tokens,
9956
+ cachedInputTextTokens: cached?.text_tokens
9957
+ });
9958
+ }
9959
+ /**
9960
+ * Maps OpenAI realtime transcription-completed usage to normalized usage.
9961
+ * Duration-billed transcription reports `seconds`; token-billed reports tokens.
9962
+ */
9963
+ function mapTranscriptionUsage(usage) {
9964
+ if (usage == null) return void 0;
9965
+ if (usage.type === "duration") return usage.seconds != null ? { seconds: usage.seconds } : void 0;
9966
+ const input = usage.input_token_details;
9967
+ return compactUsage({
9968
+ inputTokens: usage.input_tokens,
9969
+ outputTokens: usage.output_tokens,
9970
+ inputAudioTokens: input?.audio_tokens,
9971
+ inputTextTokens: input?.text_tokens,
9972
+ outputTextTokens: usage.output_tokens
9973
+ });
9974
+ }
9975
+ /** Drop `undefined` fields; return `undefined` when nothing is observable. */
9976
+ function compactUsage(usage) {
9977
+ const result = {};
9978
+ for (const [key, value] of Object.entries(usage)) if (value != null) result[key] = value;
9979
+ return Object.keys(result).length > 0 ? result : void 0;
9980
+ }
9981
+ /**
9675
9982
  * Serializes a normalized client event into OpenAI's Realtime API format.
9676
9983
  */
9677
- function serializeOpenAIRealtimeClientEvent(event, modelId) {
9984
+ function serializeOpenAIRealtimeClientEvent(event, modelId, options) {
9985
+ assertSupportedOpenAIIntent(options?.intent);
9678
9986
  switch (event.type) {
9679
9987
  case "session-update": return {
9680
9988
  type: "session.update",
9681
- session: buildOpenAISessionConfig(event.config, modelId),
9989
+ session: buildOpenAISessionConfig(event.config, modelId, options),
9682
9990
  ...event.eventId != null ? { event_id: event.eventId } : {}
9683
9991
  };
9684
9992
  case "input-audio-append": return {
@@ -9744,15 +10052,27 @@ function serializeOpenAIRealtimeClientEvent(event, modelId) {
9744
10052
  /**
9745
10053
  * Builds an OpenAI-specific session configuration from a normalized config.
9746
10054
  */
9747
- function buildOpenAISessionConfig(config, modelId) {
10055
+ function buildOpenAISessionConfig(config, modelId, options) {
10056
+ const intent = options?.intent ?? "conversation";
10057
+ assertSupportedOpenAIIntent(intent);
10058
+ if (intent === "transcription") {
10059
+ for (const key of [
10060
+ "instructions",
10061
+ "voice",
10062
+ "outputModalities",
10063
+ "outputAudioFormat",
10064
+ "outputAudioTranscription",
10065
+ "tools"
10066
+ ]) if (config[key] != null) throw new Error(`OpenAI realtime transcription sessions do not support '${key}'.`);
10067
+ }
9748
10068
  const session = {
9749
- type: "realtime",
9750
- model: modelId
10069
+ type: intent === "transcription" ? "transcription" : "realtime",
10070
+ ...intent === "conversation" ? { model: modelId } : {}
9751
10071
  };
9752
- if (config.instructions != null) session.instructions = config.instructions;
9753
- if (config.outputModalities != null) session.output_modalities = config.outputModalities;
10072
+ if (intent === "conversation" && config.instructions != null) session.instructions = config.instructions;
10073
+ if (intent === "conversation" && config.outputModalities != null) session.output_modalities = config.outputModalities;
9754
10074
  const audio = {};
9755
- if (config.inputAudioFormat != null || config.inputAudioTranscription != null || config.turnDetection != null) {
10075
+ if (intent === "transcription" || config.inputAudioFormat != null || config.inputAudioTranscription != null || config.turnDetection != null) {
9756
10076
  const input = {};
9757
10077
  if (config.inputAudioFormat != null) input.format = {
9758
10078
  type: config.inputAudioFormat.type,
@@ -9768,14 +10088,19 @@ function buildOpenAISessionConfig(config, modelId) {
9768
10088
  input.turn_detection = td;
9769
10089
  }
9770
10090
  }
9771
- if (config.inputAudioTranscription != null) input.transcription = {
10091
+ if (intent === "transcription") input.transcription = {
10092
+ model: config.inputAudioTranscription?.model ?? modelId,
10093
+ ...config.inputAudioTranscription?.language != null ? { language: config.inputAudioTranscription.language } : {},
10094
+ ...config.inputAudioTranscription?.prompt != null ? { prompt: config.inputAudioTranscription.prompt } : {}
10095
+ };
10096
+ else if (config.inputAudioTranscription != null) input.transcription = {
9772
10097
  model: config.inputAudioTranscription.model ?? "gpt-realtime-whisper",
9773
10098
  ...config.inputAudioTranscription.language != null ? { language: config.inputAudioTranscription.language } : {},
9774
10099
  ...config.inputAudioTranscription.prompt != null ? { prompt: config.inputAudioTranscription.prompt } : {}
9775
10100
  };
9776
10101
  audio.input = input;
9777
10102
  }
9778
- if (config.outputAudioFormat != null || config.voice != null) {
10103
+ if (intent === "conversation" && (config.outputAudioFormat != null || config.voice != null)) {
9779
10104
  const output = {};
9780
10105
  if (config.outputAudioFormat != null) output.format = {
9781
10106
  type: config.outputAudioFormat.type,
@@ -9785,7 +10110,7 @@ function buildOpenAISessionConfig(config, modelId) {
9785
10110
  audio.output = output;
9786
10111
  }
9787
10112
  if (Object.keys(audio).length > 0) session.audio = audio;
9788
- if (config.tools != null && config.tools.length > 0) {
10113
+ if (intent === "conversation" && config.tools != null && config.tools.length > 0) {
9789
10114
  session.tools = config.tools.map((tool) => ({
9790
10115
  type: tool.type,
9791
10116
  name: tool.name,
@@ -9797,11 +10122,19 @@ function buildOpenAISessionConfig(config, modelId) {
9797
10122
  if (config.providerOptions != null) Object.assign(session, config.providerOptions);
9798
10123
  return session;
9799
10124
  }
10125
+ function assertSupportedOpenAIIntent(intent) {
10126
+ if (intent != null && intent !== "conversation" && intent !== "transcription") throw new Error(`OpenAI realtime session intent '${intent}' is not supported.`);
10127
+ }
9800
10128
  //#endregion
9801
10129
  //#region src/realtime/openai-realtime-model.ts
9802
10130
  var OpenAIRealtimeModel = class {
9803
10131
  constructor(modelId, config) {
9804
10132
  this.specificationVersion = "v4";
10133
+ this.capabilities = {
10134
+ conversation: "turn-based",
10135
+ transports: ["websocket"],
10136
+ connections: ["client-secret-websocket", "server-websocket"]
10137
+ };
9805
10138
  this.modelId = modelId;
9806
10139
  this.provider = config.provider;
9807
10140
  this.config = config;
@@ -9844,16 +10177,36 @@ var OpenAIRealtimeModel = class {
9844
10177
  protocols: ["realtime", `openai-insecure-api-key.${options.token}`]
9845
10178
  };
9846
10179
  }
10180
+ getServerWebSocketConfig(options) {
10181
+ return resolveWith(this.config.headers, (headers) => ({
10182
+ url: buildServerUrl(this.config.baseURL, this.modelId, options?.intent ?? "conversation"),
10183
+ headers: removeUndefinedEntries(headers)
10184
+ }));
10185
+ }
9847
10186
  parseServerEvent(raw) {
9848
10187
  return parseOpenAIRealtimeServerEvent(raw);
9849
10188
  }
9850
- serializeClientEvent(event) {
9851
- return serializeOpenAIRealtimeClientEvent(event, this.modelId);
10189
+ serializeClientEvent(event, options) {
10190
+ return serializeOpenAIRealtimeClientEvent(event, this.modelId, options);
9852
10191
  }
9853
- buildSessionConfig(config) {
9854
- return buildOpenAISessionConfig(config, this.modelId);
10192
+ buildSessionConfig(config, options) {
10193
+ return buildOpenAISessionConfig(config, this.modelId, options);
9855
10194
  }
9856
10195
  };
10196
+ /**
10197
+ * Builds the upstream realtime WebSocket URL for a server-initiated connection.
10198
+ * Transcription keeps the model in `session.update`; a `?model=` here would
10199
+ * open a voice session instead.
10200
+ */
10201
+ function buildServerUrl(baseURL, modelId, intent) {
10202
+ const url = new URL(baseURL);
10203
+ const base = `${url.protocol === "http:" ? "ws:" : "wss:"}//${url.host}${url.pathname.replace(/\/$/, "")}`;
10204
+ switch (intent) {
10205
+ case "transcription": return `${base}/realtime?intent=transcription`;
10206
+ case "conversation": return `${base}/realtime?model=${encodeURIComponent(modelId)}`;
10207
+ default: throw new Error(`OpenAI realtime session intent '${intent}' is not supported.`);
10208
+ }
10209
+ }
9857
10210
  //#endregion
9858
10211
  //#region src/realtime/openai-realtime-factory.ts
9859
10212
  function resolveRealtimeApi(modelId, { api } = {}) {
@@ -10180,7 +10533,7 @@ var OpenAITranscriptionModel = class OpenAITranscriptionModel {
10180
10533
  this.config = config;
10181
10534
  this.specificationVersion = "v4";
10182
10535
  }
10183
- async getArgs({ audio, mediaType, providerOptions }) {
10536
+ async getArgs({ audio, mediaType, providerOptions, timestampGranularities: requestedTimestampGranularities }) {
10184
10537
  const warnings = [];
10185
10538
  const openAIOptions = await parseProviderOptions({
10186
10539
  provider: "openai",
@@ -10194,7 +10547,17 @@ var OpenAITranscriptionModel = class OpenAITranscriptionModel {
10194
10547
  formData.append("file", new File([blob], "audio", { type: mediaType }), `audio.${fileExtension}`);
10195
10548
  const { transcription: requestConfig } = getOpenAITranscriptionModelEntry(this.modelId).requestConfig;
10196
10549
  const responseFormat = openAIOptions?.responseFormat ?? requestConfig.responseFormat;
10197
- const timestampGranularities = openAIOptions?.timestampGranularities ?? (responseFormat === "verbose_json" ? ["segment"] : void 0);
10550
+ const timestampGranularities = openAIOptions?.timestampGranularities ?? requestedTimestampGranularities ?? (responseFormat === "verbose_json" ? ["segment"] : void 0);
10551
+ for (const granularity of requestedTimestampGranularities ?? []) if (openAIOptions?.timestampGranularities != null && !openAIOptions.timestampGranularities.includes(granularity)) warnings.push({
10552
+ type: "unsupported",
10553
+ feature: "timestampGranularities",
10554
+ details: `The "${granularity}" timestamp granularity is excluded by providerOptions.openai.timestampGranularities.`
10555
+ });
10556
+ for (const granularity of timestampGranularities ?? []) if (responseFormat !== "verbose_json" && !(responseFormat === "diarized_json" && granularity === "segment")) warnings.push({
10557
+ type: "unsupported",
10558
+ feature: openAIOptions?.timestampGranularities != null ? "providerOptions.openai.timestampGranularities" : "timestampGranularities",
10559
+ details: `OpenAI transcription with the ${responseFormat ?? "default JSON"} response format does not support ${granularity} timestamps.`
10560
+ });
10198
10561
  const chunkingStrategy = openAIOptions?.chunkingStrategy ?? requestConfig.chunkingStrategy;
10199
10562
  const transcriptionModelOptions = {
10200
10563
  response_format: responseFormat,
@@ -10204,7 +10567,7 @@ var OpenAITranscriptionModel = class OpenAITranscriptionModel {
10204
10567
  keywords: openAIOptions?.keywords,
10205
10568
  prompt: openAIOptions?.prompt,
10206
10569
  temperature: openAIOptions?.temperature,
10207
- timestamp_granularities: timestampGranularities
10570
+ timestamp_granularities: responseFormat === "verbose_json" ? timestampGranularities : void 0
10208
10571
  };
10209
10572
  for (const [key, value] of Object.entries(transcriptionModelOptions)) if (value != null) {
10210
10573
  if (Array.isArray(value)) for (const item of value) formData.append(`${key}[]`, String(item));
@@ -10244,17 +10607,19 @@ var OpenAITranscriptionModel = class OpenAITranscriptionModel {
10244
10607
  endSecond: segment.end,
10245
10608
  speaker: segment.speaker
10246
10609
  }] : []);
10610
+ const words = response.words?.map((word) => ({
10611
+ text: word.word,
10612
+ startSecond: word.start,
10613
+ endSecond: word.end
10614
+ }));
10247
10615
  return {
10248
10616
  text: response.text,
10249
10617
  segments: response.segments?.map((segment) => ({
10250
10618
  text: segment.text,
10251
10619
  startSecond: segment.start,
10252
10620
  endSecond: segment.end
10253
- })) ?? response.words?.map((word) => ({
10254
- text: word.word,
10255
- startSecond: word.start,
10256
- endSecond: word.end
10257
- })) ?? [],
10621
+ })) ?? words ?? [],
10622
+ ...words != null && words.length > 0 ? { words } : {},
10258
10623
  language,
10259
10624
  durationInSeconds: response.duration ?? (response.usage?.type === "duration" && typeof response.usage.seconds === "number" ? response.usage.seconds : void 0),
10260
10625
  warnings,
@@ -10767,7 +11132,7 @@ var OpenAISkills = class {
10767
11132
  };
10768
11133
  //#endregion
10769
11134
  //#region src/version.ts
10770
- const VERSION = "4.0.108";
11135
+ const VERSION = "4.0.110";
10771
11136
  //#endregion
10772
11137
  //#region src/openai-provider.ts
10773
11138
  /**