ai 7.0.153 → 7.0.155

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/index.d.ts +224 -9
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1071 -893
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +54 -7
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +29 -399
  9. package/dist/internal/index.js.map +1 -1
  10. package/docs/02-getting-started/02-nextjs-app-router.mdx +2 -2
  11. package/docs/02-getting-started/08-angular.mdx +327 -0
  12. package/docs/02-getting-started/{03-nextjs-pages-router.mdx → 10-nextjs-pages-router.mdx} +12 -20
  13. package/docs/02-getting-started/index.mdx +2 -0
  14. package/docs/03-agents/01-overview.mdx +1 -0
  15. package/docs/03-agents/04-loop-control.mdx +117 -0
  16. package/docs/03-agents/06-tool-approvals.mdx +1 -0
  17. package/docs/03-agents/09-chat-platforms.mdx +164 -0
  18. package/docs/03-agents/index.mdx +6 -0
  19. package/docs/03-ai-sdk-core/16-mcp-tools.mdx +7 -0
  20. package/docs/03-ai-sdk-core/60-telemetry.mdx +16 -3
  21. package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +10 -0
  22. package/docs/04-ai-sdk-ui/21-transport.mdx +86 -0
  23. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +46 -0
  24. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +46 -0
  25. package/docs/07-reference/01-ai-sdk-core/16-tool-loop-agent.mdx +35 -0
  26. package/docs/07-reference/01-ai-sdk-core/23-create-mcp-client.mdx +7 -0
  27. package/docs/07-reference/05-ai-sdk-errors/ai-invalid-prompt-error.mdx +25 -10
  28. package/package.json +11 -11
  29. package/src/agent/create-agent-ui-stream-response.ts +3 -0
  30. package/src/agent/create-agent-ui-stream.ts +16 -0
  31. package/src/agent/pipe-agent-ui-stream-to-response.ts +3 -0
  32. package/src/agent/tool-loop-agent-settings.ts +37 -1
  33. package/src/agent/tool-loop-agent.ts +43 -7
  34. package/src/batch/batch.ts +0 -8
  35. package/src/decide/validate-decision.ts +39 -64
  36. package/src/generate-object/validate-object-generation-input.ts +1 -39
  37. package/src/generate-speech/stream-speech.ts +6 -18
  38. package/src/generate-text/generate-text-events.ts +37 -0
  39. package/src/generate-text/generate-text.ts +521 -373
  40. package/src/generate-text/index.ts +8 -0
  41. package/src/generate-text/prepare-step.ts +12 -0
  42. package/src/generate-text/restricted-telemetry-dispatcher.ts +31 -18
  43. package/src/generate-text/smooth-stream.ts +2 -14
  44. package/src/generate-text/steering.ts +285 -0
  45. package/src/generate-text/step-result.ts +18 -0
  46. package/src/generate-text/stream-text.ts +664 -473
  47. package/src/generate-text/tool-caller-configuration.ts +2 -26
  48. package/src/prompt/prepare-language-model-call-options.ts +0 -50
  49. package/src/prompt/standardize-prompt.ts +9 -58
  50. package/src/telemetry/create-telemetry-dispatcher.ts +3 -0
  51. package/src/telemetry/telemetry.ts +15 -1
  52. package/src/test/mock-sandbox.ts +5 -11
  53. package/src/util/is-caused-by-abort.ts +20 -0
@@ -16,6 +16,7 @@ import {
16
16
  createAgentUIStream,
17
17
  type AgentUIMessageStreamOptions,
18
18
  } from './create-agent-ui-stream';
19
+ import type { Steering } from '../generate-text/steering';
19
20
 
20
21
  /**
21
22
  * Runs the agent and returns a response object with a UI message stream.
@@ -26,6 +27,7 @@ import {
26
27
  * @param abortSignal - Abort signal. Optional.
27
28
  * @param timeout - Timeout in milliseconds. Optional.
28
29
  * @param experimental_sandbox - The sandbox environment that is passed through to tool execution. Optional.
30
+ * @param experimental_steering - A steering object that adds user messages to the run at the next step boundary. Only supported by `ToolLoopAgent`. Optional.
29
31
  * @param options - The options for the agent. Optional.
30
32
  * @param experimental_transform - Stream transformations. Optional.
31
33
  * @param onStepEnd - Callback that is called when each step ends. Optional.
@@ -70,6 +72,7 @@ export async function createAgentUIStreamResponse<
70
72
  abortSignal?: AbortSignal;
71
73
  timeout?: TimeoutConfiguration<TOOLS>;
72
74
  experimental_sandbox?: SandboxSession;
75
+ experimental_steering?: Steering;
73
76
  options?: CALL_OPTIONS;
74
77
  experimental_transform?: Arrayable<StreamTextTransform<TOOLS>>;
75
78
  } & UIMessageStreamResponseInit &
@@ -28,7 +28,10 @@ import {
28
28
  createAsyncIterableStream,
29
29
  type AsyncIterableStream,
30
30
  } from '../util/async-iterable-stream';
31
+ import { InvalidArgumentError } from '../error/invalid-argument-error';
32
+ import type { Steering } from '../generate-text/steering';
31
33
  import type { Agent } from './agent';
34
+ import { ToolLoopAgent } from './tool-loop-agent';
32
35
 
33
36
  /**
34
37
  * UI stream options with separate generation and UI message step callbacks.
@@ -56,6 +59,7 @@ export type AgentUIMessageStreamOptions<
56
59
  * @param abortSignal - The abort signal. Optional.
57
60
  * @param timeout - Timeout in milliseconds. Optional.
58
61
  * @param experimental_sandbox - The sandbox environment that is passed through to tool execution. Optional.
62
+ * @param experimental_steering - A steering object that adds user messages to the run at the next step boundary. Only supported by `ToolLoopAgent`. Optional.
59
63
  * @param options - The options for the agent.
60
64
  * @param experimental_transform - The stream transformations. Optional.
61
65
  * @param onStepEnd - Callback that is called when each step ends. Optional.
@@ -83,6 +87,7 @@ export async function createAgentUIStream<
83
87
  abortSignal,
84
88
  timeout,
85
89
  experimental_sandbox: sandbox,
90
+ experimental_steering: steering,
86
91
  experimental_transform,
87
92
  onStepEnd,
88
93
  onStepFinish,
@@ -101,12 +106,22 @@ export async function createAgentUIStream<
101
106
  abortSignal?: AbortSignal;
102
107
  timeout?: TimeoutConfiguration<TOOLS>;
103
108
  experimental_sandbox?: SandboxSession;
109
+ experimental_steering?: Steering;
104
110
  options?: CALL_OPTIONS;
105
111
  experimental_transform?: Arrayable<StreamTextTransform<TOOLS>>;
106
112
  // TODO `originalMessages` is part of this for bc, omit in v7
107
113
  } & AgentUIMessageStreamOptions<TOOLS, UI_MESSAGE>): Promise<
108
114
  AsyncIterableStream<InferUIMessageChunk<UI_MESSAGE>>
109
115
  > {
116
+ // other agents would ignore the steering object and never resolve its messages:
117
+ if (steering != null && !(agent instanceof ToolLoopAgent)) {
118
+ throw new InvalidArgumentError({
119
+ parameter: 'experimental_steering',
120
+ value: steering,
121
+ message: 'Steering is only supported by ToolLoopAgent.',
122
+ });
123
+ }
124
+
110
125
  const validatedMessages = await validateUIMessagesForAgent<UI_MESSAGE>({
111
126
  messages: uiMessages,
112
127
  // tools are compatible; the casting is required because the context param is
@@ -130,6 +145,7 @@ export async function createAgentUIStream<
130
145
  abortSignal,
131
146
  timeout,
132
147
  experimental_sandbox: sandbox,
148
+ experimental_steering: steering,
133
149
  experimental_transform,
134
150
  onStepEnd: onStepEnd ?? onStepFinish,
135
151
  });
@@ -17,6 +17,7 @@ import {
17
17
  createAgentUIStream,
18
18
  type AgentUIMessageStreamOptions,
19
19
  } from './create-agent-ui-stream';
20
+ import type { Steering } from '../generate-text/steering';
20
21
 
21
22
  /**
22
23
  * Pipes the agent UI message stream to a Node.js ServerResponse object.
@@ -28,6 +29,7 @@ import {
28
29
  * @param abortSignal - Abort signal. Optional.
29
30
  * @param timeout - Timeout in milliseconds. Optional.
30
31
  * @param experimental_sandbox - The sandbox environment that is passed through to tool execution. Optional.
32
+ * @param experimental_steering - A steering object that adds user messages to the run at the next step boundary. Only supported by `ToolLoopAgent`. Optional.
31
33
  * @param options - The options for the agent. Optional.
32
34
  * @param experimental_transform - Stream transformations. Optional.
33
35
  * @param onStepEnd - Callback that is called when each step ends. Optional.
@@ -72,6 +74,7 @@ export async function pipeAgentUIStreamToResponse<
72
74
  abortSignal?: AbortSignal;
73
75
  timeout?: TimeoutConfiguration<TOOLS>;
74
76
  experimental_sandbox?: SandboxSession;
77
+ experimental_steering?: Steering;
75
78
  options?: CALL_OPTIONS;
76
79
  experimental_transform?: Arrayable<StreamTextTransform<TOOLS>>;
77
80
  } & UIMessageStreamResponseInit &
@@ -13,6 +13,7 @@ import type {
13
13
  GenerateTextOnEndCallback,
14
14
  GenerateTextOnStartCallback,
15
15
  GenerateTextOnStepEndCallback,
16
+ GenerateTextOnStepErrorCallback,
16
17
  GenerateTextOnStepFinishCallback,
17
18
  GenerateTextOnStepStartCallback,
18
19
  } from '../generate-text/generate-text-events';
@@ -24,7 +25,11 @@ import type {
24
25
  import type { Output } from '../generate-text/output';
25
26
  import type { PrepareStepFunction } from '../generate-text/prepare-step';
26
27
  import type { StopCondition } from '../generate-text/stop-condition';
27
- import type { StreamTextInclude } from '../generate-text/stream-text';
28
+ import type {
29
+ StreamTextInclude,
30
+ StreamTextOnAbortCallback,
31
+ StreamTextOnErrorCallback,
32
+ } from '../generate-text/stream-text';
28
33
  import type { ToolApprovalConfiguration } from '../generate-text/tool-approval-configuration';
29
34
  import type { Experimental_ToolCallers } from '../generate-text/tool-caller-configuration';
30
35
  import type { ToolCallRepairFunction } from '../generate-text/tool-call-repair-function';
@@ -253,6 +258,37 @@ export type ToolLoopAgentSettings<
253
258
  NoInfer<RUNTIME_CONTEXT>
254
259
  >;
255
260
 
261
+ /**
262
+ * Callback that is called when a step that started fails before it
263
+ * produces a step result. `onStepEnd` is not called for such a step.
264
+ *
265
+ * Unlike `onStepStart` and `onStepEnd`, it can only be set here and not
266
+ * per call, because the per-call callbacks are shared with other `Agent`
267
+ * implementations that do not report step errors.
268
+ */
269
+ onStepError?: GenerateTextOnStepErrorCallback<
270
+ NoInfer<TOOLS>,
271
+ NoInfer<RUNTIME_CONTEXT>,
272
+ NoInfer<OUTPUT>
273
+ >;
274
+
275
+ /**
276
+ * Callback that is called when an error occurs. For `stream()` it receives
277
+ * the errors of the stream, for `generate()` it receives the error before
278
+ * it is thrown. Aborts and timeouts are not reported.
279
+ */
280
+ onError?: StreamTextOnErrorCallback;
281
+
282
+ /**
283
+ * Callback that is called when `stream()` is aborted or times out.
284
+ * `generate()` does not call it: an aborted or timed out `generate()` call
285
+ * rejects, and its step ends with `onStepError`.
286
+ */
287
+ onAbort?: StreamTextOnAbortCallback<
288
+ NoInfer<TOOLS>,
289
+ NoInfer<RUNTIME_CONTEXT>
290
+ >;
291
+
256
292
  /**
257
293
  * Callback that is called when each step (LLM call) ends, including intermediate steps.
258
294
  *
@@ -15,8 +15,11 @@ import type { GenerateTextResult } from '../generate-text/generate-text-result';
15
15
  import type { Output } from '../generate-text/output';
16
16
  import { streamText } from '../generate-text/stream-text';
17
17
  import type { StreamTextResult } from '../generate-text/stream-text-result';
18
+ import type { Steering } from '../generate-text/steering';
18
19
  import type { Prompt } from '../prompt';
20
+ import { isCausedByAbort } from '../util/is-caused-by-abort';
19
21
  import { mergeCallbacks } from '../util/merge-callbacks';
22
+ import { notify } from '../util/notify';
20
23
  import type {
21
24
  Agent,
22
25
  AgentCallParameters,
@@ -25,6 +28,17 @@ import type {
25
28
  import type { ToolLoopAgentSettings } from './tool-loop-agent-settings';
26
29
  import { createDefaultStopCondition } from './default-stop-condition';
27
30
 
31
+ /**
32
+ * Call parameters that only `ToolLoopAgent` supports.
33
+ */
34
+ export interface ToolLoopAgentCallExtensions {
35
+ /**
36
+ * A steering object created with `experimental_createSteering()`.
37
+ * Messages sent with it are added to the run at the next step boundary.
38
+ */
39
+ experimental_steering?: Steering;
40
+ }
41
+
28
42
  /**
29
43
  * A tool loop agent is an agent that runs tools in a loop. In each step,
30
44
  * it calls the LLM, and if there are tool calls, it executes the tools
@@ -96,6 +110,8 @@ export class ToolLoopAgent<
96
110
  | 'onToolExecutionEnd'
97
111
  | 'onStepEnd'
98
112
  | 'onStepFinish'
113
+ | 'onError'
114
+ | 'onAbort'
99
115
  | 'onEnd'
100
116
  | 'onFinish'
101
117
  > &
@@ -122,6 +138,8 @@ export class ToolLoopAgent<
122
138
  onToolExecutionEnd: _settingsOnToolExecutionEnd,
123
139
  onStepEnd: _settingsOnStepEnd,
124
140
  onStepFinish: _settingsOnStepFinish,
141
+ onError: _settingsOnError,
142
+ onAbort: _settingsOnAbort,
125
143
  onFinish: _settingsOnFinish,
126
144
  onEnd: _settingsOnEnd,
127
145
  ...settingsWithoutCallbacks
@@ -198,6 +216,7 @@ export class ToolLoopAgent<
198
216
  abortSignal,
199
217
  timeout,
200
218
  experimental_sandbox: sandbox,
219
+ experimental_steering: steering,
201
220
  onStart,
202
221
  experimental_onStart,
203
222
  onStepStart,
@@ -209,7 +228,8 @@ export class ToolLoopAgent<
209
228
  onFinish,
210
229
  onEnd = onFinish,
211
230
  ...options
212
- }: AgentCallParameters<CALL_OPTIONS, TOOLS, RUNTIME_CONTEXT>): Promise<
231
+ }: AgentCallParameters<CALL_OPTIONS, TOOLS, RUNTIME_CONTEXT> &
232
+ ToolLoopAgentCallExtensions): Promise<
213
233
  GenerateTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>
214
234
  > {
215
235
  const generate = generateText<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
@@ -221,6 +241,7 @@ export class ToolLoopAgent<
221
241
  abortSignal,
222
242
  timeout: timeout ?? preparedCall.timeout,
223
243
  experimental_sandbox: sandbox,
244
+ experimental_steering: steering,
224
245
  onStart: mergeCallbacks(
225
246
  this.settings.onStart ?? this.settings.experimental_onStart,
226
247
  (onStart ?? experimental_onStart) as
@@ -248,11 +269,21 @@ export class ToolLoopAgent<
248
269
  onEnd: mergeCallbacks(this.settings.onEnd, onEnd),
249
270
  };
250
271
 
251
- return await generate({
252
- ...preparedCall,
253
- ...callbackArgs,
254
- headers: this.agentHeaders(preparedCall),
255
- } as unknown as Parameters<typeof generate>[0]);
272
+ try {
273
+ return await generate({
274
+ ...preparedCall,
275
+ ...callbackArgs,
276
+ headers: this.agentHeaders(preparedCall),
277
+ } as unknown as Parameters<typeof generate>[0]);
278
+ } catch (error) {
279
+ // generateText has no onError option, so report the error before it is
280
+ // thrown. Aborts (with any reason) and timeouts are not reported, like in
281
+ // streamText:
282
+ if (!isCausedByAbort(error) && !abortSignal?.aborted) {
283
+ await notify({ event: { error }, callbacks: this.settings.onError });
284
+ }
285
+ throw error;
286
+ }
256
287
  }
257
288
 
258
289
  /**
@@ -262,6 +293,7 @@ export class ToolLoopAgent<
262
293
  abortSignal,
263
294
  timeout,
264
295
  experimental_sandbox: sandbox,
296
+ experimental_steering: steering,
265
297
  experimental_transform,
266
298
  onStart,
267
299
  experimental_onStart,
@@ -274,7 +306,8 @@ export class ToolLoopAgent<
274
306
  onFinish,
275
307
  onEnd = onFinish,
276
308
  ...options
277
- }: AgentStreamParameters<CALL_OPTIONS, TOOLS, RUNTIME_CONTEXT>): Promise<
309
+ }: AgentStreamParameters<CALL_OPTIONS, TOOLS, RUNTIME_CONTEXT> &
310
+ ToolLoopAgentCallExtensions): Promise<
278
311
  StreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>
279
312
  > {
280
313
  const stream = streamText<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
@@ -286,6 +319,7 @@ export class ToolLoopAgent<
286
319
  abortSignal,
287
320
  timeout: timeout ?? preparedCall.timeout,
288
321
  experimental_sandbox: sandbox,
322
+ experimental_steering: steering,
289
323
  experimental_transform,
290
324
  onStart: mergeCallbacks(
291
325
  this.settings.onStart ?? this.settings.experimental_onStart,
@@ -312,6 +346,8 @@ export class ToolLoopAgent<
312
346
  onStepEnd ?? onStepFinish,
313
347
  ),
314
348
  onEnd: mergeCallbacks(this.settings.onEnd, onEnd),
349
+ onError: this.settings.onError,
350
+ onAbort: this.settings.onAbort,
315
351
  };
316
352
 
317
353
  return await stream({
@@ -491,14 +491,6 @@ function validateBatchReference({
491
491
  batchApi: BatchV4;
492
492
  batch: BatchReference;
493
493
  }) {
494
- if (batch.version !== 2) {
495
- throw new InvalidArgumentError({
496
- parameter: 'batch',
497
- value: batch,
498
- message: 'batch must be a supported batch reference',
499
- });
500
- }
501
-
502
494
  if (batch.provider !== batchApi.provider) {
503
495
  throw new InvalidArgumentError({
504
496
  parameter: 'provider',
@@ -3,7 +3,6 @@ import {
3
3
  type Experimental_DecisionModelV4Result as DecisionModelV4Result,
4
4
  type Experimental_DecisionModelV4Question as DecisionQuestion,
5
5
  } from '@ai-sdk/provider';
6
- import { filePartSchema, textPartSchema } from '../prompt/content-part';
7
6
  import type { DecisionState } from './decision-state';
8
7
  import { InvalidArgumentError } from '../error/invalid-argument-error';
9
8
 
@@ -17,30 +16,28 @@ function isRecord(value: unknown): value is Record<string, unknown> {
17
16
  return prototype === Object.prototype || prototype === null;
18
17
  }
19
18
 
20
- function isJSON(value: unknown, ancestors = new Set<object>()): boolean {
21
- if (value === null || typeof value === 'string' || typeof value === 'boolean')
22
- return true;
19
+ /**
20
+ * Checks the JSON constraints that types cannot express: finite numbers, no
21
+ * cycles, and no array holes. These would otherwise be silently changed by
22
+ * `JSON.stringify` or make it throw.
23
+ */
24
+ function isSerializableJSON(
25
+ value: unknown,
26
+ ancestors = new Set<object>(),
27
+ ): boolean {
23
28
  if (typeof value === 'number') return Number.isFinite(value);
24
- if (typeof value !== 'object' || (!Array.isArray(value) && !isRecord(value)))
25
- return false;
29
+ if (value == null || typeof value !== 'object') return true;
26
30
  if (ancestors.has(value)) return false;
27
31
  ancestors.add(value);
28
- const valid =
29
- Object.getOwnPropertySymbols(value).length === 0 &&
30
- (Array.isArray(value)
31
- ? Array.from(value).every(item => isJSON(item, ancestors))
32
- : Object.values(value).every(item => isJSON(item, ancestors)));
32
+ const valid = Array.isArray(value)
33
+ ? Array.from(value.keys()).every(
34
+ index => index in value && isSerializableJSON(value[index], ancestors),
35
+ )
36
+ : Object.values(value).every(item => isSerializableJSON(item, ancestors));
33
37
  ancestors.delete(value);
34
38
  return valid;
35
39
  }
36
40
 
37
- function isInput(value: unknown): boolean {
38
- return (
39
- (typeof value === 'string' || Array.isArray(value) || isRecord(value)) &&
40
- isJSON(value)
41
- );
42
- }
43
-
44
41
  function invalidInput(
45
42
  parameter: string,
46
43
  value: unknown,
@@ -56,50 +53,43 @@ export function validateDecisionInput({
56
53
  state: DecisionState;
57
54
  questions: Readonly<Record<string, DecisionQuestion>>;
58
55
  }) {
59
- const validState = Array.isArray(state)
60
- ? Array.from(state).every(part => {
61
- if (!isRecord(part)) return false;
62
- switch (part.type) {
63
- case 'text':
64
- return textPartSchema.safeParse(part).success;
65
- case 'file':
66
- return filePartSchema.safeParse(part).success;
67
- case 'json':
68
- return isJSON(part.value);
69
- default:
70
- return false;
71
- }
72
- })
73
- : (typeof state === 'string' || isRecord(state)) && isJSON(state);
74
- if (!validState) {
56
+ if (
57
+ Array.isArray(state) &&
58
+ Array.from(state.keys()).some(index => !(index in state))
59
+ ) {
60
+ invalidInput('state', state, 'state parts must not contain array holes');
61
+ }
62
+
63
+ const stateJSON = Array.isArray(state)
64
+ ? state.flatMap(part => (part.type === 'json' ? [part.value] : []))
65
+ : state;
66
+ if (!isSerializableJSON(stateJSON)) {
75
67
  invalidInput(
76
68
  'state',
77
69
  state,
78
- 'must be a string, JSON object, or array of text, file, or json parts',
70
+ 'JSON content must not contain non-finite numbers, cycles, or array holes',
79
71
  );
80
72
  }
81
- if (!isRecord(questions) || Object.keys(questions).length === 0) {
73
+
74
+ if (Object.keys(questions).length === 0) {
82
75
  invalidInput('questions', questions, 'must be a nonempty question map');
83
76
  }
84
77
 
85
78
  for (const [id, question] of Object.entries(questions)) {
86
79
  const parameter = `questions.${id}`;
87
80
  if (
88
- !isRecord(question) ||
89
- ((question.type === 'box' || question.instructions != null) &&
90
- !isInput(question.instructions))
81
+ !isSerializableJSON(question.instructions) ||
82
+ ('criteria' in question && !isSerializableJSON(question.criteria))
91
83
  ) {
92
84
  invalidInput(
93
85
  parameter,
94
86
  question,
95
- 'instructions must be a JSON-compatible string, object, or array',
87
+ 'instructions and criteria must not contain non-finite numbers, cycles, or array holes',
96
88
  );
97
89
  }
98
- if (question.type === 'box') continue;
99
- const criteria = question.criteria;
100
90
  switch (question.type) {
101
91
  case 'choice':
102
- if (!isRecord(criteria) || Object.keys(criteria).length === 0) {
92
+ if (Object.keys(question.criteria).length === 0) {
103
93
  invalidInput(
104
94
  parameter,
105
95
  question,
@@ -108,7 +98,7 @@ export function validateDecisionInput({
108
98
  }
109
99
  break;
110
100
  case 'score':
111
- if (!Array.isArray(criteria) || criteria.length === 0) {
101
+ if (question.criteria.length === 0) {
112
102
  invalidInput(
113
103
  parameter,
114
104
  question,
@@ -116,10 +106,11 @@ export function validateDecisionInput({
116
106
  );
117
107
  }
118
108
  break;
119
- case 'boolean':
109
+ case 'boolean': {
110
+ const criteria = question.criteria;
120
111
  if (
121
112
  question.instructions == null &&
122
- (!isRecord(criteria) ||
113
+ (criteria == null ||
123
114
  !Object.values(criteria).some(value => value != null))
124
115
  ) {
125
116
  invalidInput(
@@ -128,9 +119,8 @@ export function validateDecisionInput({
128
119
  'boolean questions require instructions or at least one non-null criterion',
129
120
  );
130
121
  }
131
- if (criteria === undefined) continue;
132
122
  if (
133
- !isRecord(criteria) ||
123
+ criteria != null &&
134
124
  Object.keys(criteria).some(key => key !== 'true' && key !== 'false')
135
125
  ) {
136
126
  invalidInput(
@@ -140,22 +130,7 @@ export function validateDecisionInput({
140
130
  );
141
131
  }
142
132
  break;
143
- default:
144
- invalidInput(
145
- parameter,
146
- question,
147
- 'question type must be choice, score, boolean, or box',
148
- );
149
- }
150
- if (
151
- !isJSON(criteria) ||
152
- Object.values(criteria).some(value => value !== null && !isInput(value))
153
- ) {
154
- invalidInput(
155
- parameter,
156
- question,
157
- 'criteria descriptions must be JSON-compatible strings, objects, arrays, or null',
158
- );
133
+ }
159
134
  }
160
135
  }
161
136
  }
@@ -14,20 +14,6 @@ export function validateObjectGenerationInput({
14
14
  schemaDescription?: string;
15
15
  enumValues?: Array<unknown>;
16
16
  }) {
17
- if (
18
- output != null &&
19
- output !== 'object' &&
20
- output !== 'array' &&
21
- output !== 'enum' &&
22
- output !== 'no-schema'
23
- ) {
24
- throw new InvalidArgumentError({
25
- parameter: 'output',
26
- value: output,
27
- message: 'Invalid output type.',
28
- });
29
- }
30
-
31
17
  if (output === 'no-schema') {
32
18
  if (schema != null) {
33
19
  throw new InvalidArgumentError({
@@ -63,14 +49,6 @@ export function validateObjectGenerationInput({
63
49
  }
64
50
 
65
51
  if (output === 'object') {
66
- if (schema == null) {
67
- throw new InvalidArgumentError({
68
- parameter: 'schema',
69
- value: schema,
70
- message: 'Schema is required for object output.',
71
- });
72
- }
73
-
74
52
  if (enumValues != null) {
75
53
  throw new InvalidArgumentError({
76
54
  parameter: 'enumValues',
@@ -81,14 +59,6 @@ export function validateObjectGenerationInput({
81
59
  }
82
60
 
83
61
  if (output === 'array') {
84
- if (schema == null) {
85
- throw new InvalidArgumentError({
86
- parameter: 'schema',
87
- value: schema,
88
- message: 'Element schema is required for array output.',
89
- });
90
- }
91
-
92
62
  if (enumValues != null) {
93
63
  throw new InvalidArgumentError({
94
64
  parameter: 'enumValues',
@@ -123,15 +93,7 @@ export function validateObjectGenerationInput({
123
93
  });
124
94
  }
125
95
 
126
- if (enumValues == null) {
127
- throw new InvalidArgumentError({
128
- parameter: 'enumValues',
129
- value: enumValues,
130
- message: 'Enum values are required for enum output.',
131
- });
132
- }
133
-
134
- for (const value of enumValues) {
96
+ for (const value of enumValues ?? []) {
135
97
  if (typeof value !== 'string') {
136
98
  throw new InvalidArgumentError({
137
99
  parameter: 'enumValues',
@@ -12,7 +12,6 @@ import {
12
12
  withUserAgentSuffix,
13
13
  type ProviderOptions,
14
14
  } from '@ai-sdk/provider-utils';
15
- import { InvalidArgumentError } from '../error/invalid-argument-error';
16
15
  import { NoSpeechGeneratedError } from '../error/no-speech-generated-error';
17
16
  import { StreamProviderError } from '../error/stream-provider-error';
18
17
  import { logWarnings } from '../logger/log-warnings';
@@ -533,23 +532,12 @@ function toSourceStream(
533
532
  iterable[Symbol.asyncIterator](),
534
533
  );
535
534
  }
536
- if (
537
- typeof (text as Partial<Iterable<unknown>>)?.[Symbol.iterator] ===
538
- 'function'
539
- ) {
540
- const iterable = text as Iterable<string | SpeechTextPart>;
541
- return convertAsyncIteratorToReadableStream(
542
- (async function* () {
543
- yield* iterable;
544
- })(),
545
- );
546
- }
547
- throw new InvalidArgumentError({
548
- parameter: 'text',
549
- value: text,
550
- message:
551
- 'Expected a string, an iterable or async iterable, or a ReadableStream of strings or text parts.',
552
- });
535
+ const iterable = text as Iterable<string | SpeechTextPart>;
536
+ return convertAsyncIteratorToReadableStream(
537
+ (async function* () {
538
+ yield* iterable;
539
+ })(),
540
+ );
553
541
  }
554
542
 
555
543
  function toTextPart(
@@ -392,6 +392,43 @@ export type GenerateTextOnStepFinishCallback<
392
392
  RUNTIME_CONTEXT extends Context = Context,
393
393
  > = GenerateTextOnStepEndCallback<TOOLS, RUNTIME_CONTEXT>;
394
394
 
395
+ /**
396
+ * Event passed to the `onStepError` callback.
397
+ *
398
+ * Contains the step start event of the step that failed before it produced
399
+ * a step result, along with the error.
400
+ */
401
+ export type GenerateTextStepErrorEvent<
402
+ TOOLS extends ToolSet = ToolSet,
403
+ RUNTIME_CONTEXT extends Context = Context,
404
+ OUTPUT extends Output = Output,
405
+ > = GenerateTextStepStartEvent<TOOLS, RUNTIME_CONTEXT, OUTPUT> & {
406
+ /** The error that ended the step. */
407
+ readonly error: unknown;
408
+ };
409
+
410
+ /**
411
+ * Callback that is set using the `onStepError` option.
412
+ *
413
+ * Called when a step that started fails before it produces a step result:
414
+ * when the provider call throws or rejects, or when the model stream ends
415
+ * without output. `onStepEnd` is not called for such a step. A provider
416
+ * `error` chunk does not fail the step, it still ends with `onStepEnd` and
417
+ * `finishReason: 'error'`.
418
+ *
419
+ * In `generateText`, an abort or timeout also ends the step here. The error
420
+ * is the abort or timeout error, or a provider error that has it as its
421
+ * `cause`. In `streamText`, an abort or timeout ends the step
422
+ * with `onAbort` instead.
423
+ *
424
+ * @param event - The step that failed and the error.
425
+ */
426
+ export type GenerateTextOnStepErrorCallback<
427
+ TOOLS extends ToolSet = ToolSet,
428
+ RUNTIME_CONTEXT extends Context = Context,
429
+ OUTPUT extends Output = Output,
430
+ > = Callback<GenerateTextStepErrorEvent<TOOLS, RUNTIME_CONTEXT, OUTPUT>>;
431
+
395
432
  /**
396
433
  * Callback that is set using the `onEnd` option.
397
434
  *