@ai-sdk/openai 2.0.140 → 2.0.142

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.
@@ -363,10 +363,13 @@ function getResponseMetadata({
363
363
  }
364
364
 
365
365
  // src/chat/map-openai-finish-reason.ts
366
- function mapOpenAIFinishReason(finishReason) {
366
+ function mapOpenAIFinishReason({
367
+ finishReason,
368
+ hasRefusal = false
369
+ }) {
367
370
  switch (finishReason) {
368
371
  case "stop":
369
- return "stop";
372
+ return hasRefusal ? "content-filter" : "stop";
370
373
  case "length":
371
374
  return "length";
372
375
  case "content_filter":
@@ -396,6 +399,8 @@ var openaiChatResponseSchema = lazyValidator(
396
399
  message: z2.object({
397
400
  role: z2.literal("assistant").nullish(),
398
401
  content: z2.string().nullish(),
402
+ // set instead of `content` when the model refuses to answer:
403
+ refusal: z2.string().nullish(),
399
404
  audio: z2.object({
400
405
  transcript: z2.string().nullish()
401
406
  }).nullish(),
@@ -434,6 +439,20 @@ var openaiChatResponseSchema = lazyValidator(
434
439
  })
435
440
  )
436
441
  })
442
+ ).nullish(),
443
+ // logprobs of the refusal tokens (set instead of `content` when
444
+ // the model refuses to answer):
445
+ refusal: z2.array(
446
+ z2.object({
447
+ token: z2.string(),
448
+ logprob: z2.number(),
449
+ top_logprobs: z2.array(
450
+ z2.object({
451
+ token: z2.string(),
452
+ logprob: z2.number()
453
+ })
454
+ )
455
+ })
437
456
  ).nullish()
438
457
  }).nullish(),
439
458
  finish_reason: z2.string().nullish()
@@ -468,6 +487,8 @@ var openaiChatChunkSchema = lazyValidator(
468
487
  delta: z2.object({
469
488
  role: z2.enum(["assistant"]).nullish(),
470
489
  content: z2.string().nullish(),
490
+ // set instead of `content` when the model refuses to answer:
491
+ refusal: z2.string().nullish(),
471
492
  tool_calls: z2.array(
472
493
  z2.object({
473
494
  index: z2.number(),
@@ -503,6 +524,20 @@ var openaiChatChunkSchema = lazyValidator(
503
524
  })
504
525
  )
505
526
  })
527
+ ).nullish(),
528
+ // logprobs of the refusal tokens (set instead of `content` when
529
+ // the model refuses to answer):
530
+ refusal: z2.array(
531
+ z2.object({
532
+ token: z2.string(),
533
+ logprob: z2.number(),
534
+ top_logprobs: z2.array(
535
+ z2.object({
536
+ token: z2.string(),
537
+ logprob: z2.number()
538
+ })
539
+ )
540
+ })
506
541
  ).nullish()
507
542
  }).nullish(),
508
543
  finish_reason: z2.string().nullish(),
@@ -939,7 +974,7 @@ var OpenAIChatLanguageModel = class {
939
974
  };
940
975
  }
941
976
  async doGenerate(options) {
942
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
977
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
943
978
  const { args: body, warnings } = await this.getArgs(options);
944
979
  const {
945
980
  responseHeaders,
@@ -971,6 +1006,11 @@ var OpenAIChatLanguageModel = class {
971
1006
  if (text != null && text.length > 0) {
972
1007
  content.push({ type: "text", text });
973
1008
  }
1009
+ const refusal = choice.message.refusal;
1010
+ const hasRefusal = refusal != null && refusal.length > 0;
1011
+ if (hasRefusal) {
1012
+ content.push({ type: "text", text: refusal });
1013
+ }
974
1014
  for (const toolCall of (_b = choice.message.tool_calls) != null ? _b : []) {
975
1015
  content.push({
976
1016
  type: "tool-call",
@@ -1002,18 +1042,26 @@ var OpenAIChatLanguageModel = class {
1002
1042
  cacheWriteTokens: promptTokenDetails.cache_write_tokens
1003
1043
  };
1004
1044
  }
1005
- if (((_g = choice.logprobs) == null ? void 0 : _g.content) != null) {
1006
- providerMetadata.openai.logprobs = choice.logprobs.content;
1045
+ const contentLogprobs = (_g = choice.logprobs) == null ? void 0 : _g.content;
1046
+ const refusalLogprobs = (_h = choice.logprobs) == null ? void 0 : _h.refusal;
1047
+ if (contentLogprobs != null || refusalLogprobs != null) {
1048
+ providerMetadata.openai.logprobs = [
1049
+ ...contentLogprobs != null ? contentLogprobs : [],
1050
+ ...refusalLogprobs != null ? refusalLogprobs : []
1051
+ ];
1007
1052
  }
1008
1053
  return {
1009
1054
  content,
1010
- finishReason: mapOpenAIFinishReason(choice.finish_reason),
1055
+ finishReason: mapOpenAIFinishReason({
1056
+ finishReason: choice.finish_reason,
1057
+ hasRefusal
1058
+ }),
1011
1059
  usage: {
1012
- inputTokens: (_i = (_h = response.usage) == null ? void 0 : _h.prompt_tokens) != null ? _i : void 0,
1013
- outputTokens: (_k = (_j = response.usage) == null ? void 0 : _j.completion_tokens) != null ? _k : void 0,
1014
- totalTokens: (_m = (_l = response.usage) == null ? void 0 : _l.total_tokens) != null ? _m : void 0,
1015
- reasoningTokens: (_n = completionTokenDetails == null ? void 0 : completionTokenDetails.reasoning_tokens) != null ? _n : void 0,
1016
- cachedInputTokens: (_o = promptTokenDetails == null ? void 0 : promptTokenDetails.cached_tokens) != null ? _o : void 0
1060
+ inputTokens: (_j = (_i = response.usage) == null ? void 0 : _i.prompt_tokens) != null ? _j : void 0,
1061
+ outputTokens: (_l = (_k = response.usage) == null ? void 0 : _k.completion_tokens) != null ? _l : void 0,
1062
+ totalTokens: (_n = (_m = response.usage) == null ? void 0 : _m.total_tokens) != null ? _n : void 0,
1063
+ reasoningTokens: (_o = completionTokenDetails == null ? void 0 : completionTokenDetails.reasoning_tokens) != null ? _o : void 0,
1064
+ cachedInputTokens: (_p = promptTokenDetails == null ? void 0 : promptTokenDetails.cached_tokens) != null ? _p : void 0
1017
1065
  },
1018
1066
  request: { body },
1019
1067
  response: {
@@ -1057,6 +1105,10 @@ var OpenAIChatLanguageModel = class {
1057
1105
  };
1058
1106
  let metadataExtracted = false;
1059
1107
  let isActiveText = false;
1108
+ let hasRefusal = false;
1109
+ let rawFinishReason = void 0;
1110
+ let hasLogprobs = false;
1111
+ const streamedLogprobs = [];
1060
1112
  const providerMetadata = { openai: {} };
1061
1113
  return {
1062
1114
  stream: response.pipeThrough(
@@ -1069,18 +1121,20 @@ var OpenAIChatLanguageModel = class {
1069
1121
  controller.enqueue({ type: "stream-start", warnings });
1070
1122
  },
1071
1123
  transform(chunk, controller) {
1072
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
1124
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1073
1125
  if (options.includeRawChunks) {
1074
1126
  controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
1075
1127
  }
1076
1128
  if (!chunk.success) {
1077
1129
  finishReason = "error";
1130
+ rawFinishReason = void 0;
1078
1131
  controller.enqueue({ type: "error", error: chunk.error });
1079
1132
  return;
1080
1133
  }
1081
1134
  const value = chunk.value;
1082
1135
  if ("error" in value) {
1083
1136
  finishReason = "error";
1137
+ rawFinishReason = void 0;
1084
1138
  controller.enqueue({ type: "error", error: value.error });
1085
1139
  return;
1086
1140
  }
@@ -1114,16 +1168,35 @@ var OpenAIChatLanguageModel = class {
1114
1168
  }
1115
1169
  const choice = value.choices[0];
1116
1170
  if ((choice == null ? void 0 : choice.finish_reason) != null) {
1117
- finishReason = mapOpenAIFinishReason(choice.finish_reason);
1171
+ rawFinishReason = choice.finish_reason;
1118
1172
  }
1119
- if (((_m = choice == null ? void 0 : choice.logprobs) == null ? void 0 : _m.content) != null) {
1120
- providerMetadata.openai.logprobs = choice.logprobs.content;
1173
+ if ((choice == null ? void 0 : choice.logprobs) != null) {
1174
+ const contentLogprobs = choice.logprobs.content;
1175
+ const refusalLogprobs = choice.logprobs.refusal;
1176
+ if (contentLogprobs != null || refusalLogprobs != null) {
1177
+ hasLogprobs = true;
1178
+ streamedLogprobs.push(
1179
+ ...contentLogprobs != null ? contentLogprobs : [],
1180
+ ...refusalLogprobs != null ? refusalLogprobs : []
1181
+ );
1182
+ }
1121
1183
  }
1122
1184
  if ((choice == null ? void 0 : choice.delta) == null) {
1123
1185
  return;
1124
1186
  }
1125
1187
  const delta = choice.delta;
1126
- if (delta.content != null) {
1188
+ const textDeltas = [];
1189
+ if (delta.content != null && delta.content.length > 0) {
1190
+ textDeltas.push(delta.content);
1191
+ }
1192
+ if (delta.refusal != null && delta.refusal.length > 0) {
1193
+ hasRefusal = true;
1194
+ textDeltas.push(delta.refusal);
1195
+ }
1196
+ if (textDeltas.length === 0 && delta.content === "") {
1197
+ textDeltas.push(delta.content);
1198
+ }
1199
+ for (const textDelta of textDeltas) {
1127
1200
  if (!isActiveText) {
1128
1201
  controller.enqueue({ type: "text-start", id: "0" });
1129
1202
  isActiveText = true;
@@ -1131,7 +1204,7 @@ var OpenAIChatLanguageModel = class {
1131
1204
  controller.enqueue({
1132
1205
  type: "text-delta",
1133
1206
  id: "0",
1134
- delta: delta.content
1207
+ delta: textDelta
1135
1208
  });
1136
1209
  }
1137
1210
  if (delta.tool_calls != null) {
@@ -1156,6 +1229,15 @@ var OpenAIChatLanguageModel = class {
1156
1229
  controller.enqueue({ type: "text-end", id: "0" });
1157
1230
  }
1158
1231
  toolCallTracker.flush();
1232
+ if (rawFinishReason != null) {
1233
+ finishReason = mapOpenAIFinishReason({
1234
+ finishReason: rawFinishReason,
1235
+ hasRefusal
1236
+ });
1237
+ }
1238
+ if (hasLogprobs) {
1239
+ providerMetadata.openai.logprobs = streamedLogprobs;
1240
+ }
1159
1241
  controller.enqueue({
1160
1242
  type: "finish",
1161
1243
  finishReason,
@@ -3188,12 +3270,14 @@ function serializeToolCallArguments2(input) {
3188
3270
  // src/responses/map-openai-responses-finish-reason.ts
3189
3271
  function mapOpenAIResponseFinishReason({
3190
3272
  finishReason,
3191
- hasFunctionCall
3273
+ hasFunctionCall,
3274
+ hasRefusal = false
3192
3275
  }) {
3193
3276
  switch (finishReason) {
3194
3277
  case void 0:
3195
3278
  case null:
3196
- return hasFunctionCall ? "tool-calls" : "stop";
3279
+ if (hasFunctionCall) return "tool-calls";
3280
+ return hasRefusal ? "content-filter" : "stop";
3197
3281
  case "max_output_tokens":
3198
3282
  return "length";
3199
3283
  case "content_filter":
@@ -3229,6 +3313,20 @@ var openaiResponsesChunkSchema = lazyValidator13(
3229
3313
  })
3230
3314
  ).nullish()
3231
3315
  }),
3316
+ z17.object({
3317
+ type: z17.literal("response.refusal.delta"),
3318
+ item_id: z17.string(),
3319
+ output_index: z17.number().nullish(),
3320
+ content_index: z17.number().nullish(),
3321
+ delta: z17.string()
3322
+ }),
3323
+ z17.object({
3324
+ type: z17.literal("response.refusal.done"),
3325
+ item_id: z17.string(),
3326
+ output_index: z17.number().nullish(),
3327
+ content_index: z17.number().nullish(),
3328
+ refusal: z17.string()
3329
+ }),
3232
3330
  z17.object({
3233
3331
  type: z17.enum(["response.completed", "response.incomplete"]),
3234
3332
  response: z17.object({
@@ -3515,57 +3613,64 @@ var openaiResponsesResponseSchema = lazyValidator13(
3515
3613
  role: z17.literal("assistant"),
3516
3614
  id: z17.string(),
3517
3615
  phase: z17.enum(["commentary", "final_answer"]).nullish(),
3616
+ // the API returns either text or a refusal as content parts:
3518
3617
  content: z17.array(
3519
- z17.object({
3520
- type: z17.literal("output_text"),
3521
- text: z17.string(),
3522
- logprobs: z17.array(
3523
- z17.object({
3524
- token: z17.string(),
3525
- logprob: z17.number(),
3526
- top_logprobs: z17.array(
3527
- z17.object({
3528
- token: z17.string(),
3529
- logprob: z17.number()
3530
- })
3531
- )
3532
- })
3533
- ).nullish(),
3534
- annotations: z17.array(
3535
- z17.discriminatedUnion("type", [
3536
- z17.object({
3537
- type: z17.literal("url_citation"),
3538
- start_index: z17.number(),
3539
- end_index: z17.number(),
3540
- url: z17.string(),
3541
- title: z17.string()
3542
- }),
3543
- z17.object({
3544
- type: z17.literal("file_citation"),
3545
- file_id: z17.string(),
3546
- filename: z17.string().nullish(),
3547
- index: z17.number().nullish(),
3548
- start_index: z17.number().nullish(),
3549
- end_index: z17.number().nullish(),
3550
- quote: z17.string().nullish()
3551
- }),
3552
- z17.object({
3553
- type: z17.literal("container_file_citation"),
3554
- container_id: z17.string(),
3555
- file_id: z17.string(),
3556
- filename: z17.string().nullish(),
3557
- start_index: z17.number().nullish(),
3558
- end_index: z17.number().nullish(),
3559
- index: z17.number().nullish()
3560
- }),
3618
+ z17.discriminatedUnion("type", [
3619
+ z17.object({
3620
+ type: z17.literal("output_text"),
3621
+ text: z17.string(),
3622
+ logprobs: z17.array(
3561
3623
  z17.object({
3562
- type: z17.literal("file_path"),
3563
- file_id: z17.string(),
3564
- index: z17.number().nullish()
3624
+ token: z17.string(),
3625
+ logprob: z17.number(),
3626
+ top_logprobs: z17.array(
3627
+ z17.object({
3628
+ token: z17.string(),
3629
+ logprob: z17.number()
3630
+ })
3631
+ )
3565
3632
  })
3566
- ])
3567
- )
3568
- })
3633
+ ).nullish(),
3634
+ annotations: z17.array(
3635
+ z17.discriminatedUnion("type", [
3636
+ z17.object({
3637
+ type: z17.literal("url_citation"),
3638
+ start_index: z17.number(),
3639
+ end_index: z17.number(),
3640
+ url: z17.string(),
3641
+ title: z17.string()
3642
+ }),
3643
+ z17.object({
3644
+ type: z17.literal("file_citation"),
3645
+ file_id: z17.string(),
3646
+ filename: z17.string().nullish(),
3647
+ index: z17.number().nullish(),
3648
+ start_index: z17.number().nullish(),
3649
+ end_index: z17.number().nullish(),
3650
+ quote: z17.string().nullish()
3651
+ }),
3652
+ z17.object({
3653
+ type: z17.literal("container_file_citation"),
3654
+ container_id: z17.string(),
3655
+ file_id: z17.string(),
3656
+ filename: z17.string().nullish(),
3657
+ start_index: z17.number().nullish(),
3658
+ end_index: z17.number().nullish(),
3659
+ index: z17.number().nullish()
3660
+ }),
3661
+ z17.object({
3662
+ type: z17.literal("file_path"),
3663
+ file_id: z17.string(),
3664
+ index: z17.number().nullish()
3665
+ })
3666
+ ])
3667
+ )
3668
+ }),
3669
+ z17.object({
3670
+ type: z17.literal("refusal"),
3671
+ refusal: z17.string()
3672
+ })
3673
+ ])
3569
3674
  )
3570
3675
  }),
3571
3676
  z17.object({
@@ -4427,6 +4532,7 @@ var OpenAIResponsesLanguageModel = class {
4427
4532
  const content = [];
4428
4533
  const logprobs = [];
4429
4534
  let hasFunctionCall = false;
4535
+ let hasRefusal = false;
4430
4536
  for (const part of response.output) {
4431
4537
  switch (part.type) {
4432
4538
  case "reasoning": {
@@ -4484,6 +4590,20 @@ var OpenAIResponsesLanguageModel = class {
4484
4590
  }
4485
4591
  case "message": {
4486
4592
  for (const contentPart of part.content) {
4593
+ if (contentPart.type === "refusal") {
4594
+ hasRefusal = true;
4595
+ content.push({
4596
+ type: "text",
4597
+ text: contentPart.refusal,
4598
+ providerMetadata: {
4599
+ [providerKey]: {
4600
+ itemId: part.id,
4601
+ ...part.phase != null && { phase: part.phase }
4602
+ }
4603
+ }
4604
+ });
4605
+ continue;
4606
+ }
4487
4607
  if (((_c = (_b = options.providerOptions) == null ? void 0 : _b.openai) == null ? void 0 : _c.logprobs) && contentPart.logprobs) {
4488
4608
  logprobs.push(contentPart.logprobs);
4489
4609
  }
@@ -4684,7 +4804,8 @@ var OpenAIResponsesLanguageModel = class {
4684
4804
  content,
4685
4805
  finishReason: mapOpenAIResponseFinishReason({
4686
4806
  finishReason: (_y = response.incomplete_details) == null ? void 0 : _y.reason,
4687
- hasFunctionCall
4807
+ hasFunctionCall,
4808
+ hasRefusal
4688
4809
  }),
4689
4810
  usage: {
4690
4811
  inputTokens: usage.input_tokens,
@@ -4743,6 +4864,7 @@ var OpenAIResponsesLanguageModel = class {
4743
4864
  const ongoingAnnotations = [];
4744
4865
  let activeMessagePhase;
4745
4866
  let hasFunctionCall = false;
4867
+ let hasRefusal = false;
4746
4868
  const activeReasoning = {};
4747
4869
  let serviceTier;
4748
4870
  let reasoningContext;
@@ -5084,6 +5206,15 @@ var OpenAIResponsesLanguageModel = class {
5084
5206
  if (((_h = (_g = options.providerOptions) == null ? void 0 : _g.openai) == null ? void 0 : _h.logprobs) && value.logprobs) {
5085
5207
  logprobs.push(value.logprobs);
5086
5208
  }
5209
+ } else if (isRefusalDeltaChunk(value)) {
5210
+ hasRefusal = true;
5211
+ controller.enqueue({
5212
+ type: "text-delta",
5213
+ id: value.item_id,
5214
+ delta: value.delta
5215
+ });
5216
+ } else if (isRefusalDoneChunk(value)) {
5217
+ hasRefusal = true;
5087
5218
  } else if (value.type === "response.reasoning_summary_part.added") {
5088
5219
  if (value.summary_index > 0) {
5089
5220
  const activeReasoningPart = activeReasoning[value.item_id];
@@ -5140,7 +5271,8 @@ var OpenAIResponsesLanguageModel = class {
5140
5271
  } else if (isResponseFinishedChunk(value)) {
5141
5272
  finishReason = mapOpenAIResponseFinishReason({
5142
5273
  finishReason: (_k = value.response.incomplete_details) == null ? void 0 : _k.reason,
5143
- hasFunctionCall
5274
+ hasFunctionCall,
5275
+ hasRefusal
5144
5276
  });
5145
5277
  usage.inputTokens = value.response.usage.input_tokens;
5146
5278
  usage.outputTokens = value.response.usage.output_tokens;
@@ -5217,6 +5349,12 @@ var OpenAIResponsesLanguageModel = class {
5217
5349
  };
5218
5350
  }
5219
5351
  };
5352
+ function isRefusalDeltaChunk(chunk) {
5353
+ return chunk.type === "response.refusal.delta";
5354
+ }
5355
+ function isRefusalDoneChunk(chunk) {
5356
+ return chunk.type === "response.refusal.done";
5357
+ }
5220
5358
  function isTextDeltaChunk(chunk) {
5221
5359
  return chunk.type === "response.output_text.delta";
5222
5360
  }