ai 7.0.104 → 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,36 @@
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
+
25
+ ## 7.0.105
26
+
27
+ ### Patch Changes
28
+
29
+ - 6982e9d: Resolve evaluation model IDs through AI Gateway when no default provider is configured, including string aliases in custom providers.
30
+ - Updated dependencies [6982e9d]
31
+ - Updated dependencies [6982e9d]
32
+ - @ai-sdk/gateway@4.0.85
33
+
3
34
  ## 7.0.104
4
35
 
5
36
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1129,8 +1129,9 @@ function resolveRerankingModel(model) {
1129
1129
  return asRerankingModelV4(model);
1130
1130
  }
1131
1131
  function resolveEvaluationModel(model) {
1132
+ var _a25;
1132
1133
  if (typeof model === "string") {
1133
- const provider = globalThis.AI_SDK_DEFAULT_PROVIDER;
1134
+ const provider = (_a25 = globalThis.AI_SDK_DEFAULT_PROVIDER) != null ? _a25 : gateway;
1134
1135
  if (typeof (provider == null ? void 0 : provider.evaluationModel) !== "function") {
1135
1136
  throw new NoSuchModelError2({
1136
1137
  modelId: model,
@@ -1210,7 +1211,7 @@ import {
1210
1211
  } from "@ai-sdk/provider-utils";
1211
1212
 
1212
1213
  // src/version.ts
1213
- var VERSION = true ? "7.0.104" : "0.0.0-test";
1214
+ var VERSION = true ? "7.0.106" : "0.0.0-test";
1214
1215
 
1215
1216
  // src/util/download/download.ts
1216
1217
  var download = async ({
@@ -1257,9 +1258,9 @@ var download = async ({
1257
1258
  };
1258
1259
 
1259
1260
  // src/util/download/download-function.ts
1260
- var createDefaultDownloadFunction = (download2 = download) => (requestedDownloads) => Promise.all(
1261
+ var createDefaultDownloadFunction = (download2 = download, abortSignal) => (requestedDownloads) => Promise.all(
1261
1262
  requestedDownloads.map(
1262
- async (requestedDownload) => requestedDownload.isUrlSupportedByModel ? null : await download2(requestedDownload)
1263
+ async (requestedDownload) => requestedDownload.isUrlSupportedByModel ? null : await download2({ ...requestedDownload, abortSignal })
1263
1264
  )
1264
1265
  );
1265
1266
 
@@ -1408,14 +1409,15 @@ function convertToLanguageModelV4FilePart(content) {
1408
1409
  async function convertToLanguageModelPrompt({
1409
1410
  prompt,
1410
1411
  supportedUrls,
1411
- download: download2 = createDefaultDownloadFunction(),
1412
+ download: download2,
1413
+ abortSignal,
1412
1414
  // `provider` is only needed here to convert legacy tool output types via `mapToolResultOutput`.
1413
1415
  // TODO: remove in v8 when "file-id" and "image-file-id" types are removed
1414
1416
  provider
1415
1417
  }) {
1416
1418
  const downloadedAssets = await downloadAssets(
1417
1419
  prompt.messages,
1418
- download2,
1420
+ download2 != null ? download2 : createDefaultDownloadFunction(void 0, abortSignal),
1419
1421
  supportedUrls
1420
1422
  );
1421
1423
  const approvalIdToToolCallId = /* @__PURE__ */ new Map();
@@ -2039,9 +2041,6 @@ function getMediaTypeFromUrl(url, fallbackMediaType = "application/octet-stream"
2039
2041
 
2040
2042
  // src/prompt/create-tool-model-output.ts
2041
2043
  import { getErrorMessage as getErrorMessage3 } from "@ai-sdk/provider";
2042
- import {
2043
- parseJSON
2044
- } from "@ai-sdk/provider-utils";
2045
2044
  async function createToolModelOutput({
2046
2045
  toolCallId,
2047
2046
  input,
@@ -2052,19 +2051,19 @@ async function createToolModelOutput({
2052
2051
  if (errorMode === "text") {
2053
2052
  return { type: "error-text", value: getErrorMessage3(output) };
2054
2053
  } else if (errorMode === "json") {
2055
- return { type: "error-json", value: await toJSONValue(output) };
2054
+ return { type: "error-json", value: toJSONValue(output) };
2056
2055
  }
2057
2056
  if (tool3 == null ? void 0 : tool3.toModelOutput) {
2058
2057
  return await tool3.toModelOutput({ toolCallId, input, output });
2059
2058
  }
2060
- 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) };
2061
2060
  }
2062
- async function toJSONValue(value) {
2061
+ function toJSONValue(value) {
2063
2062
  if (value === void 0) {
2064
2063
  return null;
2065
2064
  }
2066
2065
  const serialized = JSON.stringify(value);
2067
- return serialized === void 0 ? null : await parseJSON({ text: serialized });
2066
+ return serialized === void 0 ? null : JSON.parse(serialized);
2068
2067
  }
2069
2068
 
2070
2069
  // src/prompt/prepare-language-model-call-options.ts
@@ -6098,6 +6097,7 @@ async function generateText({
6098
6097
  },
6099
6098
  supportedUrls: await stepModel.supportedUrls,
6100
6099
  download: download2,
6100
+ abortSignal: mergedAbortSignal,
6101
6101
  provider: stepModel.provider.split(".")[0]
6102
6102
  });
6103
6103
  const stepProviderOptions = mergeObjects(
@@ -8839,7 +8839,7 @@ function invokeToolCallbacksFromStream({
8839
8839
  const toolName = ongoingToolCallToolNames[chunk.toolCallId];
8840
8840
  const tool3 = getOwn(tools, toolName);
8841
8841
  delete ongoingToolCallToolNames[chunk.toolCallId];
8842
- if ((tool3 == null ? void 0 : tool3.onInputAvailable) != null) {
8842
+ if (!chunk.invalid && (tool3 == null ? void 0 : tool3.onInputAvailable) != null) {
8843
8843
  await tool3.onInputAvailable({
8844
8844
  input: chunk.input,
8845
8845
  toolCallId: chunk.toolCallId,
@@ -8989,6 +8989,7 @@ async function streamLanguageModelCall({
8989
8989
  },
8990
8990
  supportedUrls: await resolvedModel.supportedUrls,
8991
8991
  download: download2,
8992
+ abortSignal,
8992
8993
  provider: resolvedModel.provider.split(".")[0]
8993
8994
  });
8994
8995
  const stepTools = await prepareTools({
@@ -9679,13 +9680,15 @@ function createOutputTransformStream(output) {
9679
9680
  let text2 = "";
9680
9681
  let textChunk = "";
9681
9682
  let textProviderMetadata = void 0;
9682
- let lastPublishedValue = "";
9683
- function resetAttemptState() {
9683
+ let lastPublishedValue = void 0;
9684
+ let hasPublishedValue = false;
9685
+ function resetOutputState() {
9684
9686
  firstTextChunkId = void 0;
9685
9687
  text2 = "";
9686
9688
  textChunk = "";
9687
9689
  textProviderMetadata = void 0;
9688
- lastPublishedValue = "";
9690
+ lastPublishedValue = void 0;
9691
+ hasPublishedValue = false;
9689
9692
  }
9690
9693
  function enqueueChunk({
9691
9694
  controller,
@@ -9715,10 +9718,13 @@ function createOutputTransformStream(output) {
9715
9718
  async transform(chunk, controller) {
9716
9719
  var _a25;
9717
9720
  if (isStreamRetryBoundaryPart(chunk)) {
9718
- resetAttemptState();
9721
+ resetOutputState();
9719
9722
  controller.enqueue(chunk);
9720
9723
  return;
9721
9724
  }
9725
+ if (chunk.type === "start-step") {
9726
+ resetOutputState();
9727
+ }
9722
9728
  if (chunk.type === "finish-step" && textChunk.length > 0) {
9723
9729
  publishTextChunk({ controller });
9724
9730
  }
@@ -9768,9 +9774,10 @@ function createOutputTransformStream(output) {
9768
9774
  const result = await output.parsePartialOutput({ text: text2 });
9769
9775
  if (result !== void 0) {
9770
9776
  const currentValue = typeof result.partial === "string" ? result.partial : JSON.stringify(result.partial);
9771
- if (currentValue !== lastPublishedValue) {
9777
+ if (!hasPublishedValue || currentValue !== lastPublishedValue) {
9772
9778
  publishTextChunk({ controller, partialOutput: result.partial });
9773
9779
  lastPublishedValue = currentValue;
9780
+ hasPublishedValue = true;
9774
9781
  }
9775
9782
  }
9776
9783
  }
@@ -9933,6 +9940,8 @@ var DefaultStreamTextResult = class {
9933
9940
  const initialResponseMessages = [];
9934
9941
  let stepMessagesForNextStep;
9935
9942
  let currentStepMessages = [];
9943
+ let isAborted = false;
9944
+ let currentStepModel = model;
9936
9945
  const createPartIdReserver = () => {
9937
9946
  const usedIds = /* @__PURE__ */ new Set();
9938
9947
  return (id) => {
@@ -10099,8 +10108,8 @@ var DefaultStreamTextResult = class {
10099
10108
  const currentStepResult = new DefaultStepResult({
10100
10109
  callId,
10101
10110
  stepNumber: recordedSteps.length,
10102
- provider: model.provider,
10103
- modelId: model.modelId,
10111
+ provider: currentStepModel.provider,
10112
+ modelId: currentStepModel.modelId,
10104
10113
  runtimeContext,
10105
10114
  toolsContext,
10106
10115
  content: recordedContent,
@@ -10125,8 +10134,8 @@ var DefaultStreamTextResult = class {
10125
10134
  });
10126
10135
  logWarnings({
10127
10136
  warnings: recordedWarnings,
10128
- provider: model.provider,
10129
- model: model.modelId
10137
+ provider: currentStepModel.provider,
10138
+ model: currentStepModel.modelId
10130
10139
  });
10131
10140
  recordedSteps.push(currentStepResult);
10132
10141
  stepMessagesForNextStep = [
@@ -10156,6 +10165,9 @@ var DefaultStreamTextResult = class {
10156
10165
  self._rawFinishReason.resolve(recordedRawFinishReason);
10157
10166
  self._totalUsage.resolve(totalUsage);
10158
10167
  self._steps.resolve(recordedSteps);
10168
+ if (isAborted) {
10169
+ return;
10170
+ }
10159
10171
  const finalStep = recordedSteps[recordedSteps.length - 1];
10160
10172
  const content = recordedSteps.flatMap((step) => step.content);
10161
10173
  const files = recordedSteps.flatMap((step) => step.files);
@@ -10242,7 +10254,9 @@ var DefaultStreamTextResult = class {
10242
10254
  controller.enqueue({ type: "start" });
10243
10255
  },
10244
10256
  async pull(controller) {
10257
+ var _a25;
10245
10258
  async function abort() {
10259
+ isAborted = true;
10246
10260
  await notify({
10247
10261
  event: {
10248
10262
  callId,
@@ -10275,6 +10289,7 @@ var DefaultStreamTextResult = class {
10275
10289
  if (isAbortError(error) && (abortSignal == null ? void 0 : abortSignal.aborted)) {
10276
10290
  await abort();
10277
10291
  } else {
10292
+ await ((_a25 = telemetryDispatcher.onError) == null ? void 0 : _a25.call(telemetryDispatcher, { callId, error }));
10278
10293
  controller.error(error);
10279
10294
  }
10280
10295
  }
@@ -10615,6 +10630,7 @@ var DefaultStreamTextResult = class {
10615
10630
  const stepModel = resolveLanguageModel(
10616
10631
  (_e = prepareStepResult == null ? void 0 : prepareStepResult.model) != null ? _e : model
10617
10632
  );
10633
+ currentStepModel = stepModel;
10618
10634
  const stepActiveTools = filterActiveTools({
10619
10635
  tools,
10620
10636
  activeTools: (_f = prepareStepResult == null ? void 0 : prepareStepResult.activeTools) != null ? _f : activeTools
@@ -10927,7 +10943,7 @@ var DefaultStreamTextResult = class {
10927
10943
  const createStepResponse = () => ({
10928
10944
  id: generateId5(),
10929
10945
  timestamp: /* @__PURE__ */ new Date(),
10930
- modelId: model.modelId
10946
+ modelId: stepModel.modelId
10931
10947
  });
10932
10948
  let stepResponse = createStepResponse();
10933
10949
  const textPartIds = /* @__PURE__ */ new Map();
@@ -11398,7 +11414,7 @@ var DefaultStreamTextResult = class {
11398
11414
  this.teeStream().pipeThrough(
11399
11415
  new TransformStream({
11400
11416
  transform({ partialOutput }, controller) {
11401
- if (partialOutput != null) {
11417
+ if (partialOutput !== void 0) {
11402
11418
  controller.enqueue(partialOutput);
11403
11419
  }
11404
11420
  }
@@ -11888,9 +11904,15 @@ function readUIMessageStream({
11888
11904
  var _a25;
11889
11905
  let controller;
11890
11906
  let hasErrored = false;
11907
+ let isCancelled = false;
11908
+ const abortController = new AbortController();
11891
11909
  const outputStream = new ReadableStream({
11892
11910
  start(controllerParam) {
11893
11911
  controller = controllerParam;
11912
+ },
11913
+ cancel() {
11914
+ isCancelled = true;
11915
+ abortController.abort();
11894
11916
  }
11895
11917
  });
11896
11918
  const state = createStreamingUIMessageState({
@@ -11911,15 +11933,18 @@ function readUIMessageStream({
11911
11933
  return job({
11912
11934
  state,
11913
11935
  write: () => {
11914
- controller == null ? void 0 : controller.enqueue(createUIMessageSnapshot(state.message));
11936
+ if (!isCancelled) {
11937
+ controller == null ? void 0 : controller.enqueue(createUIMessageSnapshot(state.message));
11938
+ }
11915
11939
  }
11916
11940
  });
11917
11941
  },
11918
11942
  onError: handleError
11919
11943
  }),
11920
- onError: handleError
11944
+ onError: handleError,
11945
+ abortSignal: abortController.signal
11921
11946
  }).finally(() => {
11922
- if (!hasErrored) {
11947
+ if (!hasErrored && !isCancelled) {
11923
11948
  controller == null ? void 0 : controller.close();
11924
11949
  }
11925
11950
  });
@@ -13321,6 +13346,7 @@ async function startBatch({
13321
13346
  prompt: standardizedPrompt,
13322
13347
  supportedUrls,
13323
13348
  download: void 0,
13349
+ abortSignal: operationAbortSignal,
13324
13350
  provider: batchApi.provider.split(".")[0]
13325
13351
  }),
13326
13352
  tools: preparedTools,
@@ -15126,6 +15152,7 @@ async function generateObject(options) {
15126
15152
  prompt: standardizedPrompt,
15127
15153
  supportedUrls: await model.supportedUrls,
15128
15154
  download: download2,
15155
+ abortSignal,
15129
15156
  provider: model.provider.split(".")[0]
15130
15157
  });
15131
15158
  await notify({
@@ -15574,6 +15601,7 @@ var DefaultStreamObjectResult = class {
15574
15601
  prompt: standardizedPrompt,
15575
15602
  supportedUrls: await model.supportedUrls,
15576
15603
  download: download2,
15604
+ abortSignal,
15577
15605
  provider: model.provider.split(".")[0]
15578
15606
  }),
15579
15607
  providerOptions,
@@ -16098,11 +16126,13 @@ function pruneMessages({
16098
16126
  }
16099
16127
  }
16100
16128
  }
16129
+ const approvalIdToToolCallId = /* @__PURE__ */ new Map();
16101
16130
  const approvalIdToToolName = /* @__PURE__ */ new Map();
16102
16131
  for (const message of messages) {
16103
16132
  if ((message.role === "assistant" || message.role === "tool") && typeof message.content !== "string") {
16104
16133
  for (const part of message.content) {
16105
16134
  if (part.type === "tool-approval-request") {
16135
+ approvalIdToToolCallId.set(part.approvalId, part.toolCallId);
16106
16136
  const toolName = toolCallIdToToolName.get(part.toolCallId);
16107
16137
  if (toolName != null) {
16108
16138
  approvalIdToToolName.set(part.approvalId, toolName);
@@ -16111,6 +16141,12 @@ function pruneMessages({
16111
16141
  }
16112
16142
  }
16113
16143
  }
16144
+ for (const approvalId of keptApprovalIds) {
16145
+ const toolCallId = approvalIdToToolCallId.get(approvalId);
16146
+ if (toolCallId != null) {
16147
+ keptToolCallIds.add(toolCallId);
16148
+ }
16149
+ }
16114
16150
  messages = messages.map((message, messageIndex) => {
16115
16151
  if (message.role !== "assistant" && message.role !== "tool" || typeof message.content === "string" || keepLastMessagesCount && messageIndex >= messages.length - keepLastMessagesCount) {
16116
16152
  return message;
@@ -16894,6 +16930,23 @@ function defaultTransform(text2) {
16894
16930
  function stripMarkdownCodeFenceSuffix(text2) {
16895
16931
  return text2.replace(/\n?```\s*$/, "").trimEnd();
16896
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
+ }
16897
16950
  function extractJsonMiddleware(options) {
16898
16951
  var _a25;
16899
16952
  const transform = (_a25 = options == null ? void 0 : options.transform) != null ? _a25 : defaultTransform;
@@ -16918,7 +16971,6 @@ function extractJsonMiddleware(options) {
16918
16971
  wrapStream: async ({ doStream }) => {
16919
16972
  const { stream, ...rest } = await doStream();
16920
16973
  const textBlocks = createIdMap();
16921
- const SUFFIX_BUFFER_SIZE = 12;
16922
16974
  return {
16923
16975
  stream: stream.pipeThrough(
16924
16976
  new TransformStream({
@@ -16967,9 +17019,15 @@ function extractJsonMiddleware(options) {
16967
17019
  controller.enqueue(block.startEvent);
16968
17020
  }
16969
17021
  }
16970
- if (block.phase === "streaming" && block.buffer.length > SUFFIX_BUFFER_SIZE) {
16971
- const toStream = block.buffer.slice(0, -SUFFIX_BUFFER_SIZE);
16972
- 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
+ }
16973
17031
  controller.enqueue({
16974
17032
  type: "text-delta",
16975
17033
  id: chunk.id,
@@ -17210,7 +17268,11 @@ function simulateStreamingMiddleware() {
17210
17268
  switch (part.type) {
17211
17269
  case "text": {
17212
17270
  if (part.text.length > 0) {
17213
- 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
+ });
17214
17276
  controller.enqueue({
17215
17277
  type: "text-delta",
17216
17278
  id: String(id),