@ai-sdk/openai 4.0.52 → 4.0.54

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,18 @@
1
1
  # @ai-sdk/openai
2
2
 
3
+ ## 4.0.54
4
+
5
+ ### Patch Changes
6
+
7
+ - f6fac50: fix(openai): handle null usage in responses
8
+ - e07b577: feat: add tool calling support to batch
9
+
10
+ ## 4.0.53
11
+
12
+ ### Patch Changes
13
+
14
+ - 7439d7a: Fix workflow deserialization for OpenAI Responses models.
15
+
3
16
  ## 4.0.52
4
17
 
5
18
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -252,7 +252,10 @@ declare const openaiResponsesChunkSchema: _ai_sdk_provider_utils.LazySchema<{
252
252
  } | {
253
253
  type: "response.completed" | "response.incomplete";
254
254
  response: {
255
- usage: {
255
+ incomplete_details?: {
256
+ reason: string;
257
+ } | null | undefined;
258
+ usage?: {
256
259
  input_tokens: number;
257
260
  output_tokens: number;
258
261
  input_tokens_details?: {
@@ -265,9 +268,6 @@ declare const openaiResponsesChunkSchema: _ai_sdk_provider_utils.LazySchema<{
265
268
  reasoning_tokens?: number | null | undefined;
266
269
  orchestration_output_tokens?: number | null | undefined;
267
270
  } | null | undefined;
268
- };
269
- incomplete_details?: {
270
- reason: string;
271
271
  } | null | undefined;
272
272
  reasoning?: {
273
273
  context?: string | null | undefined;
@@ -1585,13 +1585,15 @@ type OpenAIToolOptions = {
1585
1585
  };
1586
1586
  };
1587
1587
 
1588
+ type OpenAIHeaders = Record<string, string | undefined>;
1588
1589
  type OpenAIConfig = {
1589
1590
  provider: string;
1591
+ baseURL?: string;
1590
1592
  url: (options: {
1591
1593
  modelId: string;
1592
1594
  path: string;
1593
1595
  }) => string;
1594
- headers?: () => Record<string, string | undefined>;
1596
+ headers?: () => OpenAIHeaders;
1595
1597
  fetch?: FetchFunction;
1596
1598
  webSocket?: WebSocketConstructor;
1597
1599
  generateId?: () => string;
package/dist/index.js CHANGED
@@ -3375,6 +3375,28 @@ import {
3375
3375
  } from "@ai-sdk/provider-utils";
3376
3376
  import { z as z28 } from "zod/v4";
3377
3377
 
3378
+ // src/openai-config.ts
3379
+ function prepareOpenAIConfigForWorkflowDeserialize(config) {
3380
+ if (config.provider == null) {
3381
+ throw new Error(
3382
+ "OpenAI model is missing provider after workflow deserialization."
3383
+ );
3384
+ }
3385
+ return {
3386
+ ...config,
3387
+ provider: config.provider,
3388
+ url: typeof config.url === "function" ? config.url : ({ path }) => {
3389
+ if (config.baseURL == null) {
3390
+ throw new Error(
3391
+ "OpenAI model is missing baseURL after workflow deserialization."
3392
+ );
3393
+ }
3394
+ return `${config.baseURL}${path}`;
3395
+ },
3396
+ headers: typeof config.headers === "function" ? config.headers : config.headers == null ? void 0 : () => config.headers
3397
+ };
3398
+ }
3399
+
3378
3400
  // src/responses/convert-openai-responses-usage.ts
3379
3401
  import { createNullLanguageModelUsage as createNullLanguageModelUsage3 } from "@ai-sdk/provider-utils";
3380
3402
  function convertOpenAIResponsesUsage(usage) {
@@ -3647,7 +3669,7 @@ var openaiResponsesChunkSchema = lazySchema24(
3647
3669
  reasoning_tokens: z25.number().nullish(),
3648
3670
  orchestration_output_tokens: z25.number().nullish()
3649
3671
  }).nullish()
3650
- }),
3672
+ }).nullish(),
3651
3673
  reasoning: z25.object({
3652
3674
  context: z25.string().nullish()
3653
3675
  }).nullish(),
@@ -4479,7 +4501,7 @@ var openaiResponsesResponseSchema = lazySchema24(
4479
4501
  reasoning_tokens: z25.number().nullish(),
4480
4502
  orchestration_output_tokens: z25.number().nullish()
4481
4503
  }).nullish()
4482
- }).optional()
4504
+ }).nullish()
4483
4505
  })
4484
4506
  )
4485
4507
  );
@@ -6594,7 +6616,10 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
6594
6616
  });
6595
6617
  }
6596
6618
  static [WORKFLOW_DESERIALIZE5](options) {
6597
- return new _OpenAIResponsesLanguageModel(options.modelId, options.config);
6619
+ return new _OpenAIResponsesLanguageModel(
6620
+ options.modelId,
6621
+ prepareOpenAIConfigForWorkflowDeserialize(options.config)
6622
+ );
6598
6623
  }
6599
6624
  get provider() {
6600
6625
  return this.config.provider;
@@ -7545,7 +7570,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
7545
7570
  controller.enqueue({ type: "stream-start", warnings });
7546
7571
  },
7547
7572
  transform(chunk, controller) {
7548
- var _a3, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R;
7573
+ var _a3, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S;
7549
7574
  if (options.includeRawChunks) {
7550
7575
  controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
7551
7576
  }
@@ -8480,15 +8505,15 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
8480
8505
  raw: (_z = (_y = value.response.incomplete_details) == null ? void 0 : _y.reason) != null ? _z : void 0
8481
8506
  };
8482
8507
  }
8483
- usage = value.response.usage;
8508
+ usage = (_A = value.response.usage) != null ? _A : void 0;
8484
8509
  if (typeof value.response.service_tier === "string") {
8485
8510
  serviceTier = value.response.service_tier;
8486
8511
  }
8487
- if (((_A = value.response.reasoning) == null ? void 0 : _A.context) != null) {
8512
+ if (((_B = value.response.reasoning) == null ? void 0 : _B.context) != null) {
8488
8513
  reasoningContext = value.response.reasoning.context;
8489
8514
  }
8490
8515
  } else if (isResponseFailedChunk(value)) {
8491
- const incompleteReason = (_B = value.response.incomplete_details) == null ? void 0 : _B.reason;
8516
+ const incompleteReason = (_C = value.response.incomplete_details) == null ? void 0 : _C.reason;
8492
8517
  finishReason = {
8493
8518
  unified: incompleteReason ? mapOpenAIResponseFinishReason({
8494
8519
  finishReason: incompleteReason,
@@ -8496,8 +8521,8 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
8496
8521
  }) : "error",
8497
8522
  raw: incompleteReason != null ? incompleteReason : "error"
8498
8523
  };
8499
- usage = (_C = value.response.usage) != null ? _C : void 0;
8500
- if (((_D = value.response.reasoning) == null ? void 0 : _D.context) != null) {
8524
+ usage = (_D = value.response.usage) != null ? _D : void 0;
8525
+ if (((_E = value.response.reasoning) == null ? void 0 : _E.context) != null) {
8501
8526
  reasoningContext = value.response.reasoning.context;
8502
8527
  }
8503
8528
  if (!encounteredStreamError && value.response.error != null) {
@@ -8513,7 +8538,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
8513
8538
  };
8514
8539
  controller.enqueue({
8515
8540
  type: "error",
8516
- error: (_E = createOpenAIProviderStreamError(error)) != null ? _E : error
8541
+ error: (_F = createOpenAIProviderStreamError(error)) != null ? _F : error
8517
8542
  });
8518
8543
  }
8519
8544
  } else if (isResponseAnnotationAddedChunk(value)) {
@@ -8522,7 +8547,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
8522
8547
  controller.enqueue({
8523
8548
  type: "source",
8524
8549
  sourceType: "url",
8525
- id: (_H = (_G = (_F = self.config).generateId) == null ? void 0 : _G.call(_F)) != null ? _H : generateId2(),
8550
+ id: (_I = (_H = (_G = self.config).generateId) == null ? void 0 : _H.call(_G)) != null ? _I : generateId2(),
8526
8551
  url: value.annotation.url,
8527
8552
  title: value.annotation.title
8528
8553
  });
@@ -8530,7 +8555,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
8530
8555
  controller.enqueue({
8531
8556
  type: "source",
8532
8557
  sourceType: "document",
8533
- id: (_K = (_J = (_I = self.config).generateId) == null ? void 0 : _J.call(_I)) != null ? _K : generateId2(),
8558
+ id: (_L = (_K = (_J = self.config).generateId) == null ? void 0 : _K.call(_J)) != null ? _L : generateId2(),
8534
8559
  mediaType: "text/plain",
8535
8560
  title: value.annotation.filename,
8536
8561
  filename: value.annotation.filename,
@@ -8546,7 +8571,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
8546
8571
  controller.enqueue({
8547
8572
  type: "source",
8548
8573
  sourceType: "document",
8549
- id: (_N = (_M = (_L = self.config).generateId) == null ? void 0 : _M.call(_L)) != null ? _N : generateId2(),
8574
+ id: (_O = (_N = (_M = self.config).generateId) == null ? void 0 : _N.call(_M)) != null ? _O : generateId2(),
8550
8575
  mediaType: "text/plain",
8551
8576
  title: value.annotation.filename,
8552
8577
  filename: value.annotation.filename,
@@ -8562,7 +8587,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
8562
8587
  controller.enqueue({
8563
8588
  type: "source",
8564
8589
  sourceType: "document",
8565
- id: (_Q = (_P = (_O = self.config).generateId) == null ? void 0 : _P.call(_O)) != null ? _Q : generateId2(),
8590
+ id: (_R = (_Q = (_P = self.config).generateId) == null ? void 0 : _Q.call(_P)) != null ? _R : generateId2(),
8566
8591
  mediaType: "application/octet-stream",
8567
8592
  title: value.annotation.file_id,
8568
8593
  filename: value.annotation.file_id,
@@ -8580,7 +8605,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
8580
8605
  finishReason = { unified: "error", raw: "error" };
8581
8606
  controller.enqueue({
8582
8607
  type: "error",
8583
- error: (_R = createOpenAIProviderStreamError(value)) != null ? _R : value
8608
+ error: (_S = createOpenAIProviderStreamError(value)) != null ? _S : value
8584
8609
  });
8585
8610
  }
8586
8611
  },
@@ -8783,7 +8808,7 @@ var OpenAIResponsesBatch = class {
8783
8808
  this.options = options;
8784
8809
  }
8785
8810
  async startBatch(options) {
8786
- var _a2, _b, _c, _d;
8811
+ var _a2, _b, _c, _d, _e;
8787
8812
  const fileParts = [];
8788
8813
  const warnings = options.webhookUrl == null ? [] : [
8789
8814
  {
@@ -8808,6 +8833,18 @@ var OpenAIResponsesBatch = class {
8808
8833
  for (const warning of preparedRequest.warnings) {
8809
8834
  warnings.push({ requestId: request.id, warning });
8810
8835
  }
8836
+ for (const tool of (_a2 = request.options.tools) != null ? _a2 : []) {
8837
+ if (tool.type === "provider" && !openAIBatchConvertibleProviderToolIds.has(tool.id)) {
8838
+ warnings.push({
8839
+ requestId: request.id,
8840
+ warning: {
8841
+ type: "unsupported",
8842
+ feature: `batch result conversion for tool "${tool.name}"`,
8843
+ details: "OpenAI may return output for this tool that AI SDK text batches cannot currently convert."
8844
+ }
8845
+ });
8846
+ }
8847
+ }
8811
8848
  }
8812
8849
  const filename = "batch.jsonl";
8813
8850
  const file = new Blob(fileParts, {
@@ -8824,7 +8861,7 @@ var OpenAIResponsesBatch = class {
8824
8861
  );
8825
8862
  const { value: uploadedFile } = await postToApi({
8826
8863
  url: this.getUrl("/files"),
8827
- headers: combineHeaders7((_b = (_a2 = this.options.config).headers) == null ? void 0 : _b.call(_a2), options.headers),
8864
+ headers: combineHeaders7((_c = (_b = this.options.config).headers) == null ? void 0 : _c.call(_b), options.headers),
8828
8865
  body: {
8829
8866
  content: formData,
8830
8867
  values: {
@@ -8849,7 +8886,7 @@ var OpenAIResponsesBatch = class {
8849
8886
  });
8850
8887
  const { value: batch } = await postJsonToApi6({
8851
8888
  url: this.getUrl("/batches"),
8852
- headers: combineHeaders7((_d = (_c = this.options.config).headers) == null ? void 0 : _d.call(_c), options.headers),
8889
+ headers: combineHeaders7((_e = (_d = this.options.config).headers) == null ? void 0 : _e.call(_d), options.headers),
8853
8890
  body: {
8854
8891
  input_file_id: uploadedFile.id,
8855
8892
  endpoint: openaiBatchEndpoint,
@@ -8990,6 +9027,13 @@ var OpenAIResponsesBatch = class {
8990
9027
  });
8991
9028
  }
8992
9029
  };
9030
+ var openAIBatchConvertibleProviderToolIds = /* @__PURE__ */ new Set([
9031
+ "openai.code_interpreter",
9032
+ "openai.custom",
9033
+ "openai.file_search",
9034
+ "openai.web_search",
9035
+ "openai.web_search_preview"
9036
+ ]);
8993
9037
  var OpenAIResponsesBatchLanguageModel = class _OpenAIResponsesBatchLanguageModel extends OpenAIResponsesLanguageModel {
8994
9038
  static [WORKFLOW_SERIALIZE6](model) {
8995
9039
  return OpenAIResponsesLanguageModel[WORKFLOW_SERIALIZE6](model);
@@ -8997,7 +9041,7 @@ var OpenAIResponsesBatchLanguageModel = class _OpenAIResponsesBatchLanguageModel
8997
9041
  static [WORKFLOW_DESERIALIZE6](options) {
8998
9042
  return new _OpenAIResponsesBatchLanguageModel(
8999
9043
  options.modelId,
9000
- options.config
9044
+ prepareOpenAIConfigForWorkflowDeserialize(options.config)
9001
9045
  );
9002
9046
  }
9003
9047
  constructor(modelId, config) {
@@ -9099,7 +9143,7 @@ async function convertOpenAIErrorResponse({
9099
9143
  };
9100
9144
  }
9101
9145
  async function convertOpenAIResponsesBatchResponse(body) {
9102
- var _a2, _b, _c, _d, _e, _f;
9146
+ var _a2, _b, _c, _d, _e, _f, _g, _h;
9103
9147
  const validation = await safeValidateTypes({
9104
9148
  value: body,
9105
9149
  schema: openaiResponsesResponseSchema
@@ -9136,6 +9180,7 @@ async function convertOpenAIResponsesBatchResponse(body) {
9136
9180
  }
9137
9181
  const content = [];
9138
9182
  const logprobs = [];
9183
+ let hasFunctionCall = false;
9139
9184
  for (const part of response.output) {
9140
9185
  switch (part.type) {
9141
9186
  case "reasoning": {
@@ -9164,14 +9209,96 @@ async function convertOpenAIResponsesBatchResponse(body) {
9164
9209
  break;
9165
9210
  }
9166
9211
  case "function_call":
9167
- case "custom_tool_call":
9168
- return {
9169
- success: false,
9170
- error: {
9171
- message: "OpenAI returned a tool call, but tool calls are not supported in AI SDK text batches.",
9172
- code: "unsupported_content"
9212
+ hasFunctionCall = true;
9213
+ content.push({
9214
+ type: "tool-call",
9215
+ toolCallId: part.call_id,
9216
+ toolName: part.name,
9217
+ input: part.arguments,
9218
+ providerMetadata: {
9219
+ openai: {
9220
+ itemId: part.id,
9221
+ ...part.namespace != null && { namespace: part.namespace },
9222
+ ...part.caller != null && {
9223
+ caller: part.caller.type === "program" ? { type: "program", callerId: part.caller.caller_id } : part.caller
9224
+ }
9225
+ }
9173
9226
  }
9174
- };
9227
+ });
9228
+ break;
9229
+ case "custom_tool_call":
9230
+ hasFunctionCall = true;
9231
+ content.push({
9232
+ type: "tool-call",
9233
+ toolCallId: part.call_id,
9234
+ toolName: part.name,
9235
+ input: JSON.stringify(part.input),
9236
+ providerMetadata: { openai: { itemId: part.id } }
9237
+ });
9238
+ break;
9239
+ case "web_search_call":
9240
+ content.push({
9241
+ type: "tool-call",
9242
+ toolCallId: part.id,
9243
+ toolName: "web_search",
9244
+ input: "{}",
9245
+ providerExecuted: true,
9246
+ dynamic: true
9247
+ });
9248
+ content.push({
9249
+ type: "tool-result",
9250
+ toolCallId: part.id,
9251
+ toolName: "web_search",
9252
+ result: mapWebSearchOutput(part.action),
9253
+ dynamic: true
9254
+ });
9255
+ break;
9256
+ case "file_search_call":
9257
+ content.push({
9258
+ type: "tool-call",
9259
+ toolCallId: part.id,
9260
+ toolName: "file_search",
9261
+ input: "{}",
9262
+ providerExecuted: true,
9263
+ dynamic: true
9264
+ });
9265
+ content.push({
9266
+ type: "tool-result",
9267
+ toolCallId: part.id,
9268
+ toolName: "file_search",
9269
+ result: {
9270
+ queries: part.queries,
9271
+ results: (_d = (_c = part.results) == null ? void 0 : _c.map((result) => ({
9272
+ attributes: result.attributes,
9273
+ fileId: result.file_id,
9274
+ filename: result.filename,
9275
+ score: result.score,
9276
+ text: result.text
9277
+ }))) != null ? _d : null
9278
+ },
9279
+ dynamic: true
9280
+ });
9281
+ break;
9282
+ case "code_interpreter_call":
9283
+ content.push({
9284
+ type: "tool-call",
9285
+ toolCallId: part.id,
9286
+ toolName: "code_interpreter",
9287
+ input: JSON.stringify({
9288
+ code: part.code,
9289
+ containerId: part.container_id
9290
+ }),
9291
+ providerExecuted: true,
9292
+ dynamic: true
9293
+ });
9294
+ content.push({
9295
+ type: "tool-result",
9296
+ toolCallId: part.id,
9297
+ toolName: "code_interpreter",
9298
+ result: { outputs: part.outputs },
9299
+ dynamic: true
9300
+ });
9301
+ break;
9175
9302
  default:
9176
9303
  return {
9177
9304
  success: false,
@@ -9187,7 +9314,7 @@ async function convertOpenAIResponsesBatchResponse(body) {
9187
9314
  responseId: response.id,
9188
9315
  ...logprobs.length > 0 ? { logprobs } : {},
9189
9316
  ...typeof response.service_tier === "string" ? { serviceTier: response.service_tier } : {},
9190
- ...((_c = response.reasoning) == null ? void 0 : _c.context) != null ? { reasoningContext: response.reasoning.context } : {}
9317
+ ...((_e = response.reasoning) == null ? void 0 : _e.context) != null ? { reasoningContext: response.reasoning.context } : {}
9191
9318
  }
9192
9319
  };
9193
9320
  return {
@@ -9196,10 +9323,10 @@ async function convertOpenAIResponsesBatchResponse(body) {
9196
9323
  content,
9197
9324
  finishReason: {
9198
9325
  unified: mapOpenAIResponseFinishReason({
9199
- finishReason: (_d = response.incomplete_details) == null ? void 0 : _d.reason,
9200
- hasFunctionCall: false
9326
+ finishReason: (_f = response.incomplete_details) == null ? void 0 : _f.reason,
9327
+ hasFunctionCall
9201
9328
  }),
9202
- raw: (_f = (_e = response.incomplete_details) == null ? void 0 : _e.reason) != null ? _f : void 0
9329
+ raw: (_h = (_g = response.incomplete_details) == null ? void 0 : _g.reason) != null ? _h : void 0
9203
9330
  },
9204
9331
  usage: convertOpenAIResponsesUsage(response.usage),
9205
9332
  response: {
@@ -10673,7 +10800,7 @@ var OpenAISkills = class {
10673
10800
  };
10674
10801
 
10675
10802
  // src/version.ts
10676
- var VERSION = true ? "4.0.52" : "0.0.0-test";
10803
+ var VERSION = true ? "4.0.54" : "0.0.0-test";
10677
10804
 
10678
10805
  // src/openai-provider.ts
10679
10806
  function createOpenAI(options = {}) {
@@ -10767,6 +10894,7 @@ function createOpenAI(options = {}) {
10767
10894
  const createResponsesModel = (modelId) => {
10768
10895
  return new OpenAIResponsesBatchLanguageModel(modelId, {
10769
10896
  provider: `${providerName}.responses`,
10897
+ baseURL,
10770
10898
  url: ({ path }) => `${baseURL}${path}`,
10771
10899
  headers: getHeaders,
10772
10900
  fetch: options.fetch,