Skip to content

Commit 0496388

Browse files
committed
docs for validating ui messages
1 parent 54aca86 commit 0496388

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

docs/ai-chat/backend.mdx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,39 @@ export const myChat = chat.agent({
148148
(per-message). See [Client data and metadata](/ai-chat/frontend#client-data-and-metadata).
149149
</Tip>
150150

151+
#### onValidateMessages
152+
153+
Validate or transform incoming `UIMessage[]` before they are converted to model messages. Fires once per turn with the raw messages from the wire payload (after cleanup of aborted tool parts), **before** accumulation and `toModelMessages()`.
154+
155+
Return the validated messages array. Throw to abort the turn with an error.
156+
157+
This is the right place to call the AI SDK's [`validateUIMessages`](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-message-persistence#validating-messages-on-the-server) to catch malformed messages from storage or untrusted input before they reach the model — especially useful when persisting conversations to a database, where tool schemas may drift between deploys.
158+
159+
| Field | Type | Description |
160+
| --------- | --------------------------------------------------------------- | ---------------------------------------- |
161+
| `messages` | `UIMessage[]` | Incoming UI messages for this turn |
162+
| `chatId` | `string` | Chat session ID |
163+
| `turn` | `number` | Turn number (0-indexed) |
164+
| `trigger` | `"submit-message" \| "regenerate-message" \| "preload" \| "close"` | The trigger type for this turn |
165+
166+
```ts
167+
import { validateUIMessages } from "ai";
168+
169+
export const myChat = chat.agent({
170+
id: "my-chat",
171+
onValidateMessages: async ({ messages }) => {
172+
return validateUIMessages({ messages, tools: chatTools });
173+
},
174+
run: async ({ messages, signal }) => {
175+
return streamText({ model: openai("gpt-4o"), messages, tools: chatTools, abortSignal: signal });
176+
},
177+
});
178+
```
179+
180+
<Note>
181+
`onValidateMessages` fires **before** `onTurnStart` and message accumulation. If you need to validate messages loaded from a database, do the loading in `onChatStart` or `onPreload` and let `onValidateMessages` validate the full incoming set each turn.
182+
</Note>
183+
151184
#### onTurnStart
152185

153186
Fires at the start of every turn, after message accumulation and `onChatStart` (turn 0), but **before** `run()` executes. Use it to persist messages before streaming begins — so a mid-stream page refresh still shows the user's message.

docs/ai-chat/reference.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Options for `chat.agent()`.
1515
| `clientDataSchema` | `TaskSchema` || Schema for validating and typing `clientData` |
1616
| `onPreload` | `(event: PreloadEvent) => Promise<void> \| void` || Fires on preloaded runs before the first message |
1717
| `onChatStart` | `(event: ChatStartEvent) => Promise<void> \| void` || Fires on turn 0 before `run()` |
18+
| `onValidateMessages` | `(event: ValidateMessagesEvent) => UIMessage[] \| Promise<UIMessage[]>` || Validate/transform UIMessages before model conversion. See [onValidateMessages](/ai-chat/backend#onvalidatemessages) |
1819
| `onTurnStart` | `(event: TurnStartEvent) => Promise<void> \| void` || Fires every turn before `run()` |
1920
| `onBeforeTurnComplete` | `(event: BeforeTurnCompleteEvent) => Promise<void> \| void` || Fires after response but before stream closes. Includes `writer`. |
2021
| `onTurnComplete` | `(event: TurnCompleteEvent) => Promise<void> \| void` || Fires after each turn completes (stream closed) |
@@ -39,6 +40,10 @@ Plus all standard [TaskOptions](/tasks/overview) — `retry`, `queue`, `machine`
3940

4041
All **`chat.agent`** lifecycle events (**`onPreload`**, **`onChatStart`**, **`onTurnStart`**, **`onBeforeTurnComplete`**, **`onTurnComplete`**, **`onCompacted`**) and the object passed to **`run`** include **`ctx`**: the same **`TaskRunContext`** shape as the `ctx` in `task({ run: (payload, { ctx }) => ... })`.
4142

43+
<Note>
44+
**`onValidateMessages`** does not include `ctx` — it fires before message accumulation and is designed for pure validation/transformation of incoming messages.
45+
</Note>
46+
4247
Use **`ctx`** for run metadata, tags, parent links, or any API that needs the full run record. The chat-specific string **`runId`** on events is always **`ctx.run.id`**; both are provided for convenience.
4348

4449
```ts
@@ -100,6 +105,17 @@ Passed to the `onChatStart` callback.
100105
| `preloaded` | `boolean` | Whether this run was preloaded before the first message |
101106
| `writer` | [`ChatWriter`](#chatwriter) | Stream writer for custom chunks. Lazy — no overhead if unused. |
102107

108+
## ValidateMessagesEvent
109+
110+
Passed to the `onValidateMessages` callback.
111+
112+
| Field | Type | Description |
113+
| --------- | --------------------------------------------------------------- | ---------------------------------------- |
114+
| `messages` | `UIMessage[]` | Incoming UI messages for this turn |
115+
| `chatId` | `string` | Chat session ID |
116+
| `turn` | `number` | Turn number (0-indexed) |
117+
| `trigger` | `"submit-message" \| "regenerate-message" \| "preload" \| "close"` | The trigger type for this turn |
118+
103119
## TurnStartEvent
104120

105121
Passed to the `onTurnStart` callback.

0 commit comments

Comments
 (0)