toAiMessages
Convert Chat SDK messages to AI SDK conversation format.
Convert an array of Message objects into the { role, content }[] format expected by AI SDKs. The output is structurally compatible with AI SDK's ModelMessage[].
import { toAiMessages } from "chat";Usage
import { toAiMessages } from "chat";
bot.onSubscribedMessage(async (thread, message) => {
const result = await thread.adapter.fetchMessages(thread.id, { limit: 20 });
const history = await toAiMessages(result.messages);
const response = await agent.stream({ prompt: history });
await thread.post(response.fullStream);
});Signature
function toAiMessages(
messages: Message[],
options?: ToAiMessagesOptions
): Promise<AiMessage[]>Parameters
Prop
Type
Options
Prop
Type
Returns
Promise<AiMessage[]> — an array of messages with role and content fields, directly assignable to AI SDK's ModelMessage[].
Behavior
- Role mapping —
author.isMe === truemaps to"assistant", all others to"user" - Filtering — Messages with empty or whitespace-only text are removed
- Sorting — Messages are sorted chronologically (oldest first) by
metadata.dateSent - Links — Link metadata (URL, title, description, site name) is appended to message content. Embedded message links are labeled as
[Embedded message: ...] - Attachments — Images and text files (JSON, XML, YAML, etc.) are included as multipart content using
fetchData(). Video and audio attachments triggeronUnsupportedAttachment
Return types
type AiMessage = AiUserMessage | AiAssistantMessage;
interface AiUserMessage {
role: "user";
content: string | AiMessagePart[];
}
interface AiAssistantMessage {
role: "assistant";
content: string;
}User messages have multipart content when attachments are present:
type AiMessagePart = AiTextPart | AiImagePart | AiFilePart;
interface AiTextPart {
type: "text";
text: string;
}
interface AiImagePart {
type: "image";
image: DataContent | URL;
mediaType?: string;
}
interface AiFilePart {
type: "file";
data: DataContent | URL;
filename?: string;
mediaType: string;
}Examples
Multi-user context
Prefix each user message with their username so the AI model can distinguish speakers:
const history = await toAiMessages(result.messages, { includeNames: true });
// [{ role: "user", content: "[alice]: Hello" },
// { role: "assistant", content: "Hi there!" },
// { role: "user", content: "[bob]: Thanks" }]Transforming messages
Replace raw user IDs with readable names:
const history = await toAiMessages(result.messages, {
transformMessage: (aiMessage) => {
if (typeof aiMessage.content === "string") {
return {
...aiMessage,
content: aiMessage.content.replace(/<@U123>/g, "@VercelBot"),
};
}
return aiMessage;
},
});Filtering messages
Skip messages from a specific user:
const history = await toAiMessages(result.messages, {
transformMessage: (aiMessage, source) => {
if (source.author.userId === "U_NOISY_BOT") return null;
return aiMessage;
},
});Handling unsupported attachments
const history = await toAiMessages(result.messages, {
onUnsupportedAttachment: (attachment, message) => {
logger.warn(`Skipped ${attachment.type} attachment in message ${message.id}`);
},
});Supported attachment types
| Type | MIME types | Included as |
|---|---|---|
image | Any image MIME type | FilePart with base64 data |
file | text/*, application/json, application/xml, application/javascript, application/typescript, application/yaml, application/toml | FilePart with base64 data |
video | Any | Skipped (triggers onUnsupportedAttachment) |
audio | Any | Skipped (triggers onUnsupportedAttachment) |
file | Other (e.g. application/pdf) | Silently skipped |
Attachments require fetchData() to be available on the attachment object. Attachments without fetchData() are silently skipped.