ai 7.0.105 → 7.0.106

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,27 @@
1
1
  # ai
2
2
 
3
+ ## 7.0.106
4
+
5
+ ### Patch Changes
6
+
7
+ - 4775577: fix(ai): preserve provider metadata when simulating text streams
8
+ - 6696728: fix(ai): report the prepareStep model in streamed step results
9
+ - 09516a1: fix(ai): prevent unhandled rejections when UI message stream reading stops early
10
+ - 1aef01e: fix(ai): preserve prototype-named properties in serialized tool outputs
11
+ - 9c1ea74: fix(ai): close telemetry spans when provider response streams fail
12
+ - 107343a: fix(ai): use the prepareStep-selected model for streamed response metadata fallbacks
13
+ - 03c3e33: fix(ai): preserve tool calls required by retained pending approvals
14
+ - 5d42ebd: fix(ai): skip input available callbacks for invalid streamed tool calls
15
+ - 4a67783: fix(ai): cancel prompt attachment downloads when model calls are aborted or time out
16
+ - 1058ed5: fix(ai): strip streamed JSON fences before arbitrary trailing whitespace
17
+ - 84f5d1b: fix(ai): stream null and empty string JSON partial outputs
18
+ - 2d53a5d: fix(ai): prevent onEnd after aborting a multi-step text stream
19
+ - 2a5ed55: fix(ai): stream structured output from the final tool-loop step
20
+ - Updated dependencies [4fdf51e]
21
+ - Updated dependencies [0455398]
22
+ - @ai-sdk/gateway@4.0.86
23
+ - @ai-sdk/provider-utils@5.0.44
24
+
3
25
  ## 7.0.105
4
26
 
5
27
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1211,7 +1211,7 @@ import {
1211
1211
  } from "@ai-sdk/provider-utils";
1212
1212
 
1213
1213
  // src/version.ts
1214
- var VERSION = true ? "7.0.105" : "0.0.0-test";
1214
+ var VERSION = true ? "7.0.106" : "0.0.0-test";
1215
1215
 
1216
1216
  // src/util/download/download.ts
1217
1217
  var download = async ({
@@ -1258,9 +1258,9 @@ var download = async ({
1258
1258
  };
1259
1259
 
1260
1260
  // src/util/download/download-function.ts
1261
- var createDefaultDownloadFunction = (download2 = download) => (requestedDownloads) => Promise.all(
1261
+ var createDefaultDownloadFunction = (download2 = download, abortSignal) => (requestedDownloads) => Promise.all(
1262
1262
  requestedDownloads.map(
1263
- async (requestedDownload) => requestedDownload.isUrlSupportedByModel ? null : await download2(requestedDownload)
1263
+ async (requestedDownload) => requestedDownload.isUrlSupportedByModel ? null : await download2({ ...requestedDownload, abortSignal })
1264
1264
  )
1265
1265
  );
1266
1266
 
@@ -1409,14 +1409,15 @@ function convertToLanguageModelV4FilePart(content) {
1409
1409
  async function convertToLanguageModelPrompt({
1410
1410
  prompt,
1411
1411
  supportedUrls,
1412
- download: download2 = createDefaultDownloadFunction(),
1412
+ download: download2,
1413
+ abortSignal,
1413
1414
  // `provider` is only needed here to convert legacy tool output types via `mapToolResultOutput`.
1414
1415
  // TODO: remove in v8 when "file-id" and "image-file-id" types are removed
1415
1416
  provider
1416
1417
  }) {
1417
1418
  const downloadedAssets = await downloadAssets(
1418
1419
  prompt.messages,
1419
- download2,
1420
+ download2 != null ? download2 : createDefaultDownloadFunction(void 0, abortSignal),
1420
1421
  supportedUrls
1421
1422
  );
1422
1423
  const approvalIdToToolCallId = /* @__PURE__ */ new Map();
@@ -2040,9 +2041,6 @@ function getMediaTypeFromUrl(url, fallbackMediaType = "application/octet-stream"
2040
2041
 
2041
2042
  // src/prompt/create-tool-model-output.ts
2042
2043
  import { getErrorMessage as getErrorMessage3 } from "@ai-sdk/provider";
2043
- import {
2044
- parseJSON
2045
- } from "@ai-sdk/provider-utils";
2046
2044
  async function createToolModelOutput({
2047
2045
  toolCallId,
2048
2046
  input,
@@ -2053,19 +2051,19 @@ async function createToolModelOutput({
2053
2051
  if (errorMode === "text") {
2054
2052
  return { type: "error-text", value: getErrorMessage3(output) };
2055
2053
  } else if (errorMode === "json") {
2056
- return { type: "error-json", value: await toJSONValue(output) };
2054
+ return { type: "error-json", value: toJSONValue(output) };
2057
2055
  }
2058
2056
  if (tool3 == null ? void 0 : tool3.toModelOutput) {
2059
2057
  return await tool3.toModelOutput({ toolCallId, input, output });
2060
2058
  }
2061
- return typeof output === "string" ? { type: "text", value: output } : { type: "json", value: await toJSONValue(output) };
2059
+ return typeof output === "string" ? { type: "text", value: output } : { type: "json", value: toJSONValue(output) };
2062
2060
  }
2063
- async function toJSONValue(value) {
2061
+ function toJSONValue(value) {
2064
2062
  if (value === void 0) {
2065
2063
  return null;
2066
2064
  }
2067
2065
  const serialized = JSON.stringify(value);
2068
- return serialized === void 0 ? null : await parseJSON({ text: serialized });
2066
+ return serialized === void 0 ? null : JSON.parse(serialized);
2069
2067
  }
2070
2068
 
2071
2069
  // src/prompt/prepare-language-model-call-options.ts
@@ -6099,6 +6097,7 @@ async function generateText({
6099
6097
  },
6100
6098
  supportedUrls: await stepModel.supportedUrls,
6101
6099
  download: download2,
6100
+ abortSignal: mergedAbortSignal,
6102
6101
  provider: stepModel.provider.split(".")[0]
6103
6102
  });
6104
6103
  const stepProviderOptions = mergeObjects(
@@ -8840,7 +8839,7 @@ function invokeToolCallbacksFromStream({
8840
8839
  const toolName = ongoingToolCallToolNames[chunk.toolCallId];
8841
8840
  const tool3 = getOwn(tools, toolName);
8842
8841
  delete ongoingToolCallToolNames[chunk.toolCallId];
8843
- if ((tool3 == null ? void 0 : tool3.onInputAvailable) != null) {
8842
+ if (!chunk.invalid && (tool3 == null ? void 0 : tool3.onInputAvailable) != null) {
8844
8843
  await tool3.onInputAvailable({
8845
8844
  input: chunk.input,
8846
8845
  toolCallId: chunk.toolCallId,
@@ -8990,6 +8989,7 @@ async function streamLanguageModelCall({
8990
8989
  },
8991
8990
  supportedUrls: await resolvedModel.supportedUrls,
8992
8991
  download: download2,
8992
+ abortSignal,
8993
8993
  provider: resolvedModel.provider.split(".")[0]
8994
8994
  });
8995
8995
  const stepTools = await prepareTools({
@@ -9680,13 +9680,15 @@ function createOutputTransformStream(output) {
9680
9680
  let text2 = "";
9681
9681
  let textChunk = "";
9682
9682
  let textProviderMetadata = void 0;
9683
- let lastPublishedValue = "";
9684
- function resetAttemptState() {
9683
+ let lastPublishedValue = void 0;
9684
+ let hasPublishedValue = false;
9685
+ function resetOutputState() {
9685
9686
  firstTextChunkId = void 0;
9686
9687
  text2 = "";
9687
9688
  textChunk = "";
9688
9689
  textProviderMetadata = void 0;
9689
- lastPublishedValue = "";
9690
+ lastPublishedValue = void 0;
9691
+ hasPublishedValue = false;
9690
9692
  }
9691
9693
  function enqueueChunk({
9692
9694
  controller,
@@ -9716,10 +9718,13 @@ function createOutputTransformStream(output) {
9716
9718
  async transform(chunk, controller) {
9717
9719
  var _a25;
9718
9720
  if (isStreamRetryBoundaryPart(chunk)) {
9719
- resetAttemptState();
9721
+ resetOutputState();
9720
9722
  controller.enqueue(chunk);
9721
9723
  return;
9722
9724
  }
9725
+ if (chunk.type === "start-step") {
9726
+ resetOutputState();
9727
+ }
9723
9728
  if (chunk.type === "finish-step" && textChunk.length > 0) {
9724
9729
  publishTextChunk({ controller });
9725
9730
  }
@@ -9769,9 +9774,10 @@ function createOutputTransformStream(output) {
9769
9774
  const result = await output.parsePartialOutput({ text: text2 });
9770
9775
  if (result !== void 0) {
9771
9776
  const currentValue = typeof result.partial === "string" ? result.partial : JSON.stringify(result.partial);
9772
- if (currentValue !== lastPublishedValue) {
9777
+ if (!hasPublishedValue || currentValue !== lastPublishedValue) {
9773
9778
  publishTextChunk({ controller, partialOutput: result.partial });
9774
9779
  lastPublishedValue = currentValue;
9780
+ hasPublishedValue = true;
9775
9781
  }
9776
9782
  }
9777
9783
  }
@@ -9934,6 +9940,8 @@ var DefaultStreamTextResult = class {
9934
9940
  const initialResponseMessages = [];
9935
9941
  let stepMessagesForNextStep;
9936
9942
  let currentStepMessages = [];
9943
+ let isAborted = false;
9944
+ let currentStepModel = model;
9937
9945
  const createPartIdReserver = () => {
9938
9946
  const usedIds = /* @__PURE__ */ new Set();
9939
9947
  return (id) => {
@@ -10100,8 +10108,8 @@ var DefaultStreamTextResult = class {
10100
10108
  const currentStepResult = new DefaultStepResult({
10101
10109
  callId,
10102
10110
  stepNumber: recordedSteps.length,
10103
- provider: model.provider,
10104
- modelId: model.modelId,
10111
+ provider: currentStepModel.provider,
10112
+ modelId: currentStepModel.modelId,
10105
10113
  runtimeContext,
10106
10114
  toolsContext,
10107
10115
  content: recordedContent,
@@ -10126,8 +10134,8 @@ var DefaultStreamTextResult = class {
10126
10134
  });
10127
10135
  logWarnings({
10128
10136
  warnings: recordedWarnings,
10129
- provider: model.provider,
10130
- model: model.modelId
10137
+ provider: currentStepModel.provider,
10138
+ model: currentStepModel.modelId
10131
10139
  });
10132
10140
  recordedSteps.push(currentStepResult);
10133
10141
  stepMessagesForNextStep = [
@@ -10157,6 +10165,9 @@ var DefaultStreamTextResult = class {
10157
10165
  self._rawFinishReason.resolve(recordedRawFinishReason);
10158
10166
  self._totalUsage.resolve(totalUsage);
10159
10167
  self._steps.resolve(recordedSteps);
10168
+ if (isAborted) {
10169
+ return;
10170
+ }
10160
10171
  const finalStep = recordedSteps[recordedSteps.length - 1];
10161
10172
  const content = recordedSteps.flatMap((step) => step.content);
10162
10173
  const files = recordedSteps.flatMap((step) => step.files);
@@ -10243,7 +10254,9 @@ var DefaultStreamTextResult = class {
10243
10254
  controller.enqueue({ type: "start" });
10244
10255
  },
10245
10256
  async pull(controller) {
10257
+ var _a25;
10246
10258
  async function abort() {
10259
+ isAborted = true;
10247
10260
  await notify({
10248
10261
  event: {
10249
10262
  callId,
@@ -10276,6 +10289,7 @@ var DefaultStreamTextResult = class {
10276
10289
  if (isAbortError(error) && (abortSignal == null ? void 0 : abortSignal.aborted)) {
10277
10290
  await abort();
10278
10291
  } else {
10292
+ await ((_a25 = telemetryDispatcher.onError) == null ? void 0 : _a25.call(telemetryDispatcher, { callId, error }));
10279
10293
  controller.error(error);
10280
10294
  }
10281
10295
  }
@@ -10616,6 +10630,7 @@ var DefaultStreamTextResult = class {
10616
10630
  const stepModel = resolveLanguageModel(
10617
10631
  (_e = prepareStepResult == null ? void 0 : prepareStepResult.model) != null ? _e : model
10618
10632
  );
10633
+ currentStepModel = stepModel;
10619
10634
  const stepActiveTools = filterActiveTools({
10620
10635
  tools,
10621
10636
  activeTools: (_f = prepareStepResult == null ? void 0 : prepareStepResult.activeTools) != null ? _f : activeTools
@@ -10928,7 +10943,7 @@ var DefaultStreamTextResult = class {
10928
10943
  const createStepResponse = () => ({
10929
10944
  id: generateId5(),
10930
10945
  timestamp: /* @__PURE__ */ new Date(),
10931
- modelId: model.modelId
10946
+ modelId: stepModel.modelId
10932
10947
  });
10933
10948
  let stepResponse = createStepResponse();
10934
10949
  const textPartIds = /* @__PURE__ */ new Map();
@@ -11399,7 +11414,7 @@ var DefaultStreamTextResult = class {
11399
11414
  this.teeStream().pipeThrough(
11400
11415
  new TransformStream({
11401
11416
  transform({ partialOutput }, controller) {
11402
- if (partialOutput != null) {
11417
+ if (partialOutput !== void 0) {
11403
11418
  controller.enqueue(partialOutput);
11404
11419
  }
11405
11420
  }
@@ -11889,9 +11904,15 @@ function readUIMessageStream({
11889
11904
  var _a25;
11890
11905
  let controller;
11891
11906
  let hasErrored = false;
11907
+ let isCancelled = false;
11908
+ const abortController = new AbortController();
11892
11909
  const outputStream = new ReadableStream({
11893
11910
  start(controllerParam) {
11894
11911
  controller = controllerParam;
11912
+ },
11913
+ cancel() {
11914
+ isCancelled = true;
11915
+ abortController.abort();
11895
11916
  }
11896
11917
  });
11897
11918
  const state = createStreamingUIMessageState({
@@ -11912,15 +11933,18 @@ function readUIMessageStream({
11912
11933
  return job({
11913
11934
  state,
11914
11935
  write: () => {
11915
- controller == null ? void 0 : controller.enqueue(createUIMessageSnapshot(state.message));
11936
+ if (!isCancelled) {
11937
+ controller == null ? void 0 : controller.enqueue(createUIMessageSnapshot(state.message));
11938
+ }
11916
11939
  }
11917
11940
  });
11918
11941
  },
11919
11942
  onError: handleError
11920
11943
  }),
11921
- onError: handleError
11944
+ onError: handleError,
11945
+ abortSignal: abortController.signal
11922
11946
  }).finally(() => {
11923
- if (!hasErrored) {
11947
+ if (!hasErrored && !isCancelled) {
11924
11948
  controller == null ? void 0 : controller.close();
11925
11949
  }
11926
11950
  });
@@ -13322,6 +13346,7 @@ async function startBatch({
13322
13346
  prompt: standardizedPrompt,
13323
13347
  supportedUrls,
13324
13348
  download: void 0,
13349
+ abortSignal: operationAbortSignal,
13325
13350
  provider: batchApi.provider.split(".")[0]
13326
13351
  }),
13327
13352
  tools: preparedTools,
@@ -15127,6 +15152,7 @@ async function generateObject(options) {
15127
15152
  prompt: standardizedPrompt,
15128
15153
  supportedUrls: await model.supportedUrls,
15129
15154
  download: download2,
15155
+ abortSignal,
15130
15156
  provider: model.provider.split(".")[0]
15131
15157
  });
15132
15158
  await notify({
@@ -15575,6 +15601,7 @@ var DefaultStreamObjectResult = class {
15575
15601
  prompt: standardizedPrompt,
15576
15602
  supportedUrls: await model.supportedUrls,
15577
15603
  download: download2,
15604
+ abortSignal,
15578
15605
  provider: model.provider.split(".")[0]
15579
15606
  }),
15580
15607
  providerOptions,
@@ -16099,11 +16126,13 @@ function pruneMessages({
16099
16126
  }
16100
16127
  }
16101
16128
  }
16129
+ const approvalIdToToolCallId = /* @__PURE__ */ new Map();
16102
16130
  const approvalIdToToolName = /* @__PURE__ */ new Map();
16103
16131
  for (const message of messages) {
16104
16132
  if ((message.role === "assistant" || message.role === "tool") && typeof message.content !== "string") {
16105
16133
  for (const part of message.content) {
16106
16134
  if (part.type === "tool-approval-request") {
16135
+ approvalIdToToolCallId.set(part.approvalId, part.toolCallId);
16107
16136
  const toolName = toolCallIdToToolName.get(part.toolCallId);
16108
16137
  if (toolName != null) {
16109
16138
  approvalIdToToolName.set(part.approvalId, toolName);
@@ -16112,6 +16141,12 @@ function pruneMessages({
16112
16141
  }
16113
16142
  }
16114
16143
  }
16144
+ for (const approvalId of keptApprovalIds) {
16145
+ const toolCallId = approvalIdToToolCallId.get(approvalId);
16146
+ if (toolCallId != null) {
16147
+ keptToolCallIds.add(toolCallId);
16148
+ }
16149
+ }
16115
16150
  messages = messages.map((message, messageIndex) => {
16116
16151
  if (message.role !== "assistant" && message.role !== "tool" || typeof message.content === "string" || keepLastMessagesCount && messageIndex >= messages.length - keepLastMessagesCount) {
16117
16152
  return message;
@@ -16895,6 +16930,23 @@ function defaultTransform(text2) {
16895
16930
  function stripMarkdownCodeFenceSuffix(text2) {
16896
16931
  return text2.replace(/\n?```\s*$/, "").trimEnd();
16897
16932
  }
16933
+ function getPotentialSuffixStart(text2) {
16934
+ let index = text2.length;
16935
+ while (index > 0 && /\s/.test(text2[index - 1])) {
16936
+ index--;
16937
+ }
16938
+ let backtickCount = 0;
16939
+ while (index > 0 && backtickCount < 3 && text2[index - 1] === "`") {
16940
+ index--;
16941
+ backtickCount++;
16942
+ }
16943
+ if (backtickCount > 0) {
16944
+ while (index > 0 && /\s/.test(text2[index - 1])) {
16945
+ index--;
16946
+ }
16947
+ }
16948
+ return index;
16949
+ }
16898
16950
  function extractJsonMiddleware(options) {
16899
16951
  var _a25;
16900
16952
  const transform = (_a25 = options == null ? void 0 : options.transform) != null ? _a25 : defaultTransform;
@@ -16919,7 +16971,6 @@ function extractJsonMiddleware(options) {
16919
16971
  wrapStream: async ({ doStream }) => {
16920
16972
  const { stream, ...rest } = await doStream();
16921
16973
  const textBlocks = createIdMap();
16922
- const SUFFIX_BUFFER_SIZE = 12;
16923
16974
  return {
16924
16975
  stream: stream.pipeThrough(
16925
16976
  new TransformStream({
@@ -16968,9 +17019,15 @@ function extractJsonMiddleware(options) {
16968
17019
  controller.enqueue(block.startEvent);
16969
17020
  }
16970
17021
  }
16971
- if (block.phase === "streaming" && block.buffer.length > SUFFIX_BUFFER_SIZE) {
16972
- const toStream = block.buffer.slice(0, -SUFFIX_BUFFER_SIZE);
16973
- block.buffer = block.buffer.slice(-SUFFIX_BUFFER_SIZE);
17022
+ if (block.phase === "streaming") {
17023
+ const potentialSuffixStart = getPotentialSuffixStart(
17024
+ block.buffer
17025
+ );
17026
+ const toStream = block.buffer.slice(0, potentialSuffixStart);
17027
+ block.buffer = block.buffer.slice(potentialSuffixStart);
17028
+ if (toStream.length === 0) {
17029
+ return;
17030
+ }
16974
17031
  controller.enqueue({
16975
17032
  type: "text-delta",
16976
17033
  id: chunk.id,
@@ -17211,7 +17268,11 @@ function simulateStreamingMiddleware() {
17211
17268
  switch (part.type) {
17212
17269
  case "text": {
17213
17270
  if (part.text.length > 0) {
17214
- controller.enqueue({ type: "text-start", id: String(id) });
17271
+ controller.enqueue({
17272
+ type: "text-start",
17273
+ id: String(id),
17274
+ ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
17275
+ });
17215
17276
  controller.enqueue({
17216
17277
  type: "text-delta",
17217
17278
  id: String(id),