ai 7.0.152 → 7.0.154

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 (44) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/index.d.ts +224 -9
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +440 -120
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +52 -4
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +26 -20
  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/60-telemetry.mdx +16 -3
  20. package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +10 -0
  21. package/docs/04-ai-sdk-ui/21-transport.mdx +86 -0
  22. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +46 -0
  23. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +46 -0
  24. package/docs/07-reference/01-ai-sdk-core/16-tool-loop-agent.mdx +35 -0
  25. package/package.json +12 -12
  26. package/src/agent/create-agent-ui-stream-response.ts +3 -0
  27. package/src/agent/create-agent-ui-stream.ts +16 -0
  28. package/src/agent/pipe-agent-ui-stream-to-response.ts +3 -0
  29. package/src/agent/tool-loop-agent-settings.ts +37 -1
  30. package/src/agent/tool-loop-agent.ts +43 -7
  31. package/src/generate-object/generate-object.ts +1 -1
  32. package/src/generate-text/generate-text-events.ts +37 -0
  33. package/src/generate-text/generate-text.ts +528 -374
  34. package/src/generate-text/index.ts +8 -0
  35. package/src/generate-text/prepare-step.ts +12 -0
  36. package/src/generate-text/restricted-telemetry-dispatcher.ts +31 -18
  37. package/src/generate-text/steering.ts +293 -0
  38. package/src/generate-text/step-result.ts +18 -0
  39. package/src/generate-text/stream-text.ts +664 -473
  40. package/src/middleware/extract-json-middleware.ts +8 -2
  41. package/src/middleware/extract-reasoning-middleware.ts +42 -30
  42. package/src/telemetry/create-telemetry-dispatcher.ts +3 -0
  43. package/src/telemetry/telemetry.ts +15 -1
  44. package/src/util/is-caused-by-abort.ts +20 -0
@@ -10,10 +10,12 @@ export type {
10
10
  GenerateTextOnEndCallback,
11
11
  GenerateTextOnStartCallback,
12
12
  GenerateTextOnStepEndCallback,
13
+ GenerateTextOnStepErrorCallback,
13
14
  GenerateTextOnStepFinishCallback,
14
15
  GenerateTextOnStepStartCallback,
15
16
  GenerateTextStartEvent,
16
17
  GenerateTextStepEndEvent,
18
+ GenerateTextStepErrorEvent,
17
19
  GenerateTextStepStartEvent,
18
20
  OnFinishEvent,
19
21
  OnStartEvent,
@@ -48,6 +50,12 @@ export type {
48
50
  StepResult,
49
51
  StepResultPerformance,
50
52
  } from './step-result';
53
+ export {
54
+ createSteering as experimental_createSteering,
55
+ type Steering as Experimental_Steering,
56
+ type SteeringNotDeliveredReason as Experimental_SteeringNotDeliveredReason,
57
+ type SteeringOutcome as Experimental_SteeringOutcome,
58
+ } from './steering';
51
59
  export {
52
60
  hasToolCall,
53
61
  isLoopFinished,
@@ -5,6 +5,7 @@ import type {
5
5
  ModelMessage,
6
6
  ProviderOptions,
7
7
  ToolSet,
8
+ UserModelMessage,
8
9
  } from '@ai-sdk/provider-utils';
9
10
  import type { Instructions } from '../prompt';
10
11
  import type { LanguageModelCallOptions } from '../prompt/language-model-call-options';
@@ -26,6 +27,7 @@ import type { ToolOrder } from './tool-order';
26
27
  * @param options.messages - The messages that will be sent to the model for the current step. If you return a `messages` override, those messages carry forward to later steps.
27
28
  * @param options.initialMessages - The initial messages that were passed into generateText or streamText.
28
29
  * @param options.responseMessages - The response messages that have been accumulated from previous steps.
30
+ * @param options.experimental_steeringMessages - The steering messages that were added to `messages` for the current step.
29
31
  * @param options.runtimeContext - The user-defined runtime context.
30
32
  *
31
33
  * @returns An object that contains the settings for the step.
@@ -76,6 +78,16 @@ export type PrepareStepFunction<
76
78
  */
77
79
  responseMessages: Array<ResponseMessage>;
78
80
 
81
+ /**
82
+ * The user messages that were sent with `experimental_steering` and added to
83
+ * `messages` for the current step. Undefined when the step received no
84
+ * steering messages. `messages` already contains them. If you return a
85
+ * `messages` override that is rebuilt from other fields, e.g.
86
+ * `initialMessages` and `responseMessages`, add them back together with the
87
+ * steering messages of previous steps from `steps`.
88
+ */
89
+ experimental_steeringMessages?: Array<UserModelMessage>;
90
+
79
91
  /**
80
92
  * Tool context.
81
93
  */
@@ -16,8 +16,10 @@ import type {
16
16
  GenerateTextOnEndCallback,
17
17
  GenerateTextOnStartCallback,
18
18
  GenerateTextOnStepEndCallback,
19
+ GenerateTextOnStepErrorCallback,
19
20
  GenerateTextOnStepFinishCallback,
20
21
  GenerateTextOnStepStartCallback,
22
+ GenerateTextStepStartEvent,
21
23
  } from './generate-text-events';
22
24
  import type { Output } from './output';
23
25
  import { DefaultStepResult, type StepResult } from './step-result';
@@ -40,6 +42,7 @@ export type RestrictedTelemetryDispatcher<
40
42
  | 'onStepStart'
41
43
  | 'onStepEnd'
42
44
  | 'onStepFinish'
45
+ | 'onStepError'
43
46
  | 'onEnd'
44
47
  | 'onAbort'
45
48
  | 'onToolExecutionStart'
@@ -50,6 +53,7 @@ export type RestrictedTelemetryDispatcher<
50
53
  onStepEnd: GenerateTextOnStepEndCallback<TOOLS, RUNTIME_CONTEXT>;
51
54
  /** @deprecated Use `onStepEnd` instead. */
52
55
  onStepFinish: GenerateTextOnStepFinishCallback<TOOLS, RUNTIME_CONTEXT>;
56
+ onStepError: GenerateTextOnStepErrorCallback<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
53
57
  onEnd: GenerateTextOnEndCallback<TOOLS, RUNTIME_CONTEXT>;
54
58
  onAbort?: GenerateTextOnAbortCallback<TOOLS, RUNTIME_CONTEXT>;
55
59
  onToolExecutionStart?: OnToolExecutionStartCallback<TOOLS>;
@@ -94,6 +98,7 @@ function restrictStepResult<
94
98
  request: step.request,
95
99
  response: step.response,
96
100
  providerMetadata: step.providerMetadata,
101
+ steeringMessages: step.experimental_steeringMessages,
97
102
  });
98
103
  }
99
104
 
@@ -116,6 +121,29 @@ export function createRestrictedTelemetryDispatcher<
116
121
  }): RestrictedTelemetryDispatcher<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
117
122
  const telemetryDispatcher = createTelemetryDispatcher({ telemetry });
118
123
 
124
+ const restrictStepStartEvent = <
125
+ EVENT extends GenerateTextStepStartEvent<TOOLS, RUNTIME_CONTEXT, OUTPUT>,
126
+ >(
127
+ event: EVENT,
128
+ ) => ({
129
+ ...event,
130
+ runtimeContext: filterIncludedContext({
131
+ context: event.runtimeContext,
132
+ includeContext: includeRuntimeContext,
133
+ }),
134
+ steps: event.steps.map(step =>
135
+ restrictStepResult({
136
+ step,
137
+ includeRuntimeContext,
138
+ includeToolsContext,
139
+ }),
140
+ ),
141
+ toolsContext: filterToolsContext({
142
+ toolsContext: event.toolsContext,
143
+ includeToolsContext,
144
+ }),
145
+ });
146
+
119
147
  return {
120
148
  ...telemetryDispatcher,
121
149
  onStart: event =>
@@ -131,24 +159,7 @@ export function createRestrictedTelemetryDispatcher<
131
159
  }),
132
160
  }),
133
161
  onStepStart: event =>
134
- telemetryDispatcher.onStepStart?.({
135
- ...event,
136
- runtimeContext: filterIncludedContext({
137
- context: event.runtimeContext,
138
- includeContext: includeRuntimeContext,
139
- }),
140
- steps: event.steps.map(step =>
141
- restrictStepResult({
142
- step,
143
- includeRuntimeContext,
144
- includeToolsContext,
145
- }),
146
- ),
147
- toolsContext: filterToolsContext({
148
- toolsContext: event.toolsContext,
149
- includeToolsContext,
150
- }),
151
- }),
162
+ telemetryDispatcher.onStepStart?.(restrictStepStartEvent(event)),
152
163
  onStepEnd: event =>
153
164
  telemetryDispatcher.onStepEnd?.(
154
165
  restrictStepResult({
@@ -165,6 +176,8 @@ export function createRestrictedTelemetryDispatcher<
165
176
  includeToolsContext,
166
177
  }),
167
178
  ),
179
+ onStepError: event =>
180
+ telemetryDispatcher.onStepError?.(restrictStepStartEvent(event)),
168
181
  onEnd: event =>
169
182
  telemetryDispatcher.onEnd?.(
170
183
  ((restrictedSteps: StepResult<TOOLS, Context>[]) => {
@@ -0,0 +1,293 @@
1
+ import {
2
+ createIdGenerator,
3
+ type ModelMessage,
4
+ type UserModelMessage,
5
+ } from '@ai-sdk/provider-utils';
6
+ import { InvalidArgumentError } from '../error/invalid-argument-error';
7
+
8
+ const generateSteeringId = createIdGenerator({ prefix: 'steer', size: 24 });
9
+
10
+ const steeringRunStarter = Symbol.for('vercel.ai.steering.start');
11
+
12
+ /**
13
+ * Why a steering message was not delivered:
14
+ *
15
+ * - `finished`: the run ended before the message could be delivered.
16
+ * - `removed-by-prepare-step`: `prepareStep` returned messages without it.
17
+ * - `stop-condition`: a stop condition ended the run.
18
+ * - `waiting-for-tool-results`: the run ended to wait for tool results,
19
+ * e.g. a tool approval or a tool without an `execute` function.
20
+ * - `aborted`: the run was aborted.
21
+ * - `error`: the run failed.
22
+ */
23
+ export type SteeringNotDeliveredReason =
24
+ | 'finished'
25
+ | 'removed-by-prepare-step'
26
+ | 'stop-condition'
27
+ | 'waiting-for-tool-results'
28
+ | 'aborted'
29
+ | 'error';
30
+
31
+ /**
32
+ * The outcome of a steering message.
33
+ */
34
+ export type SteeringOutcome =
35
+ | {
36
+ readonly type: 'delivered';
37
+
38
+ /** The id of the steering message. */
39
+ readonly id: string;
40
+
41
+ /** The user message that was added to the run. */
42
+ readonly message: UserModelMessage;
43
+
44
+ /** The number of the step whose model call received the message. */
45
+ readonly stepNumber: number;
46
+ }
47
+ | {
48
+ readonly type: 'not-delivered';
49
+
50
+ /** The id of the steering message. */
51
+ readonly id: string;
52
+
53
+ /** The user message that was not added to the run. */
54
+ readonly message: UserModelMessage;
55
+
56
+ /** Why the message was not delivered. */
57
+ readonly reason: SteeringNotDeliveredReason;
58
+ };
59
+
60
+ /**
61
+ * Sends user messages to a running generation. Pass it to `generateText`,
62
+ * `streamText`, or an agent call with the `experimental_steering` option.
63
+ */
64
+ export interface Steering {
65
+ /**
66
+ * Adds a user message to the run at the next step boundary, after the
67
+ * current step and its tool calls are done. If the run would end because the
68
+ * model finished its answer, it continues for another step unless a stop
69
+ * condition is met.
70
+ *
71
+ * Messages that are sent before the run starts are delivered in the first
72
+ * step. A steering object can only be used for one generation.
73
+ *
74
+ * @param message - The user message, or its text.
75
+ * @param options.id - The id of the message. Sending a message again with
76
+ * the same id returns the outcome of the first message.
77
+ *
78
+ * @returns The outcome of the message. It never rejects.
79
+ */
80
+ steer(
81
+ message: string | UserModelMessage,
82
+ options?: { id?: string },
83
+ ): Promise<SteeringOutcome>;
84
+ }
85
+
86
+ /**
87
+ * The run side of a steering object.
88
+ */
89
+ export type SteeringRun = {
90
+ hasPendingMessages(): boolean;
91
+
92
+ /**
93
+ * Takes the waiting messages for the next step. They are resolved by
94
+ * `resolveTakenMessages` once the prompt of the step is known.
95
+ */
96
+ takeMessages(): Array<UserModelMessage>;
97
+
98
+ /**
99
+ * Resolves the taken messages as delivered when they are part of the
100
+ * messages that are sent to the model, and returns the delivered messages.
101
+ */
102
+ resolveTakenMessages(options: {
103
+ stepNumber: number;
104
+ sentMessages: ReadonlyArray<ModelMessage>;
105
+ }): Array<UserModelMessage>;
106
+
107
+ end(reason: SteeringNotDeliveredReason): void;
108
+ };
109
+
110
+ type PendingSteer = {
111
+ id: string;
112
+ message: UserModelMessage;
113
+ resolve: (outcome: SteeringOutcome) => void;
114
+ };
115
+
116
+ class DefaultSteering implements Steering {
117
+ private state: 'idle' | 'running' | 'ended' = 'idle';
118
+ private pending: Array<PendingSteer> = [];
119
+ private taken: Array<PendingSteer> = [];
120
+ private outcomes = new Map<string, Promise<SteeringOutcome>>();
121
+
122
+ steer(
123
+ message: string | UserModelMessage,
124
+ options?: { id?: string },
125
+ ): Promise<SteeringOutcome> {
126
+ const id = options?.id ?? generateSteeringId();
127
+
128
+ const existingOutcome = this.outcomes.get(id);
129
+ if (existingOutcome != null) {
130
+ return existingOutcome;
131
+ }
132
+
133
+ const userMessage: UserModelMessage =
134
+ typeof message === 'string'
135
+ ? { role: 'user', content: message }
136
+ : message;
137
+
138
+ if (userMessage.role !== 'user') {
139
+ throw new InvalidArgumentError({
140
+ parameter: 'message',
141
+ value: message,
142
+ message: 'Steering messages must be user messages.',
143
+ });
144
+ }
145
+
146
+ const outcome =
147
+ this.state === 'ended'
148
+ ? Promise.resolve<SteeringOutcome>({
149
+ type: 'not-delivered',
150
+ id,
151
+ message: userMessage,
152
+ reason: 'finished',
153
+ })
154
+ : new Promise<SteeringOutcome>(resolve => {
155
+ this.pending.push({ id, message: userMessage, resolve });
156
+ });
157
+
158
+ this.outcomes.set(id, outcome);
159
+
160
+ return outcome;
161
+ }
162
+
163
+ [steeringRunStarter](): SteeringRun {
164
+ if (this.state !== 'idle') {
165
+ throw new InvalidArgumentError({
166
+ parameter: 'experimental_steering',
167
+ value: this,
168
+ message:
169
+ 'The steering object was already used by a generation. Create a new one with experimental_createSteering() for each call.',
170
+ });
171
+ }
172
+
173
+ this.state = 'running';
174
+
175
+ return {
176
+ hasPendingMessages: () =>
177
+ this.state === 'running' && this.pending.length > 0,
178
+
179
+ takeMessages: () => {
180
+ if (this.state !== 'running') {
181
+ return [];
182
+ }
183
+
184
+ this.taken = [...this.taken, ...this.pending];
185
+ this.pending = [];
186
+
187
+ return this.taken.map(({ message }) => message);
188
+ },
189
+
190
+ resolveTakenMessages: ({ stepNumber, sentMessages }) => {
191
+ const taken = this.taken;
192
+ this.taken = [];
193
+
194
+ const delivered: Array<UserModelMessage> = [];
195
+
196
+ for (const { id, message, resolve } of taken) {
197
+ if (isSent({ message, sentMessages })) {
198
+ delivered.push(message);
199
+ resolve({ type: 'delivered', id, message, stepNumber });
200
+ } else {
201
+ resolve({
202
+ type: 'not-delivered',
203
+ id,
204
+ message,
205
+ reason: 'removed-by-prepare-step',
206
+ });
207
+ }
208
+ }
209
+
210
+ return delivered;
211
+ },
212
+
213
+ end: reason => {
214
+ if (this.state !== 'running') {
215
+ return;
216
+ }
217
+
218
+ const notDelivered = [...this.taken, ...this.pending];
219
+ this.taken = [];
220
+ this.pending = [];
221
+ this.state = 'ended';
222
+
223
+ for (const { id, message, resolve } of notDelivered) {
224
+ resolve({ type: 'not-delivered', id, message, reason });
225
+ }
226
+ },
227
+ };
228
+ }
229
+ }
230
+
231
+ // prepareStep can map messages to new objects, e.g. to add provider options,
232
+ // so a message also counts as sent when a user message has the same content:
233
+ function isSent({
234
+ message,
235
+ sentMessages,
236
+ }: {
237
+ message: UserModelMessage;
238
+ sentMessages: ReadonlyArray<ModelMessage>;
239
+ }): boolean {
240
+ const content = JSON.stringify(message.content);
241
+
242
+ return sentMessages.some(
243
+ sentMessage =>
244
+ sentMessage === message ||
245
+ (sentMessage.role === 'user' &&
246
+ JSON.stringify(sentMessage.content) === content),
247
+ );
248
+ }
249
+
250
+ /**
251
+ * Creates a steering object that sends user messages to a running generation.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * const steering = experimental_createSteering();
256
+ *
257
+ * const result = streamText({
258
+ * model,
259
+ * tools,
260
+ * stopWhen: isStepCount(20),
261
+ * experimental_steering: steering,
262
+ * });
263
+ *
264
+ * const outcome = await steering.steer('Use Paris, Texas instead.');
265
+ * ```
266
+ */
267
+ export function createSteering(): Steering {
268
+ return new DefaultSteering();
269
+ }
270
+
271
+ /**
272
+ * Starts a run for a steering object that was passed to a generation.
273
+ */
274
+ export function startSteeringRun(
275
+ steering: Steering | undefined,
276
+ ): SteeringRun | undefined {
277
+ if (steering == null) {
278
+ return undefined;
279
+ }
280
+
281
+ const start = (steering as Partial<DefaultSteering>)[steeringRunStarter];
282
+
283
+ if (typeof start !== 'function') {
284
+ throw new InvalidArgumentError({
285
+ parameter: 'experimental_steering',
286
+ value: steering,
287
+ message:
288
+ 'The steering object must be created with experimental_createSteering().',
289
+ });
290
+ }
291
+
292
+ return start.call(steering);
293
+ }
@@ -4,6 +4,7 @@ import type {
4
4
  ReasoningFilePart,
5
5
  ReasoningPart,
6
6
  ToolSet,
7
+ UserModelMessage,
7
8
  } from '@ai-sdk/provider-utils';
8
9
  import type {
9
10
  CallWarning,
@@ -275,6 +276,13 @@ export type StepResult<
275
276
  * results that can be fully encapsulated in the provider.
276
277
  */
277
278
  readonly providerMetadata: ProviderMetadata | undefined;
279
+
280
+ /**
281
+ * The user messages that were sent with `experimental_steering` and added
282
+ * to the input of this step. Undefined when the step received no steering
283
+ * messages.
284
+ */
285
+ readonly experimental_steeringMessages?: Array<UserModelMessage>;
278
286
  };
279
287
 
280
288
  export class DefaultStepResult<
@@ -301,6 +309,10 @@ export class DefaultStepResult<
301
309
  TOOLS,
302
310
  RUNTIME_CONTEXT
303
311
  >['providerMetadata'];
312
+ declare readonly experimental_steeringMessages?: StepResult<
313
+ TOOLS,
314
+ RUNTIME_CONTEXT
315
+ >['experimental_steeringMessages'];
304
316
 
305
317
  constructor({
306
318
  callId,
@@ -318,6 +330,7 @@ export class DefaultStepResult<
318
330
  request,
319
331
  response,
320
332
  providerMetadata,
333
+ steeringMessages,
321
334
  }: {
322
335
  callId: StepResult<TOOLS, RUNTIME_CONTEXT>['callId'];
323
336
  stepNumber: StepResult<TOOLS, RUNTIME_CONTEXT>['stepNumber'];
@@ -334,6 +347,7 @@ export class DefaultStepResult<
334
347
  request: StepResult<TOOLS, RUNTIME_CONTEXT>['request'];
335
348
  response: StepResult<TOOLS, RUNTIME_CONTEXT>['response'];
336
349
  providerMetadata: StepResult<TOOLS, RUNTIME_CONTEXT>['providerMetadata'];
350
+ steeringMessages?: Array<UserModelMessage>;
337
351
  }) {
338
352
  this.callId = callId;
339
353
  this.stepNumber = stepNumber;
@@ -349,6 +363,10 @@ export class DefaultStepResult<
349
363
  this.request = request;
350
364
  this.response = response;
351
365
  this.providerMetadata = providerMetadata;
366
+
367
+ if (steeringMessages != null && steeringMessages.length > 0) {
368
+ this.experimental_steeringMessages = steeringMessages;
369
+ }
352
370
  }
353
371
 
354
372
  get text() {