@ai-sdk/openai 3.0.137 → 3.0.138

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.
@@ -647,10 +647,13 @@ function getResponseMetadata({
647
647
  }
648
648
 
649
649
  // src/chat/map-openai-finish-reason.ts
650
- function mapOpenAIFinishReason(finishReason) {
650
+ function mapOpenAIFinishReason({
651
+ finishReason,
652
+ hasRefusal = false
653
+ }) {
651
654
  switch (finishReason) {
652
655
  case "stop":
653
- return "stop";
656
+ return hasRefusal ? "content-filter" : "stop";
654
657
  case "length":
655
658
  return "length";
656
659
  case "content_filter":
@@ -680,6 +683,8 @@ var openaiChatResponseSchema = lazySchema2(
680
683
  message: z2.object({
681
684
  role: z2.literal("assistant").nullish(),
682
685
  content: z2.string().nullish(),
686
+ // set instead of `content` when the model refuses to answer:
687
+ refusal: z2.string().nullish(),
683
688
  audio: z2.object({
684
689
  transcript: z2.string().nullish()
685
690
  }).nullish(),
@@ -718,6 +723,20 @@ var openaiChatResponseSchema = lazySchema2(
718
723
  })
719
724
  )
720
725
  })
726
+ ).nullish(),
727
+ // logprobs of the refusal tokens (set instead of `content` when
728
+ // the model refuses to answer):
729
+ refusal: z2.array(
730
+ z2.object({
731
+ token: z2.string(),
732
+ logprob: z2.number(),
733
+ top_logprobs: z2.array(
734
+ z2.object({
735
+ token: z2.string(),
736
+ logprob: z2.number()
737
+ })
738
+ )
739
+ })
721
740
  ).nullish()
722
741
  }).nullish(),
723
742
  finish_reason: z2.string().nullish()
@@ -752,6 +771,8 @@ var openaiChatChunkSchema = lazySchema2(
752
771
  delta: z2.object({
753
772
  role: z2.enum(["assistant"]).nullish(),
754
773
  content: z2.string().nullish(),
774
+ // set instead of `content` when the model refuses to answer:
775
+ refusal: z2.string().nullish(),
755
776
  tool_calls: z2.array(
756
777
  z2.object({
757
778
  index: z2.number(),
@@ -787,6 +808,20 @@ var openaiChatChunkSchema = lazySchema2(
787
808
  })
788
809
  )
789
810
  })
811
+ ).nullish(),
812
+ // logprobs of the refusal tokens (set instead of `content` when
813
+ // the model refuses to answer):
814
+ refusal: z2.array(
815
+ z2.object({
816
+ token: z2.string(),
817
+ logprob: z2.number(),
818
+ top_logprobs: z2.array(
819
+ z2.object({
820
+ token: z2.string(),
821
+ logprob: z2.number()
822
+ })
823
+ )
824
+ })
790
825
  ).nullish()
791
826
  }).nullish(),
792
827
  finish_reason: z2.string().nullish(),
@@ -1228,7 +1263,7 @@ var OpenAIChatLanguageModel = class {
1228
1263
  };
1229
1264
  }
1230
1265
  async doGenerate(options) {
1231
- var _a2, _b, _c, _d, _e, _f, _g, _h;
1266
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
1232
1267
  const { args: body, warnings } = await this.getArgs(options);
1233
1268
  const {
1234
1269
  responseHeaders,
@@ -1260,6 +1295,11 @@ var OpenAIChatLanguageModel = class {
1260
1295
  if (text != null && text.length > 0) {
1261
1296
  content.push({ type: "text", text });
1262
1297
  }
1298
+ const refusal = choice.message.refusal;
1299
+ const hasRefusal = refusal != null && refusal.length > 0;
1300
+ if (hasRefusal) {
1301
+ content.push({ type: "text", text: refusal });
1302
+ }
1263
1303
  for (const toolCall of (_b = choice.message.tool_calls) != null ? _b : []) {
1264
1304
  content.push({
1265
1305
  type: "tool-call",
@@ -1286,14 +1326,22 @@ var OpenAIChatLanguageModel = class {
1286
1326
  if ((completionTokenDetails == null ? void 0 : completionTokenDetails.rejected_prediction_tokens) != null) {
1287
1327
  providerMetadata.openai.rejectedPredictionTokens = completionTokenDetails == null ? void 0 : completionTokenDetails.rejected_prediction_tokens;
1288
1328
  }
1289
- if (((_g = choice.logprobs) == null ? void 0 : _g.content) != null) {
1290
- providerMetadata.openai.logprobs = choice.logprobs.content;
1329
+ const contentLogprobs = (_g = choice.logprobs) == null ? void 0 : _g.content;
1330
+ const refusalLogprobs = (_h = choice.logprobs) == null ? void 0 : _h.refusal;
1331
+ if (contentLogprobs != null || refusalLogprobs != null) {
1332
+ providerMetadata.openai.logprobs = [
1333
+ ...contentLogprobs != null ? contentLogprobs : [],
1334
+ ...refusalLogprobs != null ? refusalLogprobs : []
1335
+ ];
1291
1336
  }
1292
1337
  return {
1293
1338
  content,
1294
1339
  finishReason: {
1295
- unified: mapOpenAIFinishReason(choice.finish_reason),
1296
- raw: (_h = choice.finish_reason) != null ? _h : void 0
1340
+ unified: mapOpenAIFinishReason({
1341
+ finishReason: choice.finish_reason,
1342
+ hasRefusal
1343
+ }),
1344
+ raw: (_i = choice.finish_reason) != null ? _i : void 0
1297
1345
  },
1298
1346
  usage: convertOpenAIChatUsage(response.usage),
1299
1347
  request: { body },
@@ -1346,6 +1394,10 @@ var OpenAIChatLanguageModel = class {
1346
1394
  let usage = void 0;
1347
1395
  let metadataExtracted = false;
1348
1396
  let isActiveText = false;
1397
+ let hasRefusal = false;
1398
+ let rawFinishReason = void 0;
1399
+ let hasLogprobs = false;
1400
+ const streamedLogprobs = [];
1349
1401
  const providerMetadata = { openai: {} };
1350
1402
  const result = {
1351
1403
  stream: checkedResponse.pipeThrough(
@@ -1358,18 +1410,20 @@ var OpenAIChatLanguageModel = class {
1358
1410
  controller.enqueue({ type: "stream-start", warnings });
1359
1411
  },
1360
1412
  transform(chunk, controller) {
1361
- var _a2, _b, _c, _d, _e;
1413
+ var _a2, _b, _c, _d;
1362
1414
  if (options.includeRawChunks) {
1363
1415
  controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
1364
1416
  }
1365
1417
  if (!chunk.success) {
1366
1418
  finishReason = { unified: "error", raw: void 0 };
1419
+ rawFinishReason = void 0;
1367
1420
  controller.enqueue({ type: "error", error: chunk.error });
1368
1421
  return;
1369
1422
  }
1370
1423
  const value = chunk.value;
1371
1424
  if ("error" in value) {
1372
1425
  finishReason = { unified: "error", raw: void 0 };
1426
+ rawFinishReason = void 0;
1373
1427
  controller.enqueue({ type: "error", error: value.error });
1374
1428
  return;
1375
1429
  }
@@ -1394,19 +1448,35 @@ var OpenAIChatLanguageModel = class {
1394
1448
  }
1395
1449
  const choice = value.choices[0];
1396
1450
  if ((choice == null ? void 0 : choice.finish_reason) != null) {
1397
- finishReason = {
1398
- unified: mapOpenAIFinishReason(choice.finish_reason),
1399
- raw: choice.finish_reason
1400
- };
1451
+ rawFinishReason = choice.finish_reason;
1401
1452
  }
1402
- if (((_e = choice == null ? void 0 : choice.logprobs) == null ? void 0 : _e.content) != null) {
1403
- providerMetadata.openai.logprobs = choice.logprobs.content;
1453
+ if ((choice == null ? void 0 : choice.logprobs) != null) {
1454
+ const contentLogprobs = choice.logprobs.content;
1455
+ const refusalLogprobs = choice.logprobs.refusal;
1456
+ if (contentLogprobs != null || refusalLogprobs != null) {
1457
+ hasLogprobs = true;
1458
+ streamedLogprobs.push(
1459
+ ...contentLogprobs != null ? contentLogprobs : [],
1460
+ ...refusalLogprobs != null ? refusalLogprobs : []
1461
+ );
1462
+ }
1404
1463
  }
1405
1464
  if ((choice == null ? void 0 : choice.delta) == null) {
1406
1465
  return;
1407
1466
  }
1408
1467
  const delta = choice.delta;
1409
- if (delta.content != null) {
1468
+ const textDeltas = [];
1469
+ if (delta.content != null && delta.content.length > 0) {
1470
+ textDeltas.push(delta.content);
1471
+ }
1472
+ if (delta.refusal != null && delta.refusal.length > 0) {
1473
+ hasRefusal = true;
1474
+ textDeltas.push(delta.refusal);
1475
+ }
1476
+ if (textDeltas.length === 0 && delta.content === "") {
1477
+ textDeltas.push(delta.content);
1478
+ }
1479
+ for (const textDelta of textDeltas) {
1410
1480
  if (!isActiveText) {
1411
1481
  controller.enqueue({ type: "text-start", id: "0" });
1412
1482
  isActiveText = true;
@@ -1414,7 +1484,7 @@ var OpenAIChatLanguageModel = class {
1414
1484
  controller.enqueue({
1415
1485
  type: "text-delta",
1416
1486
  id: "0",
1417
- delta: delta.content
1487
+ delta: textDelta
1418
1488
  });
1419
1489
  }
1420
1490
  if (delta.tool_calls != null) {
@@ -1439,6 +1509,18 @@ var OpenAIChatLanguageModel = class {
1439
1509
  controller.enqueue({ type: "text-end", id: "0" });
1440
1510
  }
1441
1511
  toolCallTracker.flush();
1512
+ if (rawFinishReason != null) {
1513
+ finishReason = {
1514
+ unified: mapOpenAIFinishReason({
1515
+ finishReason: rawFinishReason,
1516
+ hasRefusal
1517
+ }),
1518
+ raw: rawFinishReason
1519
+ };
1520
+ }
1521
+ if (hasLogprobs) {
1522
+ providerMetadata.openai.logprobs = streamedLogprobs;
1523
+ }
1442
1524
  controller.enqueue({
1443
1525
  type: "finish",
1444
1526
  finishReason,
@@ -1460,7 +1542,7 @@ function isOpenAIChatOutputChunk(chunk) {
1460
1542
  }
1461
1543
  return chunk.choices.some((choice) => {
1462
1544
  const delta = choice.delta;
1463
- return (delta == null ? void 0 : delta.content) != null && delta.content.length > 0 || (delta == null ? void 0 : delta.tool_calls) != null && delta.tool_calls.length > 0 || (delta == null ? void 0 : delta.annotations) != null && delta.annotations.length > 0;
1545
+ return (delta == null ? void 0 : delta.content) != null && delta.content.length > 0 || (delta == null ? void 0 : delta.refusal) != null && delta.refusal.length > 0 || (delta == null ? void 0 : delta.tool_calls) != null && delta.tool_calls.length > 0 || (delta == null ? void 0 : delta.annotations) != null && delta.annotations.length > 0;
1464
1546
  });
1465
1547
  }
1466
1548
 
@@ -4679,12 +4761,14 @@ var openaiResponsesReasoningProviderOptionsSchema = lazySchema19(
4679
4761
  // src/responses/map-openai-responses-finish-reason.ts
4680
4762
  function mapOpenAIResponseFinishReason({
4681
4763
  finishReason,
4682
- hasFunctionCall
4764
+ hasFunctionCall,
4765
+ hasRefusal = false
4683
4766
  }) {
4684
4767
  switch (finishReason) {
4685
4768
  case void 0:
4686
4769
  case null:
4687
- return hasFunctionCall ? "tool-calls" : "stop";
4770
+ if (hasFunctionCall) return "tool-calls";
4771
+ return hasRefusal ? "content-filter" : "stop";
4688
4772
  case "max_output_tokens":
4689
4773
  return "length";
4690
4774
  case "content_filter":
@@ -4794,6 +4878,8 @@ var openaiResponsesModeledChunkTypes = /* @__PURE__ */ new Set([
4794
4878
  "response.output_item.done",
4795
4879
  "response.output_text.annotation.added",
4796
4880
  "response.output_text.delta",
4881
+ "response.refusal.delta",
4882
+ "response.refusal.done",
4797
4883
  "response.reasoning_summary_part.added",
4798
4884
  "response.reasoning_summary_part.done",
4799
4885
  "response.reasoning_summary_text.delta"
@@ -4850,6 +4936,20 @@ var openaiResponsesChunkSchema = lazySchema20(
4850
4936
  })
4851
4937
  ).nullish()
4852
4938
  }),
4939
+ z20.object({
4940
+ type: z20.literal("response.refusal.delta"),
4941
+ item_id: z20.string(),
4942
+ output_index: z20.number().nullish(),
4943
+ content_index: z20.number().nullish(),
4944
+ delta: z20.string()
4945
+ }),
4946
+ z20.object({
4947
+ type: z20.literal("response.refusal.done"),
4948
+ item_id: z20.string(),
4949
+ output_index: z20.number().nullish(),
4950
+ content_index: z20.number().nullish(),
4951
+ refusal: z20.string()
4952
+ }),
4853
4953
  z20.object({
4854
4954
  type: z20.enum(["response.completed", "response.incomplete"]),
4855
4955
  response: z20.object({
@@ -5375,53 +5475,60 @@ var openaiResponsesResponseSchema = lazySchema20(
5375
5475
  role: z20.literal("assistant"),
5376
5476
  id: z20.string(),
5377
5477
  phase: z20.enum(["commentary", "final_answer"]).nullish(),
5478
+ // the API returns either text or a refusal as content parts:
5378
5479
  content: z20.array(
5379
- z20.object({
5380
- type: z20.literal("output_text"),
5381
- text: z20.string(),
5382
- logprobs: z20.array(
5383
- z20.object({
5384
- token: z20.string(),
5385
- logprob: z20.number(),
5386
- top_logprobs: z20.array(
5387
- z20.object({
5388
- token: z20.string(),
5389
- logprob: z20.number()
5390
- })
5391
- )
5392
- })
5393
- ).nullish(),
5394
- annotations: z20.array(
5395
- z20.discriminatedUnion("type", [
5396
- z20.object({
5397
- type: z20.literal("url_citation"),
5398
- start_index: z20.number(),
5399
- end_index: z20.number(),
5400
- url: z20.string(),
5401
- title: z20.string()
5402
- }),
5403
- z20.object({
5404
- type: z20.literal("file_citation"),
5405
- file_id: z20.string(),
5406
- filename: z20.string(),
5407
- index: z20.number()
5408
- }),
5409
- z20.object({
5410
- type: z20.literal("container_file_citation"),
5411
- container_id: z20.string(),
5412
- file_id: z20.string(),
5413
- filename: z20.string(),
5414
- start_index: z20.number(),
5415
- end_index: z20.number()
5416
- }),
5480
+ z20.discriminatedUnion("type", [
5481
+ z20.object({
5482
+ type: z20.literal("output_text"),
5483
+ text: z20.string(),
5484
+ logprobs: z20.array(
5417
5485
  z20.object({
5418
- type: z20.literal("file_path"),
5419
- file_id: z20.string(),
5420
- index: z20.number()
5486
+ token: z20.string(),
5487
+ logprob: z20.number(),
5488
+ top_logprobs: z20.array(
5489
+ z20.object({
5490
+ token: z20.string(),
5491
+ logprob: z20.number()
5492
+ })
5493
+ )
5421
5494
  })
5422
- ])
5423
- )
5424
- })
5495
+ ).nullish(),
5496
+ annotations: z20.array(
5497
+ z20.discriminatedUnion("type", [
5498
+ z20.object({
5499
+ type: z20.literal("url_citation"),
5500
+ start_index: z20.number(),
5501
+ end_index: z20.number(),
5502
+ url: z20.string(),
5503
+ title: z20.string()
5504
+ }),
5505
+ z20.object({
5506
+ type: z20.literal("file_citation"),
5507
+ file_id: z20.string(),
5508
+ filename: z20.string(),
5509
+ index: z20.number()
5510
+ }),
5511
+ z20.object({
5512
+ type: z20.literal("container_file_citation"),
5513
+ container_id: z20.string(),
5514
+ file_id: z20.string(),
5515
+ filename: z20.string(),
5516
+ start_index: z20.number(),
5517
+ end_index: z20.number()
5518
+ }),
5519
+ z20.object({
5520
+ type: z20.literal("file_path"),
5521
+ file_id: z20.string(),
5522
+ index: z20.number()
5523
+ })
5524
+ ])
5525
+ )
5526
+ }),
5527
+ z20.object({
5528
+ type: z20.literal("refusal"),
5529
+ refusal: z20.string()
5530
+ })
5531
+ ])
5425
5532
  )
5426
5533
  }),
5427
5534
  z20.object({
@@ -6851,6 +6958,7 @@ var OpenAIResponsesLanguageModel = class {
6851
6958
  )) != null ? _b : [];
6852
6959
  let hasFunctionCall = false;
6853
6960
  const hostedToolSearchCallIds = [];
6961
+ let hasRefusal = false;
6854
6962
  for (const part of response.output) {
6855
6963
  switch (part.type) {
6856
6964
  case "reasoning": {
@@ -6984,6 +7092,20 @@ var OpenAIResponsesLanguageModel = class {
6984
7092
  }
6985
7093
  case "message": {
6986
7094
  for (const contentPart of part.content) {
7095
+ if (contentPart.type === "refusal") {
7096
+ hasRefusal = true;
7097
+ content.push({
7098
+ type: "text",
7099
+ text: contentPart.refusal,
7100
+ providerMetadata: {
7101
+ [providerOptionsName]: {
7102
+ itemId: part.id,
7103
+ ...part.phase != null && { phase: part.phase }
7104
+ }
7105
+ }
7106
+ });
7107
+ continue;
7108
+ }
6987
7109
  if (((_h = (_g = options.providerOptions) == null ? void 0 : _g[providerOptionsName]) == null ? void 0 : _h.logprobs) && contentPart.logprobs) {
6988
7110
  logprobs.push(contentPart.logprobs);
6989
7111
  }
@@ -7283,7 +7405,8 @@ var OpenAIResponsesLanguageModel = class {
7283
7405
  finishReason: {
7284
7406
  unified: mapOpenAIResponseFinishReason({
7285
7407
  finishReason: (_C = response.incomplete_details) == null ? void 0 : _C.reason,
7286
- hasFunctionCall
7408
+ hasFunctionCall,
7409
+ hasRefusal
7287
7410
  }),
7288
7411
  raw: (_E = (_D = response.incomplete_details) == null ? void 0 : _D.reason) != null ? _E : void 0
7289
7412
  },
@@ -7354,6 +7477,7 @@ var OpenAIResponsesLanguageModel = class {
7354
7477
  const ongoingAnnotations = [];
7355
7478
  let activeMessagePhase;
7356
7479
  let hasFunctionCall = false;
7480
+ let hasRefusal = false;
7357
7481
  const activeReasoning = {};
7358
7482
  let serviceTier;
7359
7483
  let reasoningContext;
@@ -8127,6 +8251,15 @@ var OpenAIResponsesLanguageModel = class {
8127
8251
  if (((_u = (_t = options.providerOptions) == null ? void 0 : _t[providerOptionsName]) == null ? void 0 : _u.logprobs) && value.logprobs) {
8128
8252
  logprobs.push(value.logprobs);
8129
8253
  }
8254
+ } else if (isRefusalDeltaChunk(value)) {
8255
+ hasRefusal = true;
8256
+ controller.enqueue({
8257
+ type: "text-delta",
8258
+ id: value.item_id,
8259
+ delta: value.delta
8260
+ });
8261
+ } else if (isRefusalDoneChunk(value)) {
8262
+ hasRefusal = true;
8130
8263
  } else if (value.type === "response.reasoning_summary_part.added") {
8131
8264
  if (value.summary_index > 0) {
8132
8265
  const activeReasoningPart = activeReasoning[value.item_id];
@@ -8189,7 +8322,8 @@ var OpenAIResponsesLanguageModel = class {
8189
8322
  finishReason = {
8190
8323
  unified: mapOpenAIResponseFinishReason({
8191
8324
  finishReason: (_x = value.response.incomplete_details) == null ? void 0 : _x.reason,
8192
- hasFunctionCall
8325
+ hasFunctionCall,
8326
+ hasRefusal
8193
8327
  }),
8194
8328
  raw: (_z = (_y = value.response.incomplete_details) == null ? void 0 : _y.reason) != null ? _z : void 0
8195
8329
  };
@@ -8343,6 +8477,12 @@ var OpenAIResponsesLanguageModel = class {
8343
8477
  function isTextDeltaChunk(chunk) {
8344
8478
  return chunk.type === "response.output_text.delta";
8345
8479
  }
8480
+ function isRefusalDeltaChunk(chunk) {
8481
+ return chunk.type === "response.refusal.delta";
8482
+ }
8483
+ function isRefusalDoneChunk(chunk) {
8484
+ return chunk.type === "response.refusal.done";
8485
+ }
8346
8486
  function isOpenAIChatCompletionChunk(value) {
8347
8487
  const chunk = asRecord2(value);
8348
8488
  return chunk != null && Array.isArray(chunk.choices) && typeof chunk.type !== "string";