AI SDK

Stream from an AI SDK route with AISDKChat or AISDKThreads, and configure the transport, callbacks, and adapters.

@assistant-ui/ai-sdk runs AI SDK chat state inside the assistant client. Install react even in a Vue app because the runtime uses React hooks to host that state internally; Vue renders through @assistant-ui/vue. The quickstart covers installation and the first chat.

The server route

The default transport posts to /api/chat. This Nitro route reads the UI messages plus the system prompt and frontend tool schemas that the transport adds from model context. frontendTools turns those schemas into AI SDK tools without a server executor, because the browser runs them:

server/api/chat.post.ts
import { openai } from "@ai-sdk/openai";
import { frontendTools, type FrontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText, type UIMessage } from "ai";

export default defineEventHandler(async (event) => {
  const { messages, system, tools } = await readBody<{
    messages: UIMessage[];
    system?: string;
    tools?: FrontendTools;
  }>(event);

  const result = streamText({
    model: openai("gpt-6-luna"),
    messages: await convertToModelMessages(messages),
    system,
    tools: frontendTools(tools ?? {}),
  });

  return result.toUIMessageStreamResponse();
});

Tools the server owns go in the same tools map with their own execute; see Tools.

AISDKChat

AISDKChat() creates a single thread backed by an AI SDK Chat. Pass it as threads: AISDKChat(options) in AuiConfig. These options come from AI SDK ChatInit and the assistant-ui thread adapter:

PropTypeDefaultDescription
idstringGenerated idChat id, captured when the resource mounts.
messagesUIMessage[][]Initial AI SDK messages, read when the chat is created.
generateIdAI SDK IdGeneratorAI SDK generatorGenerates message ids in the chat.
messageMetadataSchemaAI SDK schemaNoneValidates streamed message metadata.
dataPartSchemasAI SDK data schemasNoneValidates streamed data parts.
transportAI SDK ChatTransportAssistantChatTransport()Sends and reconnects chat streams.
onErrorCallbackNoneReceives an AI SDK chat error.
onFinishCallbackNoneRuns when an assistant response finishes streaming.
onToolCallCallbackNoneReceives a streamed tool call for client handling.
onDataCallbackNoneReceives a streamed data part.
sendAutomaticallyWhenCallbackNoneDecides whether to send again after a stream or tool call.
throttlenumberNonePassed to AI SDK useChat for update throttling.
adaptersAISDKRuntimeAdapter["adapters"]Runtime defaultsSupplies history, suggestion, attachment, and other supported adapters.
suggestionsThreadSuggestion[]NoneSupplies thread suggestions.
isDisabledbooleanfalseDisables the thread and its composer input.
isSendDisabledbooleanfalseBlocks sends while leaving the input usable.
unstable_capabilities{ copy?: boolean }NoneOverrides the copy capability.
toCreateMessageCallbackRuntime conversionConverts a new assistant-ui message to an AI SDK UI message.
onResumeCallbackNoneHandles resumeRun through a custom replay channel.
onResumeToolCallCallbackNoneHandles resume for a tool call outside the in-process tracker.
onRespondToToolApprovalCallbackAI SDK responseRoutes tool approval answers through a host.
onResumeErrorCallbackNoneReceives a failed automatic resumable-stream reconnect.
joinStrategy"concat-content" | "none""concat-content"Merges consecutive assistant messages into one thread message, or keeps them separate with "none".
messageRepositoryExported message repositoryRuntime-owned repositorySupplies branch history.
unstable_onBranchChangeCallbackNoneReceives branch changes.

The chat captures its id, initial messages, id generator, and schemas when it mounts. Its callbacks read the latest options on later renders. The runtime also reads adapter and shared thread options again when it renders. Remount the assistant client to start a different chat id or initial history.

Custom adapters are merged over the defaults: the AI SDK attachment adapter, and the history, attachment, and feedback adapters that a cloud thread list provides. The thread list adapter receives the chat id as threadId unless your adapter sets its own value.

Point at another endpoint

Set api on AssistantChatTransport for another Nitro route. Its AI SDK transport options also accept headers, body, and credentials.

app/components/Assistant.client.vue
<script setup lang="ts">
import { AuiConfig, AuiProvider } from "@assistant-ui/vue";
import { AISDKChat, AssistantChatTransport } from "@assistant-ui/ai-sdk";
import Thread from "~/components/assistant-ui/thread.vue";

const config = AuiConfig({
  threads: AISDKChat({
    transport: new AssistantChatTransport({
      api: "/api/assistant-chat",
      headers: { "x-client": "nuxt" },
      body: { source: "docs" },
      credentials: "same-origin",
    }),
  }),
});
</script>

<template>
  <AuiProvider :config="config">
    <Thread />
  </AuiProvider>
</template>

What each request contains

AssistantChatTransport sends messages, trigger, messageId, and metadata from the AI SDK request. As id it sends the thread list item's remoteId once the item is initialized, which with cloud is the cloud thread id, and the AI SDK chat id otherwise. It adds callSettings, system, config, and JSON Schema tools from the thread's model context. Undefined values are omitted when JSON serializes the body.

The transport's body is merged with any body passed on the send. Those values override model context fields with the same names. The transport adds id, messages, trigger, messageId, and metadata last, so a custom body cannot override them. A custom prepareSendMessagesRequest can replace the prepared body entirely.

AISDKThreads

AISDKThreads() creates a thread list and a separate AI SDK chat for each thread. It accepts the shared chat options above except id and messages, plus these list options:

PropTypeDefaultDescription
transportChatTransport or () => ChatTransportNew AssistantChatTransport per threadA factory runs once for each thread. An AssistantChatTransport instance is cloned per thread; another instance is shared.
cloudAssistantCloudNoneBacks the list with cloud history instead of memory.
threadIdstringNoneControls the selected remote thread when cloud is set.
onThreadIdChangeCallbackNoneWith cloud, receives the selected thread's remote id after the user switches threads or a new thread is initialized, and undefined for a new thread that has none yet. A change made through threadId is not reported back.

Each thread captures its chat construction options when created, including its transport. Later option changes update callbacks but do not replace a chat's transport or initial state. Without cloud, the list and histories live in the assistant client's memory. See Cloud persistence and Thread list for list setup and controls.

Errors

onError receives AI SDK chat errors. When a stream fails after an assistant message exists, the converted message has status.type of "incomplete", status.reason of "error", and error details. Render that state with the error primitives.

Token usage

getThreadMessageTokenUsage is a plain function, so it works with Vue state. It reads usage on an assistant message from metadata.usage, metadata.custom.usage, or step metadata, and returns undefined when none is present. A route can attach usage through AI SDK stream message metadata.

app/components/TokenUsage.vue
<script setup lang="ts">
import { computed } from "vue";
import { getThreadMessageTokenUsage } from "@assistant-ui/ai-sdk";
import { useAuiState } from "@assistant-ui/vue";

const messages = useAuiState((s) => s.thread.messages);
const usage = computed(() => {
  for (let index = messages.value.length - 1; index >= 0; index--) {
    const value = getThreadMessageTokenUsage(messages.value[index]);
    if (value) return value;
  }
  return undefined;
});
</script>

<template>
  <span v-if="usage?.totalTokens !== undefined">
    {{ usage.totalTokens }} tokens
  </span>
</template>

React-only exports

useChatRuntime, useAISDKRuntime, useAISDKChat, useAISDKError, and useThreadTokenUsage are React hooks. Use AISDKChat or AISDKThreads in AuiConfig, and Vue's useAuiState to read state instead.