ai 7.0.152 → 7.0.153

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,16 @@
1
1
  # ai
2
2
 
3
+ ## 7.0.153
4
+
5
+ ### Patch Changes
6
+
7
+ - 5475347: Fix non-streaming Responses API structured output failing when multiple assistant messages contain JSON. Providers can supply separate structured-output text for parsing, allowing OpenAI and Azure Responses models to select the last `final_answer` while preserving all generated content, annotations, conversation history, and token usage.
8
+ - Updated dependencies [5475347]
9
+ - Updated dependencies [e729d58]
10
+ - @ai-sdk/provider@4.0.34
11
+ - @ai-sdk/provider-utils@5.0.68
12
+ - @ai-sdk/gateway@4.0.126
13
+
3
14
  ## 7.0.152
4
15
 
5
16
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1175,7 +1175,7 @@ function cloneValue(value) {
1175
1175
  }
1176
1176
  //#endregion
1177
1177
  //#region src/version.ts
1178
- const VERSION = "7.0.152";
1178
+ const VERSION = "7.0.153";
1179
1179
  //#endregion
1180
1180
  //#region src/util/download/download.ts
1181
1181
  /**
@@ -6012,11 +6012,15 @@ async function generateText({ model: modelArg, tools, toolChoice, instructions,
6012
6012
  callbacks: [onEnd, telemetryDispatcher.onEnd]
6013
6013
  });
6014
6014
  let resolvedOutput;
6015
- if (lastStep.finishReason === "stop" || lastStep.finishReason !== "tool-calls" && lastStep.text.length > 0) resolvedOutput = await (output ?? text()).parseCompleteOutput({ text: lastStep.text }, {
6016
- response: lastStep.response,
6017
- usage: lastStep.usage,
6018
- finishReason: lastStep.finishReason
6019
- });
6015
+ if (lastStep.finishReason === "stop" || lastStep.finishReason !== "tool-calls" && lastStep.text.length > 0) {
6016
+ const outputSpecification = output ?? text();
6017
+ const textForOutput = (await outputSpecification.responseFormat)?.type === "json" ? currentModelResponse.structuredOutputText ?? lastStep.text : lastStep.text;
6018
+ resolvedOutput = await outputSpecification.parseCompleteOutput({ text: textForOutput }, {
6019
+ response: lastStep.response,
6020
+ usage: lastStep.usage,
6021
+ finishReason: lastStep.finishReason
6022
+ });
6023
+ }
6020
6024
  return new DefaultGenerateTextResult({
6021
6025
  initialResponseMessages,
6022
6026
  steps,
@@ -13911,7 +13915,7 @@ async function generateObject(options) {
13911
13915
  },
13912
13916
  callbacks: [resolvedOnStepEnd, telemetryDispatcher.onObjectStepEnd]
13913
13917
  });
13914
- const object = await parseAndValidateObjectResultWithRepair(text, outputStrategy, repairText, {
13918
+ const object = await parseAndValidateObjectResultWithRepair(generateResult.structuredOutputText ?? text, outputStrategy, repairText, {
13915
13919
  response,
13916
13920
  usage,
13917
13921
  finishReason
@@ -15937,7 +15941,7 @@ function extractJsonMiddleware(options) {
15937
15941
  return {
15938
15942
  specificationVersion: "v4",
15939
15943
  wrapGenerate: async ({ doGenerate }) => {
15940
- const { content, ...rest } = await doGenerate();
15944
+ const { content, structuredOutputText, ...rest } = await doGenerate();
15941
15945
  const transformedContent = [];
15942
15946
  for (const part of content) {
15943
15947
  if (part.type !== "text") {
@@ -15951,7 +15955,8 @@ function extractJsonMiddleware(options) {
15951
15955
  }
15952
15956
  return {
15953
15957
  content: transformedContent,
15954
- ...rest
15958
+ ...rest,
15959
+ ...structuredOutputText != null && { structuredOutputText: transform(structuredOutputText) }
15955
15960
  };
15956
15961
  },
15957
15962
  wrapStream: async ({ doStream }) => {
@@ -16083,43 +16088,55 @@ function extractReasoningMiddleware({ tagName, separator = "\n", startWithReason
16083
16088
  });
16084
16089
  const escapedOpeningTag = openingTag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16085
16090
  const escapedClosingTag = closingTag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16091
+ function extractReasoning(input) {
16092
+ const text = startWithReasoning ? openingTag + input : input;
16093
+ const regexp = new RegExp(`${escapedOpeningTag}(.*?)${escapedClosingTag}`, "gs");
16094
+ const matches = Array.from(text.matchAll(regexp));
16095
+ if (!matches.length) return {
16096
+ text: input,
16097
+ reasoning: void 0
16098
+ };
16099
+ const reasoning = matches.map((match) => match[1]).join(separator);
16100
+ let textWithoutReasoning = text;
16101
+ for (let i = matches.length - 1; i >= 0; i--) {
16102
+ const match = matches[i];
16103
+ const beforeMatch = textWithoutReasoning.slice(0, match.index);
16104
+ const afterMatch = textWithoutReasoning.slice(match.index + match[0].length);
16105
+ textWithoutReasoning = beforeMatch + (beforeMatch.length > 0 && afterMatch.length > 0 ? separator : "") + afterMatch;
16106
+ }
16107
+ return {
16108
+ text: textWithoutReasoning,
16109
+ reasoning
16110
+ };
16111
+ }
16086
16112
  return {
16087
16113
  specificationVersion: "v4",
16088
16114
  wrapGenerate: async ({ doGenerate }) => {
16089
- const { content, ...rest } = await doGenerate();
16115
+ const { content, structuredOutputText, ...rest } = await doGenerate();
16090
16116
  const transformedContent = [];
16091
16117
  for (const part of content) {
16092
16118
  if (part.type !== "text") {
16093
16119
  transformedContent.push(part);
16094
16120
  continue;
16095
16121
  }
16096
- const text = startWithReasoning ? openingTag + part.text : part.text;
16097
- const regexp = new RegExp(`${escapedOpeningTag}(.*?)${escapedClosingTag}`, "gs");
16098
- const matches = Array.from(text.matchAll(regexp));
16099
- if (!matches.length) {
16122
+ const { text, reasoning } = extractReasoning(part.text);
16123
+ if (reasoning === void 0) {
16100
16124
  transformedContent.push(part);
16101
16125
  continue;
16102
16126
  }
16103
- const reasoningText = matches.map((match) => match[1]).join(separator);
16104
- let textWithoutReasoning = text;
16105
- for (let i = matches.length - 1; i >= 0; i--) {
16106
- const match = matches[i];
16107
- const beforeMatch = textWithoutReasoning.slice(0, match.index);
16108
- const afterMatch = textWithoutReasoning.slice(match.index + match[0].length);
16109
- textWithoutReasoning = beforeMatch + (beforeMatch.length > 0 && afterMatch.length > 0 ? separator : "") + afterMatch;
16110
- }
16111
16127
  transformedContent.push({
16112
16128
  type: "reasoning",
16113
- text: reasoningText
16129
+ text: reasoning
16114
16130
  });
16115
16131
  transformedContent.push({
16116
16132
  type: "text",
16117
- text: textWithoutReasoning
16133
+ text
16118
16134
  });
16119
16135
  }
16120
16136
  return {
16121
16137
  content: transformedContent,
16122
- ...rest
16138
+ ...rest,
16139
+ ...structuredOutputText != null && { structuredOutputText: extractReasoning(structuredOutputText).text }
16123
16140
  };
16124
16141
  },
16125
16142
  wrapStream: async ({ doStream }) => {