ai 6.0.284 → 6.0.286

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.
@@ -665,7 +665,16 @@ function createOutputTransformStream<
665
665
  let text = '';
666
666
  let textChunk = '';
667
667
  let textProviderMetadata: ProviderMetadata | undefined = undefined;
668
- let lastPublishedValue = '';
668
+ let lastPublishedValue: string | undefined = undefined;
669
+ let hasPublishedValue = false;
670
+
671
+ function resetOutputState() {
672
+ firstTextChunkId = undefined;
673
+ text = '';
674
+ textChunk = '';
675
+ textProviderMetadata = undefined;
676
+ lastPublishedValue = '';
677
+ }
669
678
 
670
679
  function publishTextChunk({
671
680
  controller,
@@ -693,6 +702,10 @@ function createOutputTransformStream<
693
702
  EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
694
703
  >({
695
704
  async transform(chunk, controller) {
705
+ if (chunk.type === 'start-step') {
706
+ resetOutputState();
707
+ }
708
+
696
709
  // ensure that we publish the last text chunk before the step finish:
697
710
  if (chunk.type === 'finish-step' && textChunk.length > 0) {
698
711
  publishTextChunk({ controller });
@@ -749,9 +762,10 @@ function createOutputTransformStream<
749
762
  typeof result.partial === 'string'
750
763
  ? result.partial
751
764
  : JSON.stringify(result.partial);
752
- if (currentValue !== lastPublishedValue) {
765
+ if (!hasPublishedValue || currentValue !== lastPublishedValue) {
753
766
  publishTextChunk({ controller, partialOutput: result.partial });
754
767
  lastPublishedValue = currentValue;
768
+ hasPublishedValue = true;
755
769
  }
756
770
  }
757
771
  },
@@ -916,6 +930,7 @@ class DefaultStreamTextResult<
916
930
  const recordedSteps: StepResult<TOOLS>[] = [];
917
931
  let recordedNoOutputError: NoOutputGeneratedError | undefined;
918
932
  let currentStepToolSet = tools;
933
+ let currentStepModel = model;
919
934
 
920
935
  // provider-assigned text/reasoning part IDs are only unique within a
921
936
  // single model call (e.g. Anthropic uses the content block index, which
@@ -1156,7 +1171,10 @@ class DefaultStreamTextResult<
1156
1171
  // Add step information (after response messages are updated):
1157
1172
  const currentStepResult: StepResult<TOOLS> = new DefaultStepResult({
1158
1173
  stepNumber: recordedSteps.length,
1159
- model: modelInfo,
1174
+ model: {
1175
+ provider: currentStepModel.provider,
1176
+ modelId: currentStepModel.modelId,
1177
+ },
1160
1178
  ...callbackTelemetryProps,
1161
1179
  experimental_context,
1162
1180
  content: recordedContent,
@@ -1179,8 +1197,8 @@ class DefaultStreamTextResult<
1179
1197
 
1180
1198
  logWarnings({
1181
1199
  warnings: recordedWarnings,
1182
- provider: modelInfo.provider,
1183
- model: modelInfo.modelId,
1200
+ provider: currentStepModel.provider,
1201
+ model: currentStepModel.modelId,
1184
1202
  });
1185
1203
 
1186
1204
  recordedSteps.push(currentStepResult);
@@ -1763,6 +1781,7 @@ class DefaultStreamTextResult<
1763
1781
  const stepModel = resolveLanguageModel(
1764
1782
  prepareStepResult?.model ?? model,
1765
1783
  );
1784
+ currentStepModel = stepModel;
1766
1785
  const stepModelInfo = {
1767
1786
  provider: stepModel.provider,
1768
1787
  modelId: stepModel.modelId,
@@ -1775,6 +1794,7 @@ class DefaultStreamTextResult<
1775
1794
  },
1776
1795
  supportedUrls: await stepModel.supportedUrls,
1777
1796
  download,
1797
+ abortSignal,
1778
1798
  });
1779
1799
 
1780
1800
  const stepActiveTools =
@@ -1965,7 +1985,7 @@ class DefaultStreamTextResult<
1965
1985
  {
1966
1986
  id: generateId(),
1967
1987
  timestamp: new Date(),
1968
- modelId: modelInfo.modelId,
1988
+ modelId: stepModelInfo.modelId,
1969
1989
  };
1970
1990
 
1971
1991
  // raw text as it comes from the provider. recorded for telemetry.
@@ -2652,7 +2672,7 @@ class DefaultStreamTextResult<
2652
2672
  InferPartialOutput<OUTPUT>
2653
2673
  >({
2654
2674
  transform({ partialOutput }, controller) {
2655
- if (partialOutput != null) {
2675
+ if (partialOutput !== undefined) {
2656
2676
  controller.enqueue(partialOutput);
2657
2677
  }
2658
2678
  },
@@ -25,7 +25,13 @@ export function simulateStreamingMiddleware(): LanguageModelMiddleware {
25
25
  switch (part.type) {
26
26
  case 'text': {
27
27
  if (part.text.length > 0) {
28
- controller.enqueue({ type: 'text-start', id: String(id) });
28
+ controller.enqueue({
29
+ type: 'text-start',
30
+ id: String(id),
31
+ ...(part.providerMetadata != null
32
+ ? { providerMetadata: part.providerMetadata }
33
+ : {}),
34
+ });
29
35
  controller.enqueue({
30
36
  type: 'text-delta',
31
37
  id: String(id),
@@ -38,15 +38,17 @@ import { MissingToolResultsError } from '../error/missing-tool-result-error';
38
38
  export async function convertToLanguageModelPrompt({
39
39
  prompt,
40
40
  supportedUrls,
41
- download = createDefaultDownloadFunction(),
41
+ download,
42
+ abortSignal,
42
43
  }: {
43
44
  prompt: StandardizedPrompt;
44
45
  supportedUrls: Record<string, RegExp[]>;
45
46
  download: DownloadFunction | undefined;
47
+ abortSignal?: AbortSignal;
46
48
  }): Promise<LanguageModelV3Prompt> {
47
49
  const downloadedAssets = await downloadAssets(
48
50
  prompt.messages,
49
- download,
51
+ download ?? createDefaultDownloadFunction(undefined, abortSignal),
50
52
  supportedUrls,
51
53
  );
52
54
 
@@ -65,11 +65,17 @@ export function readUIMessageStream<UI_MESSAGE extends UIMessage>({
65
65
  }): AsyncIterableStream<UI_MESSAGE> {
66
66
  let controller: ReadableStreamDefaultController<UI_MESSAGE> | undefined;
67
67
  let hasErrored = false;
68
+ let isCancelled = false;
69
+ const abortController = new AbortController();
68
70
 
69
71
  const outputStream = new ReadableStream<UI_MESSAGE>({
70
72
  start(controllerParam) {
71
73
  controller = controllerParam;
72
74
  },
75
+ cancel() {
76
+ isCancelled = true;
77
+ abortController.abort();
78
+ },
73
79
  });
74
80
 
75
81
  const state = createStreamingUIMessageState<UI_MESSAGE>({
@@ -98,17 +104,20 @@ export function readUIMessageStream<UI_MESSAGE extends UIMessage>({
98
104
  return job({
99
105
  state,
100
106
  write: () => {
101
- controller?.enqueue(createUIMessageSnapshot(state.message));
107
+ if (!isCancelled) {
108
+ controller?.enqueue(createUIMessageSnapshot(state.message));
109
+ }
102
110
  },
103
111
  });
104
112
  },
105
113
  onError: handleError,
106
114
  }),
107
115
  onError: handleError,
116
+ abortSignal: abortController.signal,
108
117
  }).finally(() => {
109
- // Only close if no error occurred. Calling close() on an errored controller
110
- // throws "Invalid state: Controller is already closed" TypeError.
111
- if (!hasErrored) {
118
+ // Only close if no error or cancellation occurred. Both erroring and
119
+ // cancelling a stream close its controller.
120
+ if (!hasErrored && !isCancelled) {
112
121
  controller?.close();
113
122
  }
114
123
  });
@@ -34,12 +34,15 @@ export type DownloadFunction = (
34
34
  * Downloads the file if it is not supported by the model.
35
35
  */
36
36
  export const createDefaultDownloadFunction =
37
- (download: typeof originalDownload = originalDownload): DownloadFunction =>
37
+ (
38
+ download: typeof originalDownload = originalDownload,
39
+ abortSignal?: AbortSignal,
40
+ ): DownloadFunction =>
38
41
  requestedDownloads =>
39
42
  Promise.all(
40
43
  requestedDownloads.map(async requestedDownload =>
41
44
  requestedDownload.isUrlSupportedByModel
42
45
  ? null
43
- : download(requestedDownload),
46
+ : await download({ ...requestedDownload, abortSignal }),
44
47
  ),
45
48
  );