@ai-sdk/openai 4.0.109 → 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.
@@ -923,6 +923,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
923
923
  let hasFunctionCall = false;
924
924
  const hostedToolSearchCallIds: string[] = [];
925
925
 
926
+ // flag that checks if the model refused to answer:
927
+ let hasRefusal = false;
928
+
926
929
  let structuredOutputText: string | undefined;
927
930
 
928
931
  // map response content to content array
@@ -1092,11 +1095,32 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1092
1095
  part.phase === 'final_answer'
1093
1096
  ) {
1094
1097
  structuredOutputText = part.content
1095
- .map(contentPart => contentPart.text)
1098
+ .map(contentPart =>
1099
+ contentPart.type === 'output_text'
1100
+ ? contentPart.text
1101
+ : contentPart.refusal,
1102
+ )
1096
1103
  .join('');
1097
1104
  }
1098
1105
 
1099
1106
  for (const contentPart of part.content) {
1107
+ if (contentPart.type === 'refusal') {
1108
+ hasRefusal = true;
1109
+
1110
+ content.push({
1111
+ type: 'text',
1112
+ text: contentPart.refusal,
1113
+ providerMetadata: {
1114
+ [providerOptionsName]: {
1115
+ itemId: part.id,
1116
+ ...(part.phase != null && { phase: part.phase }),
1117
+ } satisfies ResponsesTextProviderMetadata,
1118
+ },
1119
+ });
1120
+
1121
+ continue;
1122
+ }
1123
+
1100
1124
  if (
1101
1125
  options.providerOptions?.[providerOptionsName]?.logprobs &&
1102
1126
  contentPart.logprobs
@@ -1529,6 +1553,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1529
1553
  unified: mapOpenAIResponseFinishReason({
1530
1554
  finishReason: response.incomplete_details?.reason,
1531
1555
  hasFunctionCall,
1556
+ hasRefusal,
1532
1557
  }),
1533
1558
  raw: response.incomplete_details?.reason ?? undefined,
1534
1559
  },
@@ -1658,6 +1683,9 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1658
1683
  // flag that checks if there have been client-side tool calls (not executed by openai)
1659
1684
  let hasFunctionCall = false;
1660
1685
 
1686
+ // flag that checks if the model refused to answer:
1687
+ let hasRefusal = false;
1688
+
1661
1689
  const activeReasoning: Record<
1662
1690
  string,
1663
1691
  {
@@ -2772,6 +2800,20 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2772
2800
  ) {
2773
2801
  logprobs.push(value.logprobs);
2774
2802
  }
2803
+ } else if (isRefusalDeltaChunk(value)) {
2804
+ hasRefusal = true;
2805
+
2806
+ const itemId = resolveOutputItemId({
2807
+ itemId: value.item_id,
2808
+ outputIndex: value.output_index,
2809
+ });
2810
+ controller.enqueue({
2811
+ type: 'text-delta',
2812
+ id: itemId,
2813
+ delta: value.delta,
2814
+ });
2815
+ } else if (isRefusalDoneChunk(value)) {
2816
+ hasRefusal = true;
2775
2817
  } else if (value.type === 'response.reasoning_summary_part.added') {
2776
2818
  const itemId = resolveOutputItemId({
2777
2819
  itemId: value.item_id,
@@ -2872,6 +2914,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2872
2914
  unified: mapOpenAIResponseFinishReason({
2873
2915
  finishReason: value.response.incomplete_details?.reason,
2874
2916
  hasFunctionCall,
2917
+ hasRefusal,
2875
2918
  }),
2876
2919
  raw: value.response.incomplete_details?.reason ?? undefined,
2877
2920
  };
@@ -3014,6 +3057,18 @@ function isTextDeltaChunk(
3014
3057
  return chunk.type === 'response.output_text.delta';
3015
3058
  }
3016
3059
 
3060
+ function isRefusalDeltaChunk(
3061
+ chunk: OpenAIResponsesChunk,
3062
+ ): chunk is OpenAIResponsesChunk & { type: 'response.refusal.delta' } {
3063
+ return chunk.type === 'response.refusal.delta';
3064
+ }
3065
+
3066
+ function isRefusalDoneChunk(
3067
+ chunk: OpenAIResponsesChunk,
3068
+ ): chunk is OpenAIResponsesChunk & { type: 'response.refusal.done' } {
3069
+ return chunk.type === 'response.refusal.done';
3070
+ }
3071
+
3017
3072
  function isOpenAIChatCompletionChunk(value: unknown): boolean {
3018
3073
  const chunk = asRecord(value);
3019
3074