@ai-sdk/workflow 1.0.66 → 1.0.68

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,19 @@
1
1
  # @ai-sdk/workflow
2
2
 
3
+ ## 1.0.68
4
+
5
+ ### Patch Changes
6
+
7
+ - e6064c5: Honor `WorkflowAgent` model-call retry settings without stacking workflow step retries.
8
+ - 83f9b12: Expose the original value from model stream error parts on resolved WorkflowAgent results without retrying the durable model step.
9
+ - ai@7.0.67
10
+
11
+ ## 1.0.67
12
+
13
+ ### Patch Changes
14
+
15
+ - e3325bd: Fix `WorkflowAgent` timeout handling by enforcing absolute deadlines inside durable model-call steps and routing timeouts through abort handling.
16
+
3
17
  ## 1.0.66
4
18
 
5
19
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -137,8 +137,7 @@ interface GenerationSettings {
137
137
  */
138
138
  seed?: number;
139
139
  /**
140
- * Maximum number of retries. Set to 0 to disable retries.
141
- * Note: In workflow context, retries are typically handled by the workflow step mechanism.
140
+ * Maximum number of retries for retryable model call failures. Set to 0 to disable retries.
142
141
  * @default 2
143
142
  */
144
143
  maxRetries?: number;
@@ -911,6 +910,14 @@ interface WorkflowAgentStreamResult<TTools extends ToolSet = ToolSet, OUTPUT = n
911
910
  * The finish reason from the last step.
912
911
  */
913
912
  finishReason: FinishReason;
913
+ /**
914
+ * The original value from a model stream error part.
915
+ *
916
+ * This property is present when the model emitted an error part, including
917
+ * when the supplied value is `undefined`. Check with `'error' in result` to
918
+ * distinguish that case from a result without a model stream error.
919
+ */
920
+ error?: unknown;
914
921
  /**
915
922
  * The total token usage across all steps.
916
923
  */
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/workflow-agent.ts
2
2
  import {
3
3
  getErrorMessage,
4
+ isAbortError as isAbortError2,
4
5
  validateTypes,
5
6
  withUserAgentSuffix
6
7
  } from "@ai-sdk/provider-utils";
@@ -12,7 +13,6 @@ import {
12
13
  createRestrictedTelemetryDispatcher as createRestrictedTelemetryDispatcher2,
13
14
  collectToolApprovals,
14
15
  convertToLanguageModelPrompt,
15
- mergeAbortSignals,
16
16
  mergeCallbacks,
17
17
  standardizePrompt,
18
18
  validateApprovedToolApprovals
@@ -74,10 +74,12 @@ import {
74
74
  import { createRestrictedTelemetryDispatcher } from "ai/internal";
75
75
 
76
76
  // src/do-stream-step.ts
77
+ import { isAbortError } from "@ai-sdk/provider-utils";
77
78
  import {
78
79
  experimental_streamLanguageModelCall as streamModelCall,
79
80
  gateway
80
81
  } from "ai";
82
+ import { prepareRetries } from "ai/internal";
81
83
 
82
84
  // src/serializable-schema.ts
83
85
  import {
@@ -165,6 +167,12 @@ function resolveSerializableTools(tools) {
165
167
  // src/do-stream-step.ts
166
168
  async function doStreamStep(conversationPrompt, modelInit, writable, serializedTools, options) {
167
169
  "use step";
170
+ var _a;
171
+ const timeout = (options == null ? void 0 : options.timeoutAt) == null ? void 0 : options.timeoutAt - Date.now();
172
+ if (((_a = options == null ? void 0 : options.abortSignal) == null ? void 0 : _a.aborted) || timeout != null && timeout <= 0) {
173
+ return { aborted: true };
174
+ }
175
+ const abortSignal = timeout == null ? options == null ? void 0 : options.abortSignal : (options == null ? void 0 : options.abortSignal) == null ? AbortSignal.timeout(timeout) : AbortSignal.any([options.abortSignal, AbortSignal.timeout(timeout)]);
168
176
  const model = typeof modelInit === "string" ? gateway.languageModel(modelInit) : modelInit;
169
177
  const tools = serializedTools ? resolveSerializableTools(serializedTools) : void 0;
170
178
  const output = (options == null ? void 0 : options.responseFormat) == null ? void 0 : {
@@ -182,31 +190,50 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
182
190
  return void 0;
183
191
  }
184
192
  };
185
- const { stream: modelStream } = await streamModelCall({
186
- model,
187
- // streamModelCall expects Prompt (ModelMessage[]) but we pass the
188
- // pre-converted LanguageModelV4Prompt. standardizePrompt inside
189
- // streamModelCall handles both formats.
190
- messages: conversationPrompt,
191
- allowSystemInMessages: true,
192
- tools,
193
- toolChoice: options == null ? void 0 : options.toolChoice,
194
- includeRawChunks: options == null ? void 0 : options.includeRawChunks,
195
- providerOptions: options == null ? void 0 : options.providerOptions,
196
- abortSignal: options == null ? void 0 : options.abortSignal,
197
- headers: options == null ? void 0 : options.headers,
198
- reasoning: options == null ? void 0 : options.reasoning,
199
- output,
200
- maxOutputTokens: options == null ? void 0 : options.maxOutputTokens,
201
- temperature: options == null ? void 0 : options.temperature,
202
- topP: options == null ? void 0 : options.topP,
203
- topK: options == null ? void 0 : options.topK,
204
- presencePenalty: options == null ? void 0 : options.presencePenalty,
205
- frequencyPenalty: options == null ? void 0 : options.frequencyPenalty,
206
- stopSequences: options == null ? void 0 : options.stopSequences,
207
- seed: options == null ? void 0 : options.seed,
208
- repairToolCall: options == null ? void 0 : options.repairToolCall
193
+ const { retry } = prepareRetries({
194
+ maxRetries: options == null ? void 0 : options.maxRetries,
195
+ abortSignal
209
196
  });
197
+ const modelStream = await (async () => {
198
+ try {
199
+ const { stream } = await retry(
200
+ () => streamModelCall({
201
+ model,
202
+ // streamModelCall expects Prompt (ModelMessage[]) but we pass the
203
+ // pre-converted LanguageModelV4Prompt. standardizePrompt inside
204
+ // streamModelCall handles both formats.
205
+ messages: conversationPrompt,
206
+ allowSystemInMessages: true,
207
+ tools,
208
+ toolChoice: options == null ? void 0 : options.toolChoice,
209
+ includeRawChunks: options == null ? void 0 : options.includeRawChunks,
210
+ providerOptions: options == null ? void 0 : options.providerOptions,
211
+ abortSignal,
212
+ headers: options == null ? void 0 : options.headers,
213
+ reasoning: options == null ? void 0 : options.reasoning,
214
+ output,
215
+ maxOutputTokens: options == null ? void 0 : options.maxOutputTokens,
216
+ temperature: options == null ? void 0 : options.temperature,
217
+ topP: options == null ? void 0 : options.topP,
218
+ topK: options == null ? void 0 : options.topK,
219
+ presencePenalty: options == null ? void 0 : options.presencePenalty,
220
+ frequencyPenalty: options == null ? void 0 : options.frequencyPenalty,
221
+ stopSequences: options == null ? void 0 : options.stopSequences,
222
+ seed: options == null ? void 0 : options.seed,
223
+ repairToolCall: options == null ? void 0 : options.repairToolCall
224
+ })
225
+ );
226
+ return stream;
227
+ } catch (error) {
228
+ if ((abortSignal == null ? void 0 : abortSignal.aborted) && isAbortError(error)) {
229
+ return void 0;
230
+ }
231
+ throw error;
232
+ }
233
+ })();
234
+ if (modelStream == null) {
235
+ return { aborted: true };
236
+ }
210
237
  const toolCalls = [];
211
238
  const providerExecutedToolResults = /* @__PURE__ */ new Map();
212
239
  let finish;
@@ -214,6 +241,8 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
214
241
  const reasoningParts = [];
215
242
  let responseMetadata;
216
243
  let warnings;
244
+ let terminalError;
245
+ let hasTerminalError = false;
217
246
  const writer = writable == null ? void 0 : writable.getWriter();
218
247
  try {
219
248
  for await (const part of modelStream) {
@@ -279,10 +308,22 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
279
308
  if (writer) {
280
309
  await writer.write(part);
281
310
  }
311
+ if (part.type === "error" && !hasTerminalError) {
312
+ terminalError = part.error;
313
+ hasTerminalError = true;
314
+ }
315
+ }
316
+ } catch (error) {
317
+ if ((abortSignal == null ? void 0 : abortSignal.aborted) && isAbortError(error)) {
318
+ return { aborted: true };
282
319
  }
320
+ throw error;
283
321
  } finally {
284
322
  writer == null ? void 0 : writer.releaseLock();
285
323
  }
324
+ if ((abortSignal == null ? void 0 : abortSignal.aborted) || (options == null ? void 0 : options.timeoutAt) != null && options.timeoutAt <= Date.now()) {
325
+ return { aborted: true };
326
+ }
286
327
  return {
287
328
  toolCalls,
288
329
  finish,
@@ -292,9 +333,11 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
292
333
  responseMetadata,
293
334
  warnings
294
335
  },
295
- providerExecutedToolResults
336
+ providerExecutedToolResults,
337
+ ...hasTerminalError ? { terminalError } : {}
296
338
  };
297
339
  }
340
+ doStreamStep.maxRetries = 0;
298
341
 
299
342
  // src/stream-text-iterator.ts
300
343
  var prepareStepGenerationSettingKeys = [
@@ -339,6 +382,7 @@ async function* streamTextIterator({
339
382
  toolsContext,
340
383
  telemetry,
341
384
  includeRawChunks = false,
385
+ timeoutAt,
342
386
  repairToolCall,
343
387
  responseFormat,
344
388
  experimental_sandbox: sandbox
@@ -357,6 +401,9 @@ async function* streamTextIterator({
357
401
  let stepNumber = 0;
358
402
  let lastStep;
359
403
  let lastStepWasToolCalls = false;
404
+ let wasAborted = false;
405
+ let terminalError;
406
+ let hasTerminalError = false;
360
407
  const telemetryDispatcher = createRestrictedTelemetryDispatcher({
361
408
  telemetry,
362
409
  includeRuntimeContext: telemetry == null ? void 0 : telemetry.includeRuntimeContext,
@@ -472,7 +519,7 @@ async function* streamTextIterator({
472
519
  providerOptions: currentGenerationSettings.providerOptions,
473
520
  headers: currentGenerationSettings.headers
474
521
  }));
475
- const { toolCalls, finish, raw, providerExecutedToolResults } = await doStreamStep(
522
+ const streamStepResult = await doStreamStep(
476
523
  conversationPrompt,
477
524
  currentModel,
478
525
  writable,
@@ -481,10 +528,20 @@ async function* streamTextIterator({
481
528
  ...currentGenerationSettings,
482
529
  toolChoice: currentToolChoice,
483
530
  includeRawChunks,
531
+ timeoutAt,
484
532
  repairToolCall,
485
533
  responseFormat
486
534
  }
487
535
  );
536
+ if (streamStepResult.aborted) {
537
+ wasAborted = true;
538
+ break;
539
+ }
540
+ if ("terminalError" in streamStepResult) {
541
+ terminalError = streamStepResult.terminalError;
542
+ hasTerminalError = true;
543
+ }
544
+ const { toolCalls, finish, raw, providerExecutedToolResults } = streamStepResult;
488
545
  const step = buildStepResult(raw, toolCalls, finish, {
489
546
  stepNumber,
490
547
  runtimeContext: currentRuntimeContext,
@@ -506,7 +563,9 @@ async function* streamTextIterator({
506
563
  lastStep = step;
507
564
  lastStepWasToolCalls = false;
508
565
  const finishReason = finish == null ? void 0 : finish.finishReason;
509
- if (finishReason === "tool-calls") {
566
+ if (hasTerminalError) {
567
+ done = true;
568
+ } else if (finishReason === "tool-calls") {
510
569
  lastStepWasToolCalls = true;
511
570
  const textContent = step.content.filter(
512
571
  (item) => item.type === "text"
@@ -600,6 +659,12 @@ async function* streamTextIterator({
600
659
  experimental_sandbox: sandbox
601
660
  };
602
661
  }
662
+ if (wasAborted) {
663
+ return { aborted: true, messages: conversationPrompt };
664
+ }
665
+ if (hasTerminalError) {
666
+ return { error: terminalError, messages: conversationPrompt };
667
+ }
603
668
  return conversationPrompt;
604
669
  }
605
670
  function getModelInfo(model) {
@@ -763,7 +828,7 @@ var WorkflowAgent = class {
763
828
  throw new Error("Not implemented");
764
829
  }
765
830
  async stream(options) {
766
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T;
831
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U;
767
832
  const { onFinish, onEnd = onFinish } = options;
768
833
  let effectiveModel = this.model;
769
834
  let effectiveInstructions = (_b = (_a = options.instructions) != null ? _a : options.system) != null ? _b : this.instructions;
@@ -1108,10 +1173,8 @@ var WorkflowAgent = class {
1108
1173
  supportedUrls: {},
1109
1174
  download
1110
1175
  });
1111
- const effectiveAbortSignal = mergeAbortSignals(
1112
- (_r = options.abortSignal) != null ? _r : effectiveGenerationSettings.abortSignal,
1113
- options.timeout
1114
- );
1176
+ const effectiveAbortSignal = (_r = options.abortSignal) != null ? _r : effectiveGenerationSettings.abortSignal;
1177
+ const timeoutAt = options.timeout == null ? void 0 : Date.now() + options.timeout;
1115
1178
  const mergedGenerationSettings = {
1116
1179
  ...effectiveGenerationSettings,
1117
1180
  ...options.maxOutputTokens !== void 0 && {
@@ -1389,7 +1452,6 @@ var WorkflowAgent = class {
1389
1452
  stopConditions: effectiveStopWhenFromPrepare,
1390
1453
  onStepEnd: mergedOnStepEnd,
1391
1454
  onStepStart: mergedOnStepStart,
1392
- onError: options.onError,
1393
1455
  prepareStep: (_z = options.prepareStep) != null ? _z : this.prepareStep,
1394
1456
  generationSettings: mergedGenerationSettings,
1395
1457
  toolChoice: effectiveToolChoice,
@@ -1397,13 +1459,17 @@ var WorkflowAgent = class {
1397
1459
  toolsContext,
1398
1460
  telemetry: effectiveTelemetry,
1399
1461
  includeRawChunks: (_A = options.includeRawChunks) != null ? _A : false,
1462
+ timeoutAt,
1400
1463
  repairToolCall: (_C = (_B = options.repairToolCall) != null ? _B : options.experimental_repairToolCall) != null ? _C : this.repairToolCall,
1401
1464
  responseFormat: await ((_E = (_D = options.output) != null ? _D : this.output) == null ? void 0 : _E.responseFormat),
1402
1465
  experimental_sandbox: sandbox
1403
1466
  });
1404
1467
  let finalMessages;
1405
1468
  let encounteredError;
1469
+ let hasEncounteredError = false;
1406
1470
  let wasAborted = false;
1471
+ let terminalError;
1472
+ let hasTerminalError = false;
1407
1473
  try {
1408
1474
  let result = await iterator.next();
1409
1475
  while (!result.done) {
@@ -1700,11 +1766,24 @@ var WorkflowAgent = class {
1700
1766
  }
1701
1767
  }
1702
1768
  if (result.done) {
1703
- finalMessages = result.value;
1769
+ if (Array.isArray(result.value)) {
1770
+ finalMessages = result.value;
1771
+ } else if ("error" in result.value) {
1772
+ finalMessages = result.value.messages;
1773
+ terminalError = result.value.error;
1774
+ hasTerminalError = true;
1775
+ } else {
1776
+ finalMessages = result.value.messages;
1777
+ wasAborted = true;
1778
+ if (options.onAbort) {
1779
+ await options.onAbort({ steps });
1780
+ }
1781
+ }
1704
1782
  }
1705
1783
  } catch (error) {
1706
1784
  encounteredError = error;
1707
- if (error instanceof Error && error.name === "AbortError") {
1785
+ hasEncounteredError = true;
1786
+ if (isAbortError2(error)) {
1708
1787
  wasAborted = true;
1709
1788
  if (options.onAbort) {
1710
1789
  await options.onAbort({ steps });
@@ -1714,8 +1793,14 @@ var WorkflowAgent = class {
1714
1793
  }
1715
1794
  await ((_L = telemetryDispatcher.onError) == null ? void 0 : _L.call(telemetryDispatcher, error));
1716
1795
  }
1796
+ if (hasTerminalError) {
1797
+ if (options.onError) {
1798
+ await options.onError({ error: terminalError });
1799
+ }
1800
+ await ((_M = telemetryDispatcher.onError) == null ? void 0 : _M.call(telemetryDispatcher, terminalError));
1801
+ }
1717
1802
  const messages = finalMessages != null ? finalMessages : prompt.messages;
1718
- const effectiveOutput = (_M = options.output) != null ? _M : this.output;
1803
+ const effectiveOutput = (_N = options.output) != null ? _N : this.output;
1719
1804
  let experimentalOutput = void 0;
1720
1805
  if (effectiveOutput && steps.length > 0) {
1721
1806
  const lastStep2 = steps[steps.length - 1];
@@ -1731,20 +1816,21 @@ var WorkflowAgent = class {
1731
1816
  }
1732
1817
  );
1733
1818
  } catch (parseError) {
1734
- if (!encounteredError) {
1819
+ if (!hasEncounteredError) {
1735
1820
  encounteredError = parseError;
1821
+ hasEncounteredError = true;
1736
1822
  }
1737
1823
  }
1738
1824
  }
1739
1825
  }
1740
1826
  const lastStep = steps[steps.length - 1];
1741
1827
  const totalUsage = aggregateUsage(steps);
1742
- const finishReason = (_N = lastStep == null ? void 0 : lastStep.finishReason) != null ? _N : "other";
1828
+ const finishReason = (_O = lastStep == null ? void 0 : lastStep.finishReason) != null ? _O : "other";
1743
1829
  if (mergedOnEnd && !wasAborted) {
1744
1830
  await mergedOnEnd({
1745
1831
  steps,
1746
1832
  messages,
1747
- text: (_O = lastStep == null ? void 0 : lastStep.text) != null ? _O : "",
1833
+ text: (_P = lastStep == null ? void 0 : lastStep.text) != null ? _P : "",
1748
1834
  finishReason,
1749
1835
  usage: totalUsage,
1750
1836
  totalUsage,
@@ -1756,17 +1842,17 @@ var WorkflowAgent = class {
1756
1842
  if (!wasAborted && steps.length > 0) {
1757
1843
  const telemetrySteps = steps.map(normalizeStepForTelemetry2);
1758
1844
  const lastTelemetryStep = telemetrySteps[telemetrySteps.length - 1];
1759
- await ((_P = telemetryDispatcher.onEnd) == null ? void 0 : _P.call(telemetryDispatcher, {
1845
+ await ((_Q = telemetryDispatcher.onEnd) == null ? void 0 : _Q.call(telemetryDispatcher, {
1760
1846
  ...lastTelemetryStep,
1761
1847
  steps: telemetrySteps,
1762
1848
  usage: totalUsage,
1763
1849
  totalUsage
1764
1850
  }));
1765
1851
  }
1766
- if (encounteredError) {
1852
+ if (hasEncounteredError) {
1767
1853
  if (options.writable) {
1768
- const sendFinish = (_Q = options.sendFinish) != null ? _Q : true;
1769
- const preventClose = (_R = options.preventClose) != null ? _R : false;
1854
+ const sendFinish = (_R = options.sendFinish) != null ? _R : true;
1855
+ const preventClose = (_S = options.preventClose) != null ? _S : false;
1770
1856
  if (sendFinish || !preventClose) {
1771
1857
  await closeStream(options.writable, preventClose, sendFinish);
1772
1858
  }
@@ -1774,8 +1860,8 @@ var WorkflowAgent = class {
1774
1860
  throw encounteredError;
1775
1861
  }
1776
1862
  if (options.writable) {
1777
- const sendFinish = (_S = options.sendFinish) != null ? _S : true;
1778
- const preventClose = (_T = options.preventClose) != null ? _T : false;
1863
+ const sendFinish = (_T = options.sendFinish) != null ? _T : true;
1864
+ const preventClose = (_U = options.preventClose) != null ? _U : false;
1779
1865
  if (sendFinish || !preventClose) {
1780
1866
  await closeStream(options.writable, preventClose, sendFinish);
1781
1867
  }
@@ -1787,7 +1873,8 @@ var WorkflowAgent = class {
1787
1873
  toolResults: lastStepToolResults,
1788
1874
  finishReason,
1789
1875
  totalUsage,
1790
- output: experimentalOutput
1876
+ output: experimentalOutput,
1877
+ ...hasTerminalError ? { error: terminalError } : {}
1791
1878
  };
1792
1879
  }
1793
1880
  };