ai 7.0.153 → 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.
- package/CHANGELOG.md +11 -0
- package/dist/index.d.ts +224 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +399 -96
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +52 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +26 -20
- package/dist/internal/index.js.map +1 -1
- package/docs/02-getting-started/02-nextjs-app-router.mdx +2 -2
- package/docs/02-getting-started/08-angular.mdx +327 -0
- package/docs/02-getting-started/{03-nextjs-pages-router.mdx → 10-nextjs-pages-router.mdx} +12 -20
- package/docs/02-getting-started/index.mdx +2 -0
- package/docs/03-agents/01-overview.mdx +1 -0
- package/docs/03-agents/04-loop-control.mdx +117 -0
- package/docs/03-agents/06-tool-approvals.mdx +1 -0
- package/docs/03-agents/09-chat-platforms.mdx +164 -0
- package/docs/03-agents/index.mdx +6 -0
- package/docs/03-ai-sdk-core/60-telemetry.mdx +16 -3
- package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +10 -0
- package/docs/04-ai-sdk-ui/21-transport.mdx +86 -0
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +46 -0
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +46 -0
- package/docs/07-reference/01-ai-sdk-core/16-tool-loop-agent.mdx +35 -0
- package/package.json +11 -11
- package/src/agent/create-agent-ui-stream-response.ts +3 -0
- package/src/agent/create-agent-ui-stream.ts +16 -0
- package/src/agent/pipe-agent-ui-stream-to-response.ts +3 -0
- package/src/agent/tool-loop-agent-settings.ts +37 -1
- package/src/agent/tool-loop-agent.ts +43 -7
- package/src/generate-text/generate-text-events.ts +37 -0
- package/src/generate-text/generate-text.ts +521 -373
- package/src/generate-text/index.ts +8 -0
- package/src/generate-text/prepare-step.ts +12 -0
- package/src/generate-text/restricted-telemetry-dispatcher.ts +31 -18
- package/src/generate-text/steering.ts +293 -0
- package/src/generate-text/step-result.ts +18 -0
- package/src/generate-text/stream-text.ts +664 -473
- package/src/telemetry/create-telemetry-dispatcher.ts +3 -0
- package/src/telemetry/telemetry.ts +15 -1
- package/src/util/is-caused-by-abort.ts +20 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Chat Platforms
|
|
3
|
+
description: Run AI SDK agents in Slack, Microsoft Teams, Discord, and other chat platforms with Chat SDK.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Chat Platforms
|
|
7
|
+
|
|
8
|
+
[Chat SDK](https://chat-sdk.dev) is a TypeScript SDK for building bots that run on Slack, Microsoft Teams, Google Chat, Discord, Telegram, GitHub, Linear, WhatsApp, and more. You write your bot logic once, and platform adapters handle webhook verification, message parsing, and native formatting for each platform.
|
|
9
|
+
|
|
10
|
+
Chat SDK works with the AI SDK out of the box:
|
|
11
|
+
|
|
12
|
+
- `thread.post()` accepts an agent's `fullStream` and streams the reply into the conversation.
|
|
13
|
+
- `toAiMessages` from `chat/ai` converts a thread's messages into AI SDK `ModelMessage[]`.
|
|
14
|
+
- `createChatTools` from `chat/ai` gives an agent tools for reading and acting in the conversation.
|
|
15
|
+
|
|
16
|
+
This page shows how to connect a [`ToolLoopAgent`](/docs/agents/building-agents) to Slack. The same code works on every platform Chat SDK supports.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Install Chat SDK, a platform adapter, and a state adapter. The state adapter stores thread subscriptions and locks. This example uses Slack and Redis:
|
|
21
|
+
|
|
22
|
+
<InstallPackages packages="ai chat @chat-adapter/slack @chat-adapter/state-redis" />
|
|
23
|
+
|
|
24
|
+
The Slack adapter reads `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET` from the environment, and the Redis state adapter reads `REDIS_URL`. See the [Slack adapter docs](https://chat-sdk.dev/adapters/official/slack) for the Slack app manifest. The examples on this page set `agentView: true` to match a manifest that uses Slack's Agent messaging experience (`agent_view`).
|
|
25
|
+
|
|
26
|
+
## Create a Bot
|
|
27
|
+
|
|
28
|
+
Create a `Chat` instance and an agent. When someone mentions the bot, subscribe to the thread and stream the agent's reply into it:
|
|
29
|
+
|
|
30
|
+
```ts filename="lib/bot.ts"
|
|
31
|
+
import { Chat } from 'chat';
|
|
32
|
+
import { createSlackAdapter } from '@chat-adapter/slack';
|
|
33
|
+
import { createRedisState } from '@chat-adapter/state-redis';
|
|
34
|
+
import { ToolLoopAgent } from 'ai';
|
|
35
|
+
|
|
36
|
+
export const bot = new Chat({
|
|
37
|
+
userName: 'mybot',
|
|
38
|
+
adapters: {
|
|
39
|
+
slack: createSlackAdapter({ agentView: true }),
|
|
40
|
+
},
|
|
41
|
+
state: createRedisState(),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const agent = new ToolLoopAgent({
|
|
45
|
+
model: 'anthropic/claude-sonnet-5.5',
|
|
46
|
+
instructions:
|
|
47
|
+
'You are a helpful assistant in a team chat. Keep replies short.',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
bot.onNewMention(async (thread, message) => {
|
|
51
|
+
await thread.subscribe();
|
|
52
|
+
|
|
53
|
+
const result = await agent.stream({
|
|
54
|
+
prompt: message.text,
|
|
55
|
+
abortSignal: thread.signal,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await thread.post(result.fullStream);
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`thread.signal` is aborted when the platform cancels the turn (for example, when a user clicks Slack's stop button), so passing it as `abortSignal` stops generation too.
|
|
63
|
+
|
|
64
|
+
Then route the platform's webhooks to the bot:
|
|
65
|
+
|
|
66
|
+
```ts filename="app/api/webhooks/slack/route.ts"
|
|
67
|
+
import { after } from 'next/server';
|
|
68
|
+
import { bot } from '@/lib/bot';
|
|
69
|
+
|
|
70
|
+
export async function POST(request: Request): Promise<Response> {
|
|
71
|
+
return bot.webhooks.slack(request, {
|
|
72
|
+
waitUntil: task => after(() => task),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`waitUntil` keeps the function running after the webhook responds, so the agent can finish streaming its reply.
|
|
78
|
+
|
|
79
|
+
### Why `fullStream`?
|
|
80
|
+
|
|
81
|
+
Pass `fullStream` instead of `textStream`. When an agent calls tools between text steps, `textStream` joins the text from each step without separators. `fullStream` includes `finish-step` events, which Chat SDK uses to add paragraph breaks between steps.
|
|
82
|
+
|
|
83
|
+
## Use Thread History
|
|
84
|
+
|
|
85
|
+
After `thread.subscribe()`, later messages in the thread go to `onSubscribedMessage`. Load the thread history and convert it with `toAiMessages` to give the agent the full conversation:
|
|
86
|
+
|
|
87
|
+
```ts filename="lib/bot.ts"
|
|
88
|
+
import { toAiMessages } from 'chat/ai';
|
|
89
|
+
|
|
90
|
+
bot.onSubscribedMessage(async thread => {
|
|
91
|
+
const { messages } = await bot.history.thread.list(thread.id, {
|
|
92
|
+
limit: 20,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const result = await agent.stream({
|
|
96
|
+
messages: await toAiMessages(messages),
|
|
97
|
+
abortSignal: thread.signal,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await thread.post(result.fullStream);
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`toAiMessages` maps the bot's own messages to `assistant` and all others to `user`, sorts them oldest first, and includes supported attachments such as images and text files. Pass `{ includeNames: true }` to prefix each user message with the sender's name in multi-user threads.
|
|
105
|
+
|
|
106
|
+
## Give the Agent Chat Tools
|
|
107
|
+
|
|
108
|
+
`createChatTools` returns AI SDK tools that let the agent read and act in the chat: fetch messages, look up channels, post, react, and more. Use a preset to limit the tools to what the agent needs:
|
|
109
|
+
|
|
110
|
+
```ts filename="lib/bot.ts"
|
|
111
|
+
import { ToolLoopAgent } from 'ai';
|
|
112
|
+
import { createChatTools } from 'chat/ai';
|
|
113
|
+
|
|
114
|
+
const agent = new ToolLoopAgent({
|
|
115
|
+
model: 'anthropic/claude-sonnet-5.5',
|
|
116
|
+
instructions:
|
|
117
|
+
'You are a helpful assistant in a team chat. You can read messages in the current channel.',
|
|
118
|
+
tools: createChatTools({ chat: bot, preset: 'reader' }),
|
|
119
|
+
toolApproval: {
|
|
120
|
+
getUser: {
|
|
121
|
+
type: 'denied',
|
|
122
|
+
reason: 'Profile lookups are disabled for this bot',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The presets are `reader` (read-only), `messenger` (read, post, DM, and react), and `moderator` (all tools, including edit and delete).
|
|
129
|
+
|
|
130
|
+
When a tool runs inside a Chat SDK handler, it is limited to the conversation being handled. This stops the model from reading or posting in other channels the bot can access. See [Limiting what the agent can reach](https://chat-sdk.dev/docs/ai/ai-sdk-tools#limiting-what-the-agent-can-reach) for the `scope` and `strictScope` options.
|
|
131
|
+
|
|
132
|
+
Chat SDK marks write tools and `getUser` as needing approval by default. A `toolApproval` rule on the agent takes precedence over those defaults, so you can approve or deny calls automatically when your bot has no approval UI. The example above denies `getUser` profile lookups. See [Tool Approvals](/docs/agents/tool-approvals) for all approval options.
|
|
133
|
+
|
|
134
|
+
## Add More Platforms
|
|
135
|
+
|
|
136
|
+
Register more adapters to run the same handlers on other platforms:
|
|
137
|
+
|
|
138
|
+
```ts filename="lib/bot.ts"
|
|
139
|
+
import { Chat } from 'chat';
|
|
140
|
+
import { createSlackAdapter } from '@chat-adapter/slack';
|
|
141
|
+
import { createTeamsAdapter } from '@chat-adapter/teams';
|
|
142
|
+
import { createDiscordAdapter } from '@chat-adapter/discord';
|
|
143
|
+
import { createRedisState } from '@chat-adapter/state-redis';
|
|
144
|
+
|
|
145
|
+
export const bot = new Chat({
|
|
146
|
+
userName: 'mybot',
|
|
147
|
+
adapters: {
|
|
148
|
+
slack: createSlackAdapter({ agentView: true }),
|
|
149
|
+
teams: createTeamsAdapter(),
|
|
150
|
+
discord: createDiscordAdapter(),
|
|
151
|
+
},
|
|
152
|
+
state: createRedisState(),
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Each adapter gets its own webhook route, such as `bot.webhooks.teams`. Chat SDK normalizes messages and threads across platforms, so your handlers and agent stay the same.
|
|
157
|
+
|
|
158
|
+
To serve a browser chat from the same bot, use the Chat SDK Web adapter with `useChat`. See [Chat SDK Web Adapter](/docs/ai-sdk-ui/transport#chat-sdk-web-adapter).
|
|
159
|
+
|
|
160
|
+
## Next Steps
|
|
161
|
+
|
|
162
|
+
- **[Slackbot Agent Guide](/cookbook/guides/slackbot)** - Build and deploy a Slack agent with tools step by step
|
|
163
|
+
- **[Tool Approvals](/docs/agents/tool-approvals)** - Approve or deny tool calls automatically or with a human in the loop
|
|
164
|
+
- **[Chat SDK documentation](https://chat-sdk.dev/docs)** - Adapters, cards, modals, slash commands, and more
|
package/docs/03-agents/index.mdx
CHANGED
|
@@ -68,5 +68,11 @@ The following section shows you how to build agents with the AI SDK - systems wh
|
|
|
68
68
|
'Run ToolLoopAgent instances in an interactive terminal interface.',
|
|
69
69
|
href: '/docs/agents/terminal-ui',
|
|
70
70
|
},
|
|
71
|
+
{
|
|
72
|
+
title: 'Chat Platforms',
|
|
73
|
+
description:
|
|
74
|
+
'Run agents in Slack, Microsoft Teams, Discord, and more with Chat SDK.',
|
|
75
|
+
href: '/docs/agents/chat-platforms',
|
|
76
|
+
},
|
|
71
77
|
]}
|
|
72
78
|
/>
|
|
@@ -319,14 +319,21 @@ context across provider calls and tool executions.
|
|
|
319
319
|
import { tracingChannel } from 'node:diagnostics_channel';
|
|
320
320
|
import {
|
|
321
321
|
AI_SDK_TELEMETRY_TRACING_CHANNEL,
|
|
322
|
+
type InferTelemetryEvent,
|
|
322
323
|
type TelemetryTracingChannelMessage,
|
|
323
324
|
} from 'ai';
|
|
324
325
|
|
|
325
326
|
tracingChannel(AI_SDK_TELEMETRY_TRACING_CHANNEL).subscribe({
|
|
326
327
|
start(message) {
|
|
327
|
-
const telemetryMessage = message as TelemetryTracingChannelMessage
|
|
328
|
-
|
|
329
|
-
|
|
328
|
+
const telemetryMessage = message as TelemetryTracingChannelMessage<
|
|
329
|
+
InferTelemetryEvent<unknown>
|
|
330
|
+
>;
|
|
331
|
+
|
|
332
|
+
if (
|
|
333
|
+
telemetryMessage.type === 'generateText' ||
|
|
334
|
+
telemetryMessage.type === 'streamText'
|
|
335
|
+
) {
|
|
336
|
+
console.log('AI SDK version:', telemetryMessage.event.aiVersion);
|
|
330
337
|
// Inspect telemetryMessage.event and forward it to your provider.
|
|
331
338
|
}
|
|
332
339
|
},
|
|
@@ -431,6 +438,12 @@ export function myIntegration(): Telemetry {
|
|
|
431
438
|
type: '(event: GenerateTextStepEndEvent) => void | PromiseLike<void>',
|
|
432
439
|
description: 'Called when a step (LLM call) completes.',
|
|
433
440
|
},
|
|
441
|
+
{
|
|
442
|
+
name: 'onStepError',
|
|
443
|
+
type: '(event: GenerateTextStepErrorEvent) => void | PromiseLike<void>',
|
|
444
|
+
description:
|
|
445
|
+
'Called when a step that started fails before it produces a step result. Use it to close step-level spans. The call then ends with `onError`.',
|
|
446
|
+
},
|
|
434
447
|
{
|
|
435
448
|
name: 'onEmbedEnd',
|
|
436
449
|
type: '(event: EmbeddingModelCallEndEvent) => void | PromiseLike<void>',
|
|
@@ -299,6 +299,10 @@ A multi-step generation with local tool execution usually runs like this:
|
|
|
299
299
|
`onLanguageModelCallStart` and `onLanguageModelCallEnd` describe only the model call inside that step.
|
|
300
300
|
This distinction matters when the step includes local tool execution: the step duration can be longer than the model response duration.
|
|
301
301
|
|
|
302
|
+
When a step that started fails before it produces a step result, `onStepError` is called instead of `onStepEnd`. This happens when the provider call throws or rejects, or when the model stream ends without output. A provider `error` chunk does not fail the step: the step still ends with `onStepEnd` and `finishReason: 'error'`.
|
|
303
|
+
|
|
304
|
+
Aborts and timeouts end the step differently in each function. In `generateText` and `agent.generate()`, they end the step with `onStepError`. Its `error` is the abort or timeout error, or a provider error that has it as its `cause`. In `streamText` and `agent.stream()`, they end the step with `onAbort`.
|
|
305
|
+
|
|
302
306
|
## Runtime and Tool Context
|
|
303
307
|
|
|
304
308
|
Generation and step lifecycle callbacks receive the full `runtimeContext` and
|
|
@@ -403,6 +407,12 @@ const result = await generateText({
|
|
|
403
407
|
description:
|
|
404
408
|
'Called after each generation step completes. Receives the step result, including usage and performance.',
|
|
405
409
|
},
|
|
410
|
+
{
|
|
411
|
+
name: 'onStepError',
|
|
412
|
+
type: '(event: GenerateTextStepErrorEvent) => void | Promise<void>',
|
|
413
|
+
description:
|
|
414
|
+
'Called when a step that started fails before it produces a step result: the provider call throws or rejects, or the model stream ends without output. Receives the `onStepStart` event of the failed step plus `error`. In `generateText`, also called on abort or timeout. In `streamText`, aborts and timeouts call `onAbort` instead.',
|
|
415
|
+
},
|
|
406
416
|
{
|
|
407
417
|
name: 'onEnd',
|
|
408
418
|
type: '(event: GenerateTextEndEvent) => void | Promise<void>',
|
|
@@ -271,6 +271,92 @@ WorkflowAgent guide.
|
|
|
271
271
|
|
|
272
272
|
For the full API reference, see [`WorkflowChatTransport`](/docs/reference/ai-sdk-workflow/workflow-chat-transport). For server-side endpoint setup, see the [WorkflowAgent guide](/docs/agents/workflow-agent#resumable-streaming-with-workflowchattransport).
|
|
273
273
|
|
|
274
|
+
## Chat SDK Web Adapter
|
|
275
|
+
|
|
276
|
+
The [Chat SDK](https://chat-sdk.dev) Web adapter (`@chat-adapter/web`) serves a `useChat` endpoint from a Chat SDK bot. It speaks the [UI message stream protocol](/docs/ai-sdk-ui/stream-protocol), so the default transport works without changes. Use it when the same bot also runs on Slack, Microsoft Teams, Discord, or other [chat platforms](/docs/agents/chat-platforms), and you want one set of handlers for all of them.
|
|
277
|
+
|
|
278
|
+
<InstallPackages packages="ai @ai-sdk/react chat @chat-adapter/web @chat-adapter/state-redis" />
|
|
279
|
+
|
|
280
|
+
On the server, register the Web adapter and handle messages with `onDirectMessage`. Each browser conversation is a direct message thread:
|
|
281
|
+
|
|
282
|
+
```ts filename="lib/bot.ts"
|
|
283
|
+
import { Chat } from 'chat';
|
|
284
|
+
import { toAiMessages } from 'chat/ai';
|
|
285
|
+
import { createWebAdapter } from '@chat-adapter/web';
|
|
286
|
+
import { createRedisState } from '@chat-adapter/state-redis';
|
|
287
|
+
import { ToolLoopAgent } from 'ai';
|
|
288
|
+
import { getSession } from './session'; // your app's auth
|
|
289
|
+
|
|
290
|
+
const agent = new ToolLoopAgent({
|
|
291
|
+
model: 'anthropic/claude-sonnet-5.5',
|
|
292
|
+
instructions: 'You are a helpful assistant.',
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
export const bot = new Chat({
|
|
296
|
+
userName: 'mybot',
|
|
297
|
+
adapters: {
|
|
298
|
+
web: createWebAdapter({
|
|
299
|
+
userName: 'mybot',
|
|
300
|
+
// Identify the caller from your app's session. Returning null responds with 401.
|
|
301
|
+
getUser: async () => {
|
|
302
|
+
const session = await getSession();
|
|
303
|
+
return session ? { id: session.userId } : null;
|
|
304
|
+
},
|
|
305
|
+
}),
|
|
306
|
+
},
|
|
307
|
+
state: createRedisState(),
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
bot.onDirectMessage(async thread => {
|
|
311
|
+
const { messages } = await bot.history.thread.list(thread.id, {
|
|
312
|
+
limit: 20,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const result = await agent.stream({
|
|
316
|
+
messages: await toAiMessages(messages),
|
|
317
|
+
abortSignal: thread.signal,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
await thread.post(result.fullStream);
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
```ts filename="app/api/chat/route.ts"
|
|
325
|
+
import { after } from 'next/server';
|
|
326
|
+
import { bot } from '@/lib/bot';
|
|
327
|
+
|
|
328
|
+
export async function POST(request: Request): Promise<Response> {
|
|
329
|
+
return bot.webhooks.web(request, {
|
|
330
|
+
waitUntil: task => after(() => task),
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
On the client, point the default transport at the route:
|
|
336
|
+
|
|
337
|
+
```tsx filename="app/page.tsx"
|
|
338
|
+
'use client';
|
|
339
|
+
|
|
340
|
+
import { useChat } from '@ai-sdk/react';
|
|
341
|
+
import { DefaultChatTransport } from 'ai';
|
|
342
|
+
|
|
343
|
+
export default function Chat() {
|
|
344
|
+
const { messages, sendMessage } = useChat({
|
|
345
|
+
transport: new DefaultChatTransport({ api: '/api/chat' }),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// ... render chat UI
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Things to know:
|
|
353
|
+
|
|
354
|
+
- **Authentication**: browser requests have no platform signature, so `getUser` is the security boundary. Resolve the user from your app's session and return `null` to reject the request.
|
|
355
|
+
- **Conversation history**: the adapter ignores the message history in the request body. It stores each conversation's messages in the state adapter, and `bot.history.thread.list()` reads them back. The chat `id` from `useChat` identifies the conversation.
|
|
356
|
+
- **Stopping**: the adapter honors the request's abort signal, so calling `stop()` in `useChat` stops the handler's stream.
|
|
357
|
+
|
|
358
|
+
For all options, including the `ag-ui` protocol and `useChat` wrappers for React, Vue, and Svelte, see the [Web adapter docs](https://chat-sdk.dev/adapters/official/web).
|
|
359
|
+
|
|
274
360
|
## Building Custom Transports
|
|
275
361
|
|
|
276
362
|
To understand how to build your own transport, refer to the source code of the default implementation:
|
|
@@ -658,6 +658,13 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
658
658
|
description:
|
|
659
659
|
'The messages that will be sent to the model for the current step. If prepareStep returns a messages override, those messages carry forward to later steps.',
|
|
660
660
|
},
|
|
661
|
+
{
|
|
662
|
+
name: 'experimental_steeringMessages',
|
|
663
|
+
type: 'Array<UserModelMessage> | undefined',
|
|
664
|
+
isOptional: true,
|
|
665
|
+
description:
|
|
666
|
+
'The user messages that were sent with `experimental_steering` and added to `messages` for the current step. Undefined when the step received no steering messages. `messages` already contains them. If you return a `messages` override that is rebuilt from other fields, add them back together with the steering messages of previous steps from `steps`.',
|
|
667
|
+
},
|
|
661
668
|
{
|
|
662
669
|
name: 'runtimeContext',
|
|
663
670
|
type: 'CONTEXT',
|
|
@@ -845,6 +852,13 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
845
852
|
description:
|
|
846
853
|
'Experimental sandbox environment that is passed through to `prepareStep`, tool description functions, and tool execution. Tools can access it from their description function options and execution options.',
|
|
847
854
|
},
|
|
855
|
+
{
|
|
856
|
+
name: 'experimental_steering',
|
|
857
|
+
type: 'Experimental_Steering',
|
|
858
|
+
isOptional: true,
|
|
859
|
+
description:
|
|
860
|
+
"Experimental steering object created with `experimental_createSteering()`. Messages sent with `steering.steer()` are added as user messages at the next step boundary, after the current step and its tool calls are done. A waiting message keeps the loop going for another step unless a stop condition is met. `steer()` resolves with `{ type: 'delivered', id, message, stepNumber }` or `{ type: 'not-delivered', id, message, reason }`. See [Steering](/docs/agents/loop-control#steering).",
|
|
861
|
+
},
|
|
848
862
|
{
|
|
849
863
|
name: 'experimental_download',
|
|
850
864
|
type: '(requestedDownloads: Array<{ url: URL; isUrlSupportedByModel: boolean }>) => Promise<Array<null | { data: Uint8Array; mediaType?: string }>>',
|
|
@@ -1999,6 +2013,31 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
1999
2013
|
},
|
|
2000
2014
|
],
|
|
2001
2015
|
},
|
|
2016
|
+
{
|
|
2017
|
+
name: 'onStepError',
|
|
2018
|
+
type: '(event: GenerateTextStepErrorEvent) => Promise<void> | void',
|
|
2019
|
+
isOptional: true,
|
|
2020
|
+
description:
|
|
2021
|
+
'Callback that is called when a step that started fails before it produces a step result, i.e. when the provider call throws or rejects, or when the call is aborted or times out. `onStepEnd` is not called for such a step. For aborts and timeouts, `error` is the abort or timeout error, or a provider error that has it as its `cause`.',
|
|
2022
|
+
properties: [
|
|
2023
|
+
{
|
|
2024
|
+
type: 'GenerateTextStepErrorEvent',
|
|
2025
|
+
parameters: [
|
|
2026
|
+
{
|
|
2027
|
+
name: '...GenerateTextStepStartEvent',
|
|
2028
|
+
type: 'GenerateTextStepStartEvent',
|
|
2029
|
+
description:
|
|
2030
|
+
'All properties of the `onStepStart` event of the step that failed, such as `callId`, `stepNumber`, `steps`, `messages`, `runtimeContext`, and `toolsContext`.',
|
|
2031
|
+
},
|
|
2032
|
+
{
|
|
2033
|
+
name: 'error',
|
|
2034
|
+
type: 'unknown',
|
|
2035
|
+
description: 'The error that ended the step.',
|
|
2036
|
+
},
|
|
2037
|
+
],
|
|
2038
|
+
},
|
|
2039
|
+
],
|
|
2040
|
+
},
|
|
2002
2041
|
{
|
|
2003
2042
|
name: 'onStepFinish',
|
|
2004
2043
|
type: 'GenerateTextOnStepFinishCallback<TOOLS>',
|
|
@@ -3226,6 +3265,13 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
3226
3265
|
description:
|
|
3227
3266
|
'Additional provider-specific metadata. They are passed through from the provider to the AI SDK and enable provider-specific results that can be fully encapsulated in the provider.',
|
|
3228
3267
|
},
|
|
3268
|
+
{
|
|
3269
|
+
name: 'experimental_steeringMessages',
|
|
3270
|
+
type: 'Array<UserModelMessage> | undefined',
|
|
3271
|
+
isOptional: true,
|
|
3272
|
+
description:
|
|
3273
|
+
'The user messages that were sent with `experimental_steering` and added to the input of this step. Undefined when the step received no steering messages.',
|
|
3274
|
+
},
|
|
3229
3275
|
],
|
|
3230
3276
|
},
|
|
3231
3277
|
],
|
|
@@ -715,6 +715,13 @@ retain the content their contracts require.
|
|
|
715
715
|
description:
|
|
716
716
|
'The messages that will be sent to the model for the current step. If prepareStep returns a messages override, those messages carry forward to later steps.',
|
|
717
717
|
},
|
|
718
|
+
{
|
|
719
|
+
name: 'experimental_steeringMessages',
|
|
720
|
+
type: 'Array<UserModelMessage> | undefined',
|
|
721
|
+
isOptional: true,
|
|
722
|
+
description:
|
|
723
|
+
'The user messages that were sent with `experimental_steering` and added to `messages` for the current step. Undefined when the step received no steering messages. `messages` already contains them. If you return a `messages` override that is rebuilt from other fields, add them back together with the steering messages of previous steps from `steps`.',
|
|
724
|
+
},
|
|
718
725
|
{
|
|
719
726
|
name: 'runtimeContext',
|
|
720
727
|
type: 'CONTEXT',
|
|
@@ -902,6 +909,13 @@ retain the content their contracts require.
|
|
|
902
909
|
description:
|
|
903
910
|
'Experimental sandbox environment that is passed through to `prepareStep`, tool description functions, and tool execution. Tools can access it from their description function options and execution options.',
|
|
904
911
|
},
|
|
912
|
+
{
|
|
913
|
+
name: 'experimental_steering',
|
|
914
|
+
type: 'Experimental_Steering',
|
|
915
|
+
isOptional: true,
|
|
916
|
+
description:
|
|
917
|
+
"Experimental steering object created with `experimental_createSteering()`. Messages sent with `steering.steer()` are added as user messages at the next step boundary, after the current step and its tool calls are done. A waiting message keeps the loop going for another step unless a stop condition is met. `steer()` resolves with `{ type: 'delivered', id, message, stepNumber }` or `{ type: 'not-delivered', id, message, reason }`. See [Steering](/docs/agents/loop-control#steering).",
|
|
918
|
+
},
|
|
905
919
|
{
|
|
906
920
|
name: 'experimental_download',
|
|
907
921
|
type: '(requestedDownloads: Array<{ url: URL; isUrlSupportedByModel: boolean }>) => Promise<Array<null | { data: Uint8Array; mediaType?: string }>>',
|
|
@@ -1727,6 +1741,31 @@ retain the content their contracts require.
|
|
|
1727
1741
|
},
|
|
1728
1742
|
],
|
|
1729
1743
|
},
|
|
1744
|
+
{
|
|
1745
|
+
name: 'onStepError',
|
|
1746
|
+
type: '(event: GenerateTextStepErrorEvent) => Promise<void> | void',
|
|
1747
|
+
isOptional: true,
|
|
1748
|
+
description:
|
|
1749
|
+
"Callback that is called when a step that started fails before it produces a step result, i.e. when the provider call throws or rejects, or when the model stream ends without output. `onStepEnd` is not called for such a step. A provider `error` chunk does not fail the step: it still ends with `onStepEnd` and `finishReason: 'error'`. Aborts and timeouts end the step with `onAbort` instead.",
|
|
1750
|
+
properties: [
|
|
1751
|
+
{
|
|
1752
|
+
type: 'GenerateTextStepErrorEvent',
|
|
1753
|
+
parameters: [
|
|
1754
|
+
{
|
|
1755
|
+
name: '...GenerateTextStepStartEvent',
|
|
1756
|
+
type: 'GenerateTextStepStartEvent',
|
|
1757
|
+
description:
|
|
1758
|
+
'All properties of the `onStepStart` event of the step that failed, such as `callId`, `stepNumber`, `steps`, `messages`, `runtimeContext`, and `toolsContext`.',
|
|
1759
|
+
},
|
|
1760
|
+
{
|
|
1761
|
+
name: 'error',
|
|
1762
|
+
type: 'unknown',
|
|
1763
|
+
description: 'The error that ended the step.',
|
|
1764
|
+
},
|
|
1765
|
+
],
|
|
1766
|
+
},
|
|
1767
|
+
],
|
|
1768
|
+
},
|
|
1730
1769
|
{
|
|
1731
1770
|
name: 'onStepFinish',
|
|
1732
1771
|
type: 'GenerateTextOnStepFinishCallback<TOOLS>',
|
|
@@ -3350,6 +3389,13 @@ retain the content their contracts require.
|
|
|
3350
3389
|
description:
|
|
3351
3390
|
'Optional metadata from the provider. The outer key is the provider name. The inner values are the metadata. Details depend on the provider.',
|
|
3352
3391
|
},
|
|
3392
|
+
{
|
|
3393
|
+
name: 'experimental_steeringMessages',
|
|
3394
|
+
type: 'Array<UserModelMessage> | undefined',
|
|
3395
|
+
isOptional: true,
|
|
3396
|
+
description:
|
|
3397
|
+
'The user messages that were sent with `experimental_steering` and added to the input of this step. Undefined when the step received no steering messages.',
|
|
3398
|
+
},
|
|
3353
3399
|
],
|
|
3354
3400
|
},
|
|
3355
3401
|
],
|
|
@@ -519,6 +519,27 @@ To see `ToolLoopAgent` in action, check out [these examples](#examples).
|
|
|
519
519
|
description:
|
|
520
520
|
'Callback invoked after each agent step (LLM/tool call) completes. If also specified in `generate()` or `stream()`, both callbacks are called (constructor first).',
|
|
521
521
|
},
|
|
522
|
+
{
|
|
523
|
+
name: 'onStepError',
|
|
524
|
+
type: '(event: GenerateTextStepErrorEvent) => Promise<void> | void',
|
|
525
|
+
isOptional: true,
|
|
526
|
+
description:
|
|
527
|
+
"Callback invoked when an agent step that started fails before it produces a step result, i.e. when the provider call throws or rejects, or when the model stream ends without output. `onStepEnd` is not called for such a step. A provider `error` chunk does not fail the step: it still ends with `onStepEnd` and `finishReason: 'error'`. The event contains all properties of the `onStepStart` event of the step that failed, plus `error`. In `generate()`, aborts and timeouts also end the step here. In `stream()`, they end the step with `onAbort` instead. Only available in the constructor.",
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
name: 'onError',
|
|
531
|
+
type: '(event: { error: unknown }) => Promise<void> | void',
|
|
532
|
+
isOptional: true,
|
|
533
|
+
description:
|
|
534
|
+
'Callback invoked when an error occurs. For `stream()` it receives the errors of the stream, for `generate()` it receives the error before it is thrown. Aborts and timeouts are not reported. When not set, `stream()` logs errors to the console. Only available in the constructor.',
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
name: 'onAbort',
|
|
538
|
+
type: '(event: GenerateTextAbortEvent) => Promise<void> | void',
|
|
539
|
+
isOptional: true,
|
|
540
|
+
description:
|
|
541
|
+
'Callback invoked when `stream()` is aborted or times out. Receives the steps that finished before the abort and the abort reason. `generate()` does not call it: an aborted or timed out `generate()` call rejects, and its step ends with `onStepError`. Only available in the constructor.',
|
|
542
|
+
},
|
|
522
543
|
{
|
|
523
544
|
name: 'onStepFinish',
|
|
524
545
|
type: 'GenerateTextOnStepFinishCallback',
|
|
@@ -742,6 +763,13 @@ const result = await agent.generate({
|
|
|
742
763
|
description:
|
|
743
764
|
'Experimental sandbox environment that is passed through to `prepareStep`, tool description functions, and tool execution. Tools can access it from their description function options and execution options.',
|
|
744
765
|
},
|
|
766
|
+
{
|
|
767
|
+
name: 'experimental_steering',
|
|
768
|
+
type: 'Experimental_Steering',
|
|
769
|
+
isOptional: true,
|
|
770
|
+
description:
|
|
771
|
+
"Experimental steering object created with `experimental_createSteering()`. Messages sent with `steering.steer()` are added as user messages at the next step boundary, after the current step and its tool calls are done. A waiting message keeps the loop going for another step unless a stop condition is met. `steer()` resolves with `{ type: 'delivered', id, message, stepNumber }` or `{ type: 'not-delivered', id, message, reason }`. See [Steering](/docs/agents/loop-control#steering).",
|
|
772
|
+
},
|
|
745
773
|
{
|
|
746
774
|
name: 'options',
|
|
747
775
|
type: 'CALL_OPTIONS',
|
|
@@ -858,6 +886,13 @@ for await (const chunk of stream.textStream) {
|
|
|
858
886
|
description:
|
|
859
887
|
'Experimental sandbox environment that is passed through to `prepareStep`, tool description functions, and tool execution. Tools can access it from their description function options and execution options.',
|
|
860
888
|
},
|
|
889
|
+
{
|
|
890
|
+
name: 'experimental_steering',
|
|
891
|
+
type: 'Experimental_Steering',
|
|
892
|
+
isOptional: true,
|
|
893
|
+
description:
|
|
894
|
+
"Experimental steering object created with `experimental_createSteering()`. Messages sent with `steering.steer()` are added as user messages at the next step boundary, after the current step and its tool calls are done. A waiting message keeps the loop going for another step unless a stop condition is met. `steer()` resolves with `{ type: 'delivered', id, message, stepNumber }` or `{ type: 'not-delivered', id, message, reason }`. See [Steering](/docs/agents/loop-control#steering).",
|
|
895
|
+
},
|
|
861
896
|
{
|
|
862
897
|
name: 'options',
|
|
863
898
|
type: 'CALL_OPTIONS',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.154",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -42,20 +42,20 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.127",
|
|
46
46
|
"@ai-sdk/provider": "4.0.34",
|
|
47
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.69"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@ai-sdk/amazon-bedrock": "5.0.
|
|
51
|
-
"@ai-sdk/deepseek": "3.0.
|
|
52
|
-
"@ai-sdk/google": "4.0.
|
|
53
|
-
"@ai-sdk/groq": "4.0.
|
|
54
|
-
"@ai-sdk/huggingface": "2.0.
|
|
55
|
-
"@ai-sdk/moonshotai": "3.0.
|
|
56
|
-
"@ai-sdk/openai": "4.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.125",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.74",
|
|
52
|
+
"@ai-sdk/google": "4.0.106",
|
|
53
|
+
"@ai-sdk/groq": "4.0.71",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.79",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.79",
|
|
56
|
+
"@ai-sdk/openai": "4.0.103",
|
|
57
57
|
"@ai-sdk/test-server": "2.0.4",
|
|
58
|
-
"@ai-sdk/xai": "5.0.
|
|
58
|
+
"@ai-sdk/xai": "5.0.31",
|
|
59
59
|
"@edge-runtime/vm": "^5.0.0",
|
|
60
60
|
"@smithy/eventstream-codec": "^4.3.3",
|
|
61
61
|
"@smithy/util-utf8": "^4.3.3",
|
|
@@ -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
|
});
|