# Table of Contents
- [Getting Started | assistant-ui](#getting-started-assistant-ui)
- [Architecture | assistant-ui](#architecture-assistant-ui)
- [About assistant-ui | assistant-ui](#about-assistant-ui-assistant-ui)
- [Attachments | assistant-ui](#attachments-assistant-ui)
- [Speech | assistant-ui](#speech-assistant-ui)
- [Message Editing | assistant-ui](#message-editing-assistant-ui)
- [Message Branching | assistant-ui](#message-branching-assistant-ui)
- [Tool UIs | assistant-ui](#tool-uis-assistant-ui)
- [Intelligent Components | assistant-ui](#intelligent-components-assistant-ui)
- [useAssistantInstructions | assistant-ui](#useassistantinstructions-assistant-ui)
- [Model Context | assistant-ui](#model-context-assistant-ui)
- [makeAssistantTool | assistant-ui](#makeassistanttool-assistant-ui)
- [ThreadList | assistant-ui](#threadlist-assistant-ui)
- [Thread | assistant-ui](#thread-assistant-ui)
- [Attachment | assistant-ui](#attachment-assistant-ui)
- [AssistantModal | assistant-ui](#assistantmodal-assistant-ui)
- [ToolFallback | assistant-ui](#toolfallback-assistant-ui)
- [Markdown | assistant-ui](#markdown-assistant-ui)
- [Custom Scrollbar | assistant-ui](#custom-scrollbar-assistant-ui)
- [Picking a Runtime | assistant-ui](#picking-a-runtime-assistant-ui)
- [Overview | assistant-ui](#overview-assistant-ui)
- [LangChain LangServe | assistant-ui](#langchain-langserve-assistant-ui)
- [Helicone | assistant-ui](#helicone-assistant-ui)
- [User Authorization | assistant-ui](#user-authorization-assistant-ui)
- [AssistantSidebar | assistant-ui](#assistantsidebar-assistant-ui)
- [Using old React versions | assistant-ui](#using-old-react-versions-assistant-ui)
- [makeAssistantVisible | assistant-ui](#makeassistantvisible-assistant-ui)
- [Chat History for AI SDK | assistant-ui](#chat-history-for-ai-sdk-assistant-ui)
- [Chat History for LangGraph Cloud | assistant-ui](#chat-history-for-langgraph-cloud-assistant-ui)
- [ExternalStoreRuntime | assistant-ui](#externalstoreruntime-assistant-ui)
- [useChatRuntime | assistant-ui](#usechatruntime-assistant-ui)
- [LocalRuntime | assistant-ui](#localruntime-assistant-ui)
---
# Getting Started | assistant-ui
Getting Started
===============
[Start with a new project](#start-with-a-new-project)
------------------------------------------------------

### [Initialize assistant-ui in your project](#initialize-assistant-ui-in-your-project)
Step 1: Run `assistant-ui init` to install assistant-ui in a new or existing React.js project:
npx assistant-ui@latest create # new project
npx assistant-ui@latest init # existing project
### [Add API key](#add-api-key)
Add a new `.env` file to your project with your OpenAI API key:
OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# chat history -- sign up for free on https://cloud.assistant-ui.com
# NEXT_PUBLIC_ASSISTANT_BASE_URL="https://..."
### [Start the app](#start-the-app)
npm run dev
[Manual installation](#manual-installation)
--------------------------------------------
We recommend `npx assistant-ui init` to setup existing projects.
### [Add assistant-ui](#add-assistant-ui)
npx assistant-ui add thread thread-list
### [Setup Backend Endpoint](#setup-backend-endpoint)
Install provider SDK:
OpenAIAnthropicAzureAWSGeminiGCPGroqFireworksCohereOllamaChrome AI
Terminal
npm install ai @assistant-ui/react-ai-sdk @ai-sdk/openai
Add an API endpoint:
OpenAIAnthropicAzureAWSGeminiGCPGroqFireworksCohereOllamaChrome AI
/app/api/chat/route.ts
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o-mini"),
messages,
});
return result.toDataStreamResponse();
}
Define environment variables:
OpenAIAnthropicAzureAWSGeminiGCPGroqFireworksCohereOllamaChrome AI
/.env.local
OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
If you aren't using Next.js, you can also deploy this endpoint to Cloudflare Workers, or any other serverless platform.
### [Use it in your app](#use-it-in-your-app)
ThreadAssistantModal
/app/page.tsx
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
import { ThreadList } from "@/components/assistant-ui/thread-list";
import { Thread } from "@/components/assistant-ui/thread";
const MyApp = () => {
const runtime = useChatRuntime({
api: "/api/chat",
});
return (
);
};
[About assistant-ui\
\
Previous Page](/docs/about-assistantui)
[Architecture\
\
Next Page](/docs/architecture)
### On this page
[Start with a new project](#start-with-a-new-project)
[Initialize assistant-ui in your project](#initialize-assistant-ui-in-your-project)
[Add API key](#add-api-key)
[Start the app](#start-the-app)
[Manual installation](#manual-installation)
[Add assistant-ui](#add-assistant-ui)
[Setup Backend Endpoint](#setup-backend-endpoint)
[Use it in your app](#use-it-in-your-app)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/getting-started.mdx)
Open AssistantOpen Assistant
---
# Architecture | assistant-ui
Architecture
============
[assistant-ui is built on these main pillars:](#assistant-ui-is-built-on-these-main-pillars)
---------------------------------------------------------------------------------------------
### 1\. Frontend components
Shadcn UI chat components with built-in state management
### 2\. Runtime
State management layer connecting UI to LLMs and backend services
### 3\. Assistant Cloud
Hosted service for thread persistence, history, and user management
### [1\. Frontend components](#1-frontend-components)
Stylized and functional chat components built on top of Shadcn components that have context state management provided by the assistantUI runtime provider. These pre-built React components come with intelligent state management. [View our components](/docs/ui/Thread)
### [2\. Runtime](#2-runtime)
A React state management context for assistant chat. The runtime handles data conversions between the local state and calls to backends and LLMs. We offer different runtime solutions that work with various frameworks like Vercel AI SDK, LangGraph, LangChain, Helicone, local runtime, and an ExternalStore when you need full control of the frontend message state. [You can view the runtimes we support](/docs/runtimes/pick-a-runtime)
### [3\. Assistant Cloud](#3-assistant-cloud)
A hosted service that enhances your assistant experience with comprehensive thread management and message history. Assistant Cloud stores complete message history, automatically persists threads, supports human-in-the-loop workflows, and integrates with common auth providers to seamlessly allow users to resume conversations at any point. [Cloud Docs](/docs/cloud/overview)
### [There are three common ways to architect your assistant-ui application:](#there-are-three-common-ways-to-architect-your-assistant-ui-application)
#### [**1\. Direct Integration with External Providers:**](#1-direct-integration-with-external-providers)
#### [**2\. Using your own API endpoint**](#2-using-your-own-api-endpoint)
#### [**3\. With Assistant Cloud**](#3-with-assistant-cloud)
[Getting Started\
\
Previous Page](/docs/getting-started)
[Attachments\
\
Next Page](/docs/guides/Attachments)
### On this page
[assistant-ui is built on these main pillars:](#assistant-ui-is-built-on-these-main-pillars)
[1\. Frontend components](#1-frontend-components)
[2\. Runtime](#2-runtime)
[3\. Assistant Cloud](#3-assistant-cloud)
[There are three common ways to architect your assistant-ui application:](#there-are-three-common-ways-to-architect-your-assistant-ui-application)
[**1\. Direct Integration with External Providers:**](#1-direct-integration-with-external-providers)
[**2\. Using your own API endpoint**](#2-using-your-own-api-endpoint)
[**3\. With Assistant Cloud**](#3-with-assistant-cloud)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/architecture.mdx)
Open AssistantOpen Assistant
---
# About assistant-ui | assistant-ui
About assistant-ui
==================
assistant-ui helps you create beautiful, enterprise-grade AI chat interfaces in minutes. Whether you're building a chatGPT clone, a customer support chatbot, an AI assistant, or a complex multi agent application, assistant-ui provides the frontend primative components and state management layers to focus on what makes your application unique.
[Key Features](#key-features)
------------------------------
### Instant Chat UI
Pre-built beautiful, customizable chat interfaces out of the box. Easy to quickly iterate on your idea.
### Chat State Management
Powerful state management for chat interactions, optimized for streaming responses and efficient rendering.
### High Performance
Optimized for speed and efficiency with minimal bundle size, ensuring your AI chat interfaces remain responsive.
### Framework Agnostic
Easily integrate with any backend system, whether using Vercel AI SDK, direct LLM connections, or custom solutions. Works with any React-based framework.
Want to try it out?
[Get Started in 30 Seconds](/docs/getting-started)
.
[Getting Started\
\
Next Page](/docs/getting-started)
### On this page
[Key Features](#key-features)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/about-assistantui.mdx)
Open AssistantOpen Assistant
---
# Attachments | assistant-ui
Attachments
===========
Allow the user to attach files to their messages.
Sample Attachment
Scroll to bottom
Add AttachmentSend
[Enabling attachments](#enabling-attachments)
----------------------------------------------
In order to enable attachments, you need to pass a `AttachmentAdapter` to your runtime hook, e.g. `useChatRuntime`/`useLangGraphRuntime`/`useExternalStoreRuntime`/...
In this example, we use a `CompositeAttachmentAdapter` that allows the user to attach images and text. The CompositeAttachmentAdapter allows you to combine multiple attachment adapters into one.
/app/MyRuntimeProvider.tsx
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
import {
CompositeAttachmentAdapter,
SimpleImageAttachmentAdapter,
SimpleTextAttachmentAdapter,
} from "@assistant-ui/react";
const runtime = useChatRuntime({
api: "/api/chat",
adapters: {
attachments: new CompositeAttachmentAdapter([\
new SimpleImageAttachmentAdapter(),\
new SimpleTextAttachmentAdapter(),\
]),
},
});
[Tutorial: Mastering Attachment Handling with CompositeAttachmentAdapter](#tutorial-mastering-attachment-handling-with-compositeattachmentadapter)
===================================================================================================================================================
In this tutorial, we'll explore how to handle attachments in your assistant-ui application using the CompositeAttachmentAdapter. This powerful feature allows users to attach various types of files to their messages, enhancing the functionality of your AI chat interface.
[Introduction](#introduction)
------------------------------
Attachments are a crucial part of many chat applications, allowing users to share images, documents, and other files. The assistant-ui library provides a flexible system for handling attachments through its AttachmentAdapter interface.
[Prerequisites](#prerequisites)
--------------------------------
Before we begin, make sure you have:
1. A basic assistant-ui project set up
2. Familiarity with React and TypeScript
3. Node.js and npm/yarn installed
[Step 1: Understanding AttachmentAdapter](#step-1-understanding-attachmentadapter)
-----------------------------------------------------------------------------------
The AttachmentAdapter is an interface that defines how attachments are handled in your application. It includes methods for adding, sending, and removing attachments.
[Step 2: Introducing CompositeAttachmentAdapter](#step-2-introducing-compositeattachmentadapter)
-------------------------------------------------------------------------------------------------
The CompositeAttachmentAdapter is a powerful tool that allows you to combine multiple attachment adapters into one. This is particularly useful when you want to support different types of attachments, such as images and text files.
[Step 3: Setting up the CompositeAttachmentAdapter](#step-3-setting-up-the-compositeattachmentadapter)
-------------------------------------------------------------------------------------------------------
Let's create a CompositeAttachmentAdapter that supports both image and text attachments:
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
import {
CompositeAttachmentAdapter,
SimpleImageAttachmentAdapter,
SimpleTextAttachmentAdapter,
} from "@assistant-ui/react";
const MyRuntimeProvider = () => {
const runtime = useChatRuntime({
api: "/api/chat",
adapters: {
attachments: new CompositeAttachmentAdapter([\
new SimpleImageAttachmentAdapter(),\
new SimpleTextAttachmentAdapter(),\
]),
},
});
// ... rest of your component
};
In this example, we're creating a CompositeAttachmentAdapter that combines a SimpleImageAttachmentAdapter and a SimpleTextAttachmentAdapter. This allows our application to handle both image and text file attachments.
[Step 4: Implementing Attachment UI](#step-4-implementing-attachment-ui)
-------------------------------------------------------------------------
Now that we have our adapter set up, let's implement the UI components for attachments:
import {
ComposerAttachments,
ComposerAddAttachment,
UserMessageAttachments,
} from "@/components/assistant-ui/attachment";
const Composer = () => {
return (
{/* ... other composer elements */}
);
};
const UserMessage = () => {
return (
{/* ... other message elements */}
);
};
These components will handle the display of attachments in the composer and in user messages.
[Step 5: Customizing Attachment Behavior](#step-5-customizing-attachment-behavior)
-----------------------------------------------------------------------------------
You can customize the behavior of your attachments by modifying the AttachmentAdapter. For example, you might want to add support for a new file type or change how attachments are processed before sending.
Here's an example of a custom attachment adapter:
import { AttachmentAdapter } from "@assistant-ui/react";
class CustomAttachmentAdapter implements AttachmentAdapter {
accept = "image/*, .pdf";
async add({ file }) {
// Custom logic for adding an attachment
// ...
}
async send(attachment) {
// Custom logic for sending an attachment
// ...
}
async remove() {
// Custom logic for removing an attachment
// ...
}
}
// Use it in your CompositeAttachmentAdapter
const compositeAdapter = new CompositeAttachmentAdapter([\
new CustomAttachmentAdapter(),\
new SimpleTextAttachmentAdapter(),\
]);
[Step 6: Handling Attachments in Your Backend](#step-6-handling-attachments-in-your-backend)
---------------------------------------------------------------------------------------------
Remember to update your backend API to handle the attachments sent by the frontend. The exact implementation will depend on your backend technology, but you'll need to:
1. Receive the attachment data
2. Process and store the attachments as needed
3. Associate the attachments with the correct messages or conversations
[Conclusion](#conclusion)
--------------------------
By mastering the CompositeAttachmentAdapter, you've unlocked powerful attachment handling capabilities in your assistant-ui application. You can now support multiple types of attachments, customize their behavior, and create a rich, interactive chat experience for your users.
Remember to test your attachment handling thoroughly, considering different file types, sizes, and potential error scenarios. Happy coding!
[Architecture\
\
Previous Page](/docs/architecture)
[Message Branching\
\
Next Page](/docs/guides/Branching)
### On this page
[Enabling attachments](#enabling-attachments)
[Tutorial: Mastering Attachment Handling with CompositeAttachmentAdapter](#tutorial-mastering-attachment-handling-with-compositeattachmentadapter)
[Introduction](#introduction)
[Prerequisites](#prerequisites)
[Step 1: Understanding AttachmentAdapter](#step-1-understanding-attachmentadapter)
[Step 2: Introducing CompositeAttachmentAdapter](#step-2-introducing-compositeattachmentadapter)
[Step 3: Setting up the CompositeAttachmentAdapter](#step-3-setting-up-the-compositeattachmentadapter)
[Step 4: Implementing Attachment UI](#step-4-implementing-attachment-ui)
[Step 5: Customizing Attachment Behavior](#step-5-customizing-attachment-behavior)
[Step 6: Handling Attachments in Your Backend](#step-6-handling-attachments-in-your-backend)
[Conclusion](#conclusion)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/guides/Attachments.mdx)
Open AssistantOpen Assistant
---
# Speech | assistant-ui
Speech
======
[Text-to-Speech](#text-to-speech)
----------------------------------
assistant-ui supports text-to-speech via the `SpeechSynthesisAdapter` interface.
Sample Speech to Text
Try submitting a message, then clicking on the speech icon in the assistant message.
Scroll to bottom
Add AttachmentSend
### [SpeechSynthesisAdapter](#speechsynthesisadapter)
Currently, the following speech synthesis adapters are supported:
* `WebSpeechSynthesisAdapter`: Uses the browser's `Web Speech API` API
Support for other speech synthesis adapters is planned for the future.
Passing a `SpeechSynthesisAdapter` to the `EdgeRuntime` will enable text-to-speech support.
### [UI](#ui)
By default, a `Read aloud` button will be shown in the assistant message action bar.
This is implemented using `AssistantActionBar.SpeechControl` which is a wrapper around `AssistantActionBar.Speak` and `AssistantActionBar.StopSpeaking`. The underlying primitives are `ActionBarPrimitive.Speak` and `ActionBarPrimitive.StopSpeaking`.
### [Example](#example)
The following example uses the `WebSpeechSynthesisAdapter`.
import { WebSpeechSynthesisAdapter } from "@assistant-ui/react";
const runtime = useChatRuntime({
api: "/api/chat",
adapters: {
speech: new WebSpeechSynthesisAdapter(),
},
});
[Message Editing\
\
Previous Page](/docs/guides/Editing)
[Tool UIs\
\
Next Page](/docs/guides/ToolUI)
### On this page
[Text-to-Speech](#text-to-speech)
[SpeechSynthesisAdapter](#speechsynthesisadapter)
[UI](#ui)
[Example](#example)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/guides/Speech.mdx)
Open AssistantOpen Assistant
---
# Message Editing | assistant-ui
Message Editing
===============
Give the user the ability to edit their message.
[Enabling edit support](#enabling-edit-support)
------------------------------------------------
You can show an editor interface by using `ComposerPrimitive`.
import { ComposerPrimitive } from "@assistant-ui/react";
...
const Thread = () => {
return (
...
...
);
};
const UserMessage = () => {
return (
...
...
{/* <-- add a button to enable edit mode */}
);
};
// define a new component
const EditComposer = () => {
return (
// you can return a MessagePrimitive including a ComposerPrimitive, or only a ComposerPrimitive
...
);
};
[Message Branching\
\
Previous Page](/docs/guides/Branching)
[Speech\
\
Next Page](/docs/guides/Speech)
### On this page
[Enabling edit support](#enabling-edit-support)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/guides/Editing.mdx)
Open AssistantOpen Assistant
---
# Message Branching | assistant-ui
Message Branching
=================
Switch between different conversation branches.
Sample Branching
Try submitting then editing a message to see the branching in action.
How can I help you today?
What is the weather in Tokyo?What is assistant-ui?
Scroll to bottom
Add AttachmentSend
A new branch is created when:
* a user message is edited
* an assistant message is reloaded
Branches are automatically tracked by assistant-ui by observing changes to the `messages` array.
[Enabling branch support](#enabling-branch-support)
----------------------------------------------------
You can show a branch picker by using `BranchPickerPrimitive`.
import { BranchPickerPrimitive } from "@assistant-ui/react";
const Message = () => {
return (
...
{/* <-- show the branch picker */}
...
);
};
const BranchPicker = () => {
return (
/
);
};
[API](#api)
------------
You can access the current branch state or navigate via the API as well.
These APIs rely on the message state and may only be called inside a message component.
const hasBranches = useMessageIf({ hasBranches: true }); // whether branchCount is >= 2
// navigation
const goToNextBranch = useGoToNextBranch(); // null if there is no next branch
const goToPreviousBranch = useGoToPreviousBranch(); // null if there is no previous branch
[Attachments\
\
Previous Page](/docs/guides/Attachments)
[Message Editing\
\
Next Page](/docs/guides/Editing)
### On this page
[Enabling branch support](#enabling-branch-support)
[API](#api)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/guides/Branching.mdx)
Open AssistantOpen Assistant
---
# Tool UIs | assistant-ui
Tool UIs
========
You can show a custom UI when a tool is called to let the user know what is happening.
### [Tool UI Components](#tool-ui-components)
import { makeAssistantToolUI } from "@assistant-ui/react";
type WebSearchArgs = {
query: string;
};
type WebSearchResult = {
title: string;
description: string;
url: string;
};
export const WebSearchToolUI = makeAssistantToolUI<
WebSearchArgs,
WebSearchResult
>({
toolName: "web_search",
render: ({ args, status }) => {
return
web_search({args.query})
;
},
});
You can put this component anywhere in your app inside the `` component.
import { WebSearchToolUI } from '@/tools/WebSearchToolUI';
const MyApp = () => {
...
return (
...
...
);
};
### [Tool UI Hooks](#tool-ui-hooks)
import { makeAssistantToolUI } from "@assistant-ui/react";
type WebSearchArgs = {
query: string;
};
type WebSearchResult = {
title: string;
description: string;
url: string;
};
export const useWebSearchToolUI = makeAssistantToolUI<
WebSearchArgs,
WebSearchResult
>({
toolName: "web_search",
render: ({ args, status }) => {
return
web_search({args.query})
;
},
});
You can use this hook anywhere in your app inside the `` component.
import { useWebSearchToolUI } from '@/tools/useWebSearchToolUI';
const MyComponent = () => {
useWebSearchToolUI();
...
};
const MyApp = () => {
...
return (
...
...
);
};
### [Inline Tool UI Hooks](#inline-tool-ui-hooks)
If you need access to component props, you can use the `useAssistantToolUI` hook. If you are passing a component inline, you should use the `useInlineRender` hook to prevent the component from being re-mounted on every render.
import { useAssistantToolUI, useInlineRender } from "@assistant-ui/react";
const MyComponent = ({ product_id }) => {
useAssistantToolUI({
toolName: "current_product_info",
render: useInlineRender(({ args, status }) => {
// you can access component props here
return
product_info({ product_id })
;
}),
});
...
};
const MyApp = () => {
...
return (
...
...
);
};
### [Tool Execution Context](#tool-execution-context)
When implementing a tool's execute function, you have access to a context object that includes:
* `toolCallId`: A unique identifier for the current tool execution
* `abortSignal`: An AbortSignal for handling cancellation
const searchTool = {
description: "Search the web",
parameters: z.object({
query: z.string(),
}),
execute: async (args, { toolCallId, abortSignal }) => {
// You can use toolCallId to track or log specific tool executions
console.log(`Executing search with ID: ${toolCallId}`);
// Use abortSignal to handle cancellation
const response = await fetch(
`/api/search?q=${encodeURIComponent(args.query)}`,
{
signal: abortSignal,
},
);
return response.json();
},
};
### [Schema Validation Error Handling](#schema-validation-error-handling)
Tools can now handle schema validation errors through the `experimental_onSchemaValidationError` property. This allows you to provide custom behavior when the tool's parameters fail validation:
const searchTool = {
description: "Search the web",
parameters: z.object({
query: z.string().min(3),
}),
execute: async (args, context) => {
const response = await fetch(`/api/search?q=${args.query}`, {
signal: context.abortSignal,
});
return response.json();
},
experimental_onSchemaValidationError: async (invalidArgs, context) => {
// Handle validation errors gracefully
console.warn(`Invalid search query: ${JSON.stringify(invalidArgs)}`);
return {
error: "Search query must be at least 3 characters long",
suggestions: ["Try a longer search term"],
};
},
};
### [Field-Level Validation Status](#field-level-validation-status)
You can use the `useToolArgsFieldStatus` hook to check the validation status of individual tool argument fields. This is useful for providing real-time feedback about the validity of specific input fields in your tool UI:
import { useToolArgsFieldStatus } from "@assistant-ui/react";
const SearchToolUI = makeAssistantToolUI<{ query: string }, SearchResult>({
toolName: "search",
render: ({ args }) => {
const status = useToolArgsFieldStatus("query");
const isInProgress = status.type === "running";
return (
);
},
});
### [Function Calling for User Input](#function-calling-for-user-input)
The following example shows a `date_picker` tool that the AI can call to collect a date from the user.
import { makeAssistantToolUI } from "@assistant-ui/react";
import { DatePicker } from "@/components/datepicker";
const DatePickerToolUI = makeAssistantToolUI<{}, { date: string }>({
toolName: "date_picker",
render: ({ result, status, addResult }) => {
if (result) {
return
You picked {result.date}
;
}
const handleSubmit = (date: Date) => {
addResult({ date: date.toISOString() });
};
return ;
},
});
[Implementing Tool UIs: A Step-by-Step Guide to Creating Interactive AI Tools](#implementing-tool-uis-a-step-by-step-guide-to-creating-interactive-ai-tools)
=============================================================================================================================================================
In this tutorial, we'll walk through the process of implementing Tool UIs in assistant-ui. Tool UIs allow you to create custom interfaces for AI tools, enhancing the user experience and providing visual feedback when a tool is called.
[Table of Contents](#table-of-contents)
----------------------------------------
1. Introduction to Tool UIs
2. Creating a Basic Tool UI Component
3. Using Tool UI Hooks
4. Implementing Inline Tool UI Hooks
5. Function Calling for User Input
[1\. Introduction to Tool UIs](#1-introduction-to-tool-uis)
------------------------------------------------------------
Tool UIs in assistant-ui provide a way to display custom interfaces when an AI tool is called. This can help users understand what's happening behind the scenes and provide interactive elements when needed.
[2\. Creating a Basic Tool UI Component](#2-creating-a-basic-tool-ui-component)
--------------------------------------------------------------------------------
Let's start by creating a simple Tool UI component for a web search tool.
import { makeAssistantToolUI } from "@assistant-ui/react";
type WebSearchArgs = {
query: string;
};
type WebSearchResult = {
title: string;
description: string;
url: string;
};
export const WebSearchToolUI = makeAssistantToolUI<
WebSearchArgs,
WebSearchResult
>({
toolName: "web_search",
render: ({ args, status }) => {
return
web_search({args.query})
;
},
});
To use this component, place it inside the ``:
import { WebSearchToolUI } from "@/tools/WebSearchToolUI";
const MyApp = () => {
return (
{/* Other components */}
{/* More components */}
);
};
[3\. Using Tool UI Hooks](#3-using-tool-ui-hooks)
--------------------------------------------------
For more flexibility, you can create a Tool UI hook:
import { makeAssistantToolUI } from "@assistant-ui/react";
type WebSearchArgs = {
query: string;
};
type WebSearchResult = {
title: string;
description: string;
url: string;
};
export const useWebSearchToolUI = makeAssistantToolUI<
WebSearchArgs,
WebSearchResult
>({
toolName: "web_search",
render: ({ args, status }) => {
return
web_search({args.query})
;
},
});
Use the hook in a component within the ``:
import { useWebSearchToolUI } from "@/tools/useWebSearchToolUI";
const MyComponent = () => {
useWebSearchToolUI();
// Component logic
};
const MyApp = () => {
return (
);
};
[4\. Implementing Inline Tool UI Hooks](#4-implementing-inline-tool-ui-hooks)
------------------------------------------------------------------------------
For cases where you need access to component props, use the `useAssistantToolUI` hook with `useInlineRender`:
import { useAssistantToolUI, useInlineRender } from "@assistant-ui/react";
const MyComponent = ({ product_id }) => {
useAssistantToolUI({
toolName: "current_product_info",
render: useInlineRender(({ args, status }) => {
return
product_info({product_id})
;
}),
});
// Component logic
};
const MyApp = () => {
return (
);
};
[5\. Function Calling for User Input](#5-function-calling-for-user-input)
--------------------------------------------------------------------------
Finally, let's create a Tool UI that allows user input, such as a date picker:
import { makeAssistantToolUI } from "@assistant-ui/react";
import { DatePicker } from "@/components/datepicker";
const DatePickerToolUI = makeAssistantToolUI<{}, { date: string }>({
toolName: "date_picker",
render: ({ result, status, addResult }) => {
if (result) {
return
You picked {result.date}
;
}
const handleSubmit = (date: Date) => {
addResult({ date: date.toISOString() });
};
return ;
},
});
This Tool UI displays a date picker when called and allows the user to select a date. Once a date is chosen, it's added to the result and displayed.
By following these steps, you can create interactive and informative Tool UIs that enhance the AI chat experience in your application. These UIs provide visual feedback and allow for user interaction when AI tools are called, making your assistant more user-friendly and engaging."
[Speech\
\
Previous Page](/docs/guides/Speech)
[Intelligent Components\
\
Next Page](/docs/copilots/motivation)
### On this page
[Tool UI Components](#tool-ui-components)
[Tool UI Hooks](#tool-ui-hooks)
[Inline Tool UI Hooks](#inline-tool-ui-hooks)
[Tool Execution Context](#tool-execution-context)
[Schema Validation Error Handling](#schema-validation-error-handling)
[Field-Level Validation Status](#field-level-validation-status)
[Function Calling for User Input](#function-calling-for-user-input)
[Implementing Tool UIs: A Step-by-Step Guide to Creating Interactive AI Tools](#implementing-tool-uis-a-step-by-step-guide-to-creating-interactive-ai-tools)
[Table of Contents](#table-of-contents)
[1\. Introduction to Tool UIs](#1-introduction-to-tool-uis)
[2\. Creating a Basic Tool UI Component](#2-creating-a-basic-tool-ui-component)
[3\. Using Tool UI Hooks](#3-using-tool-ui-hooks)
[4\. Implementing Inline Tool UI Hooks](#4-implementing-inline-tool-ui-hooks)
[5\. Function Calling for User Input](#5-function-calling-for-user-input)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/guides/ToolUI.mdx)
Open AssistantOpen Assistant
---
# Intelligent Components | assistant-ui
Intelligent Components
======================
React revolutionized web development with components that combine logic, structure, and style. Now, with assistant-ui, we're adding a fourth dimension: intelligence. Let's learn how to build smart components through a practical banking app example.
[The Evolution of Components](#the-evolution-of-components)
------------------------------------------------------------
Traditional React components combine three elements:
// Traditional React Component
function TransactionHistory({ transactions }) {
// 1. Logic (JavaScript/TypeScript)
const handleRefund = (transactionId) => {
// Process refund...
};
// 2. Structure (JSX/TSX)
return (
// 3. Style (CSS via className)
{transactions.map((transaction) => (
${transaction.amount}{transaction.merchant}
))}
);
}
[Adding Intelligence](#adding-intelligence)
--------------------------------------------
With assistant-ui, we can enhance this component with intelligence using four powerful APIs:
### [1\. Making Components Readable (makeAssistantVisible)](#1-making-components-readable-makeassistantvisible)
First, let's make our buttons "readable" and interactive:
import { makeAssistantVisible } from "@assistant-ui/react";
// Make the refund button intelligent
const SmartButton = makeAssistantVisible(
({ onClick, children }) => ,
{
clickable: true, // Allow the assistant to click the button
},
);
function TransactionHistory({ transactions }) {
return (
);
}
Now the assistant can:
* Understand the transaction history structure
* Interact with refund buttons
* Help users manage their transactions
### [2\. Adding System Instructions (useAssistantInstructions)](#2-adding-system-instructions-useassistantinstructions)
Next, let's give the assistant specific instructions about its role:
import { useAssistantInstructions } from "@assistant-ui/react";
function SmartTransactionHistory() {
useAssistantInstructions(`
You are a helpful banking assistant that:
1. Helps users understand their transactions
2. Explains refund policies
3. Identifies suspicious transactions
4. Guides users through the refund process
`);
return ;
}
### [3\. Creating Tools (makeAssistantTool)](#3-creating-tools-makeassistanttool)
Let's add transaction-specific tools for the assistant:
import { makeAssistantTool, tool } from "@assistant-ui/react";
import { z } from "zod";
// Define a tool to analyze transactions
const analyzeTransaction = tool({
parameters: z.object({
transactionId: z.string(),
merchantName: z.string(),
}),
execute: async ({ transactionId, merchantName }) => {
// Analyze transaction patterns, merchant reputation, etc.
return {
isSuspicious: false,
merchantRating: 4.5,
similarTransactions: 3,
refundEligible: true,
};
},
});
// Create a tool component
const TransactionAnalyzer = makeAssistantTool(analyzeTransaction);
function SmartTransactionHistory() {
// Previous instructions...
return (
<>
>
);
}
### [4\. Adding Custom Context (Model Context)](#4-adding-custom-context-model-context)
Finally, let's add dynamic context based on the user's transaction patterns:
import { useAssistantRuntime } from "@assistant-ui/react";
import { useEffect } from "react";
function SmartTransactionHistory({ userProfile }) {
const assistantRuntime = useAssistantRuntime();
useEffect(() => {
return assistantRuntime.registerModelContextProvider({
getModelContext: () => ({
system: `
User spending patterns:
- Average transaction: ${userProfile.avgTransaction}
- Common merchants: ${userProfile.frequentMerchants.join(", ")}
- Refund history: ${userProfile.refundCount} requests
`,
}),
});
}, [assistantRuntime, userProfile]);
// Previous components...
}
[The Result: An Intelligent Banking Experience](#the-result-an-intelligent-banking-experience)
-----------------------------------------------------------------------------------------------
This enhanced component now provides:
* Natural language interaction with transaction history
* Contextual help for understanding transactions
* Automated transaction analysis
* Smart refund assistance
The assistant can now:
1. Read and understand transaction details
2. Follow banking-specific guidelines
3. Use tools to analyze transactions
4. Access user patterns for personalized help
This creates a more intuitive and safer banking experience while maintaining the familiar React component model.
[Next Steps](#next-steps)
--------------------------
Learn more about each API:
* [makeAssistantVisible](make-assistant-readable)
for component understanding
* [makeAssistantTool](make-assistant-tool)
for transaction analysis
* [useAssistantInstructions](use-assistant-instructions)
for behavior guidance
* [Model Context](model-context)
for dynamic context management
[Tool UIs\
\
Previous Page](/docs/guides/ToolUI)
[makeAssistantTool\
\
Next Page](/docs/copilots/make-assistant-tool)
### On this page
[The Evolution of Components](#the-evolution-of-components)
[Adding Intelligence](#adding-intelligence)
[1\. Making Components Readable (makeAssistantVisible)](#1-making-components-readable-makeassistantvisible)
[2\. Adding System Instructions (useAssistantInstructions)](#2-adding-system-instructions-useassistantinstructions)
[3\. Creating Tools (makeAssistantTool)](#3-creating-tools-makeassistanttool)
[4\. Adding Custom Context (Model Context)](#4-adding-custom-context-model-context)
[The Result: An Intelligent Banking Experience](#the-result-an-intelligent-banking-experience)
[Next Steps](#next-steps)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/copilots/motivation.mdx)
Open AssistantOpen Assistant
---
# useAssistantInstructions | assistant-ui
useAssistantInstructions
========================
`useAssistantInstructions` is a React hook that allows you to set system instructions for your assistant-ui components.
[Usage](#usage)
----------------
import { useAssistantInstructions } from "@assistant-ui/react";
function MyComponent() {
// Simple string usage
useAssistantInstructions("You are a helpful form assistant...");
// With configuration object
useAssistantInstructions({
instruction: "You are a helpful form assistant...",
disabled: false, // Optional: disable the instructions
});
return
My Component
;
}
[API Reference](#api-reference)
--------------------------------
### [Parameters](#parameters)
The hook accepts either:
* A string containing the system instructions
* A configuration object with:
* `instruction`: The system instructions
* `disabled`: Optional boolean to disable the instructions
### [Behavior](#behavior)
The hook will:
1. Register the provided instructions as system instructions in the model context
2. Automatically clean up when the component unmounts
3. Update when the instructions change
4. Do nothing if disabled is set to true
[Example](#example)
--------------------
function SmartForm() {
useAssistantInstructions({
instruction: `
You are a form assistant that:
- Validates user input
- Provides helpful suggestions
- Explains any errors
- Guides users through complex fields
`,
});
return ;
}
[makeAssistantTool\
\
Previous Page](/docs/copilots/make-assistant-tool)
[Model Context\
\
Next Page](/docs/copilots/model-context)
### On this page
[Usage](#usage)
[API Reference](#api-reference)
[Parameters](#parameters)
[Behavior](#behavior)
[Example](#example)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/copilots/use-assistant-instructions.mdx)
Open AssistantOpen Assistant
---
# Model Context | assistant-ui
Model Context
=============
Model Context is the foundation of intelligence in assistant-ui components. It provides configuration and capabilities to the assistant through a context provider system.
[Core Concepts](#core-concepts)
--------------------------------
### [System Instructions](#system-instructions)
System instructions define the base behavior and knowledge available to the assistant. These can be provided in several ways:
import {
useAssistantInstructions,
makeAssistantVisible,
} from "@assistant-ui/react";
// Via useAssistantInstructions
useAssistantInstructions("You are a helpful assistant...");
// Via makeAssistantVisible
const ReadableComponent = makeAssistantVisible(MyComponent);
// Automatically provides component HTML as system context
### [Tools](#tools)
Tools are functions that the assistant can use to interact with your application. They can be provided through various mechanisms:
import {
makeAssistantVisible,
makeAssistantTool,
tool,
useAssistantRuntime,
} from "@assistant-ui/react";
import { z } from "zod";
// Via makeAssistantVisible's clickable option
const ClickableButton = makeAssistantVisible(Button, {
clickable: true, // Provides a click tool
});
// Via makeAssistantTool
const submitForm = tool({
parameters: z.object({
email: z.string().email(),
name: z.string(),
}),
execute: async ({ email, name }) => {
// Implementation
return { success: true };
},
});
const SubmitFormTool = makeAssistantTool(submitForm);
// Use in your component
function Form() {
return (
);
}
[Context Provider System](#context-provider-system)
----------------------------------------------------
The context provider system allows components to contribute to the model context. Here's a typical usage pattern:
import { useAssistantRuntime, tool } from "@assistant-ui/react";
import { useEffect } from "react";
import { z } from "zod";
function MyComponent() {
const assistantRuntime = useAssistantRuntime();
// Define tool using the tool() helper
const myTool = tool({
parameters: z.object({
query: z.string(),
}),
execute: async ({ query }) => {
const result = await searchDatabase(query);
return { result };
},
});
useEffect(() => {
// Register context provider
return assistantRuntime.registerModelContextProvider({
getModelContext: () => ({
system: "You are a helpful search assistant...",
tools: { myTool },
}),
});
}, [assistantRuntime]); // Re-register if runtime changes
return
{/* component content */}
;
}
### [Provider Composition](#provider-composition)
Multiple providers can be registered, and their contexts will be composed:
* System instructions are concatenated
* Tool sets are merged
* Nested readable components only contribute their context at the outermost level
[Best Practices](#best-practices)
----------------------------------
1. **System Instructions**
* Keep them focused and specific to the component's purpose
* Use useAssistantInstructions for explicit instructions
* Let makeAssistantVisible handle component structure
2. **Tools**
* Use the tool() helper to define tool schemas and behavior
* Prefer makeAssistantTool for reusable tools
* Handle errors gracefully
* Consider async operations and loading states
* Use the built-in click tool when possible
3. **Context Management**
* Register providers in useEffect for proper cleanup
* Clean up providers when components unmount
* Avoid deeply nested readable components
* Consider performance implications of large HTML structures
[useAssistantInstructions\
\
Previous Page](/docs/copilots/use-assistant-instructions)
[Thread\
\
Next Page](/docs/ui/Thread)
### On this page
[Core Concepts](#core-concepts)
[System Instructions](#system-instructions)
[Tools](#tools)
[Context Provider System](#context-provider-system)
[Provider Composition](#provider-composition)
[Best Practices](#best-practices)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/copilots/model-context.mdx)
Open AssistantOpen Assistant
---
# makeAssistantTool | assistant-ui
makeAssistantTool
=================
`makeAssistantTool` creates a React component that provides a tool to the assistant. This is useful for defining reusable tools that can be composed into your application.
[Usage](#usage)
----------------
import { makeAssistantTool, tool } from "@assistant-ui/react";
import { z } from "zod";
// Define the tool using the tool() helper
const submitForm = tool({
parameters: z.object({
email: z.string().email(),
name: z.string(),
}),
execute: async ({ email, name }) => {
// Implementation
return { success: true };
},
});
// Create a tool component
const SubmitFormTool = makeAssistantTool(submitForm);
// Use in your component
function Form() {
return (
);
}
[API Reference](#api-reference)
--------------------------------
### [Parameters](#parameters)
* `tool`: A tool definition created using the `tool()` helper function
* `parameters`: Zod schema defining the tool's parameters
* `execute`: Function that implements the tool's behavior
### [Returns](#returns)
Returns a React component that:
* Provides the tool to the assistant when mounted
* Automatically removes the tool when unmounted
* Renders nothing in the DOM (returns null)
[Example with Multiple Tools](#example-with-multiple-tools)
------------------------------------------------------------
import { makeAssistantTool, tool } from "@assistant-ui/react";
import { z } from "zod";
// Define tools
const validateEmail = tool({
parameters: z.object({
email: z.string(),
}),
execute: ({ email }) => {
const isValid = email.includes("@");
return { isValid, reason: isValid ? "Valid email" : "Missing @" };
},
});
const sendEmail = tool({
parameters: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
execute: async (params) => {
// Implementation
return { sent: true };
},
});
// Create tool components
const EmailValidator = makeAssistantTool(validateEmail);
const EmailSender = makeAssistantTool(sendEmail);
// Use together
function EmailForm() {
return (
);
}
[Best Practices](#best-practices)
----------------------------------
1. **Parameter Validation**
* Always use Zod schemas to define parameters
* Be specific about parameter types and constraints
* Add helpful error messages to schema validations
2. **Error Handling**
* Return meaningful error messages
* Consider returning partial results when possible
* Handle async errors appropriately
3. **Composition**
* Break complex tools into smaller, focused ones
* Consider tool dependencies and interactions
* Use multiple tools together for complex functionality
[Intelligent Components\
\
Previous Page](/docs/copilots/motivation)
[useAssistantInstructions\
\
Next Page](/docs/copilots/use-assistant-instructions)
### On this page
[Usage](#usage)
[API Reference](#api-reference)
[Parameters](#parameters)
[Returns](#returns)
[Example with Multiple Tools](#example-with-multiple-tools)
[Best Practices](#best-practices)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/copilots/make-assistant-tool.mdx)
Open AssistantOpen Assistant
---
# ThreadList | assistant-ui
ThreadList
==========
[Overview](#overview)
----------------------
The ThreadList component lets the user switch between threads.
Sample ThreadList
New Thread
How can I help you today?
What is the weather in Tokyo?What is assistant-ui?
Scroll to bottom
Add AttachmentSend
[Getting Started](#getting-started)
------------------------------------
### [Add `thread-list`](#add-thread-list)
npx shadcn@latest add "https://r.assistant-ui.com/thread-list"
This adds a `/components/assistant-ui/thread-list.tsx` file to your project, which you can adjust as needed.
### [Use in your application](#use-in-your-application)
/app/page.tsx
import { Thread } from "@/components/assistant-ui/thread";
import { ThreadList } from "@/components/assistant-ui/thread-list";
export default function Home() {
return (
);
}
[Thread\
\
Previous Page](/docs/ui/Thread)
[Attachment\
\
Next Page](/docs/ui/Attachment)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Add `thread-list`](#add-thread-list)
[Use in your application](#use-in-your-application)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/ui/ThreadList.mdx)
Open AssistantOpen Assistant
---
# Thread | assistant-ui
Thread
======
[Overview](#overview)
----------------------
The raw message list and message composer UI.
Sample
How can I help you today?
What is the weather in Tokyo?What is assistant-ui?
Scroll to bottom
Add AttachmentSend
[Getting Started](#getting-started)
------------------------------------
### [Add `thread`](#add-thread)
npx shadcn@latest add "https://r.assistant-ui.com/thread"
This adds a `/components/assistant-ui/thread.tsx` file to your project, which you can adjust as needed.
### [Use in your application](#use-in-your-application)
/app/page.tsx
import { Thread } from "@/components/assistant-ui/thread";
export default function Home() {
return (
);
}
[Model Context\
\
Previous Page](/docs/copilots/model-context)
[ThreadList\
\
Next Page](/docs/ui/ThreadList)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Add `thread`](#add-thread)
[Use in your application](#use-in-your-application)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/ui/Thread.mdx)
Open AssistantOpen Assistant
---
# Attachment | assistant-ui
Attachment
==========
[Overview](#overview)
----------------------
The Attachment components let the user attach files and view the attachments.
Sample Attachment
Scroll to bottom
Add AttachmentSend
[Getting Started](#getting-started)
------------------------------------
### [Add `attachment`](#add-attachment)
npx shadcn@latest add "https://r.assistant-ui.com/attachment"
This adds a `/components/assistant-ui/attachment.tsx` file to your project, which you can adjust as needed.
### [Use in your application](#use-in-your-application)
/components/assistant-ui/thread.tsx
import {
ComposerAttachments,
ComposerAddAttachment,
} from "@/components/assistant-ui/attachment";
const Composer: FC = () => {
return (
);
};
/components/assistant-ui/thread.tsx
import { UserMessageAttachments } from "@/components/assistant-ui/attachment";
const UserMessage: FC = () => {
return (
);
};
[ThreadList\
\
Previous Page](/docs/ui/ThreadList)
[AssistantModal\
\
Next Page](/docs/ui/AssistantModal)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Add `attachment`](#add-attachment)
[Use in your application](#use-in-your-application)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/ui/Attachment.mdx)
Open AssistantOpen Assistant
---
# AssistantModal | assistant-ui
AssistantModal
==============
[Overview](#overview)
----------------------
A chat bubble shown in the bottom right corner of the screen. Useful for support or Q&A use cases.
Sample Assistant Modal
Open AssistantOpen Assistant
[Getting Started](#getting-started)
------------------------------------
### [Add `assistant-modal`](#add-assistant-modal)
npx shadcn@latest add "https://r.assistant-ui.com/assistant-modal"
This adds `/components/assistant-ui/assistant-modal.tsx` to your project, which you can adjust as needed.
### [Use in your application](#use-in-your-application)
/app/page.tsx
import { AssistantModal } from "@/components/assistant-ui/assistant-modal";
export default function Home() {
return (
);
}
[Attachment\
\
Previous Page](/docs/ui/Attachment)
[AssistantSidebar\
\
Next Page](/docs/ui/AssistantSidebar)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Add `assistant-modal`](#add-assistant-modal)
[Use in your application](#use-in-your-application)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/ui/AssistantModal.mdx)
Open AssistantOpen Assistant
---
# ToolFallback | assistant-ui
ToolFallback
============
[Overview](#overview)
----------------------
The ToolFallback component displays a default ToolUI for tools that do not have a dedicated ToolUI.
[Getting Started](#getting-started)
------------------------------------
### [Add `tool-fallback`](#add-tool-fallback)
npx shadcn@latest add "https://r.assistant-ui.com/tool-fallback"
This adds a `/components/assistant-ui/tool-fallback.tsx` file to your project, which you can adjust as needed.
### [Use it in your application](#use-it-in-your-application)
Pass the `ToolFallback` component to the `MessagePrimitive.Content` component
/components/assistant-ui/thread.tsx
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
const AssistantMessage: FC = () => {
return (
);
};
[Markdown\
\
Previous Page](/docs/ui/Markdown)
[Custom Scrollbar\
\
Next Page](/docs/ui/Scrollbar)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Add `tool-fallback`](#add-tool-fallback)
[Use it in your application](#use-it-in-your-application)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/ui/ToolFallback.mdx)
Open AssistantOpen Assistant
---
# Markdown | assistant-ui
Markdown
========
Allow the assistant to display rich text using markdown.
Markdown support is already included by default in the `Thread` component.
[Enabling markdown support](#enabling-markdown-support)
--------------------------------------------------------
### [Add `markdown-text`](#add-markdown-text)
npx shadcn@latest add "https://r.assistant-ui.com/markdown-text"
This adds a `/components/assistant-ui/markdown-text.tsx` file to your project, which you can adjust as needed.
### [Use it in your application](#use-it-in-your-application)
Pass the `MarkdownText` component to the `MessagePrimitive.Content` component
/components/assistant-ui/thread.tsx
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
const AssistantMessage: FC = () => {
return (
);
};
[AssistantSidebar\
\
Previous Page](/docs/ui/AssistantSidebar)
[ToolFallback\
\
Next Page](/docs/ui/ToolFallback)
### On this page
[Enabling markdown support](#enabling-markdown-support)
[Add `markdown-text`](#add-markdown-text)
[Use it in your application](#use-it-in-your-application)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/ui/Markdown.mdx)
Open AssistantOpen Assistant
---
# Custom Scrollbar | assistant-ui
Custom Scrollbar
================
If you want to show a custom scrollbar UI of the Thread.Viewport in place of the system default, you can integrate `@radix-ui/react-scroll-area`. An example implementation of this is [shadcn/ui's Scroll Area](https://ui.shadcn.com/docs/components/scroll-area)
.
[Add shadcn Scroll Area](#add-shadcn-scroll-area)
--------------------------------------------------
npx shadcn@latest add scroll-area
### [@radix-ui/react-scroll-area v1.2.0 release candidate required](#radix-uireact-scroll-area-v120-release-candidate-required)
The v1.2.0-rc.x release candidate can be installed via
pnpm add @radix-ui/react-scroll-area@next
[Additional Styles](#additional-styles)
----------------------------------------
The Radix UI Viewport component adds an intermediate `
` element. Add the following CSS to your `globals.css`:
@/app/globals.css
.thread-viewport > [data-radix-scroll-area-content] {
@apply flex flex-col items-center self-stretch bg-inherit;
}
[Integration](#integration)
----------------------------
* Wrap `Thread.Root` with ``
* Wrap `Thread.Viewport` with ``
* Add shadcn's `` to `Thread.Root`
The resulting MyThread component should look like this:
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { ScrollBar } from "@/components/ui/scroll-area";
const MyThread: FC = () => {
return (
...
);
};
[ToolFallback\
\
Previous Page](/docs/ui/ToolFallback)
[Picking a Runtime\
\
Next Page](/docs/runtimes/pick-a-runtime)
### On this page
[Add shadcn Scroll Area](#add-shadcn-scroll-area)
[@radix-ui/react-scroll-area v1.2.0 release candidate required](#radix-uireact-scroll-area-v120-release-candidate-required)
[Additional Styles](#additional-styles)
[Integration](#integration)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/ui/Scrollbar.mdx)
Open AssistantOpen Assistant
---
# Picking a Runtime | assistant-ui
Picking a Runtime
=================
assistant-ui offers a variety of runtimes for different use cases.
[New Chatbot](#new-chatbot)
----------------------------
The following runtimes are recommended for new apps:
* [AI SDK](/docs/runtimes/ai-sdk/use-chat)
* [LangGraph Cloud](/docs/runtimes/langgraph)
### [Vercel AI SDK](#vercel-ai-sdk)
AI SDK by Vercel lets you build conversational AI agents.
### [LangGraph Cloud](#langgraph-cloud)
The LangGraph Cloud runtime lets you build stateful agents with LangGraph.
[Already using a custom backend](#already-using-a-custom-backend)
------------------------------------------------------------------
For custom backends, we have two entrypoints for integration:
* [LocalRuntime](/docs/runtimes/custom/local)
* [ExternalStoreRuntime](/docs/runtimes/custom/external-store)
[Custom Scrollbar\
\
Previous Page](/docs/ui/Scrollbar)
[useChatRuntime\
\
Next Page](/docs/runtimes/ai-sdk/use-chat)
### On this page
[New Chatbot](#new-chatbot)
[Vercel AI SDK](#vercel-ai-sdk)
[LangGraph Cloud](#langgraph-cloud)
[Already using a custom backend](#already-using-a-custom-backend)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/runtimes/pick-a-runtime.mdx)
Open AssistantOpen Assistant
---
# Overview | assistant-ui
Overview
========
Assistant Cloud is a hosted service built for assistant-ui frontends.
[Features](#features)
----------------------
### [Thread management](#thread-management)
Using our `` component, show the users a list of conversations. Allow the users to seamlessly switch between conversations and even let long running tasks run in the background.
Assistant Cloud automatically persists a list of threads and corresponding metadata. It also automatically generates a title for conversations based on the initial messages.
Supported backends:
* AI SDK
* LangGraph
* Custom
### [Chat history](#chat-history)
For every conversation, Assistant Cloud can store the history of messages, allowing the user to resume the conversation at any point in time. This supports human in the loop workflows, where the execution of an agent is interrupted until user feedback is collected.
Supported backends:
* AI SDK
* LangGraph
* Custom (currently only Local Runtime)
### [Authorization](#authorization)
Assistant Cloud integrates with your auth provider (Clerk, Auth0, Supabase, Firebase, ...) to identify your users and authorize them to access just the conversations they are allowed to see.
Supported auth providers:
* Clerk
* Auth0
* Supabase
* Firebase
* Your own
[Getting Started](#getting-started)
------------------------------------
To get started, follow the walkthrough for your preferred backend:
* [AI SDK](/docs/cloud/persistence/ai-sdk)
* [LangGraph](/docs/cloud/persistence/langgraph)
You can also check out our example repositories to see how to integrate Assistant Cloud with your frontend:
* [With AI SDK](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-cloud)
* [With LangGraph](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-langgraph)
[ExternalStoreRuntime\
\
Previous Page](/docs/runtimes/custom/external-store)
[User Authorization\
\
Next Page](/docs/cloud/authorization)
### On this page
[Features](#features)
[Thread management](#thread-management)
[Chat history](#chat-history)
[Authorization](#authorization)
[Getting Started](#getting-started)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/cloud/overview.mdx)
Open AssistantOpen Assistant
---
# LangChain LangServe | assistant-ui
LangChain LangServe
===================
[Overview](#overview)
----------------------
Integration with a LangServe server via Vercel AI SDK.
[Getting Started](#getting-started)
------------------------------------
### [Create a Next.JS project](#create-a-nextjs-project)
npx create-next-app@latest my-app
cd my-app
### [Install `@langchain/core`, `ai-sdk` and `@assistant-ui/react`](#install-langchaincore-ai-sdk-and-assistant-uireact)
npm install @assistant-ui/react @assistant-ui/react-ai-sdk ai ai/react @langchain/core
### [Setup a backend route under `/api/chat`](#setup-a-backend-route-under-apichat)
@/app/api/chat/route.ts
import { RemoteRunnable } from "@langchain/core/runnables/remote";
import type { RunnableConfig } from "@langchain/core/runnables";
import { streamText, LangChainAdapter, type Message } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = (await req.json()) as { messages: Message[] };
// TODO replace with your own langserve URL
const remoteChain = new RemoteRunnable<
{ messages: Message[] },
string,
RunnableConfig
>({
url: "",
});
const stream = await remoteChain.stream({
messages,
});
return LangChainAdapter.toDataStreamResponse(stream);
}
### [Define a `MyRuntimeProvider` component](#define-a-myruntimeprovider-component)
@/app/MyRuntimeProvider.tsx
"use client";
import { useChat } from "ai/react";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useVercelUseChatRuntime } from "@assistant-ui/react-ai-sdk";
export function MyRuntimeProvider({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const chat = useChat({
api: "/api/chat",
unstable_AISDKInterop: true,
});
const runtime = useVercelUseChatRuntime(chat);
return (
{children}
);
}
### [Wrap your app in `MyRuntimeProvider`](#wrap-your-app-in-myruntimeprovider)
@/app/layout.tsx
import type { ReactNode } from "react";
import { MyRuntimeProvider } from "@/app/MyRuntimeProvider";
export default function RootLayout({
children,
}: Readonly<{
children: ReactNode;
}>) {
return (
{children}
);
}
[Part 3: Approval UI\
\
Previous Page](/docs/runtimes/langgraph/tutorial/part-3)
[Helicone\
\
Next Page](/docs/runtimes/helicone)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Create a Next.JS project](#create-a-nextjs-project)
[Install `@langchain/core`, `ai-sdk` and `@assistant-ui/react`](#install-langchaincore-ai-sdk-and-assistant-uireact)
[Setup a backend route under `/api/chat`](#setup-a-backend-route-under-apichat)
[Define a `MyRuntimeProvider` component](#define-a-myruntimeprovider-component)
[Wrap your app in `MyRuntimeProvider`](#wrap-your-app-in-myruntimeprovider)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/runtimes/langserve.mdx)
Open AssistantOpen Assistant
---
# Helicone | assistant-ui
Helicone
========
Helicone acts as a proxy for your OpenAI API calls, enabling detailed logging and monitoring. To integrate, update your API base URL and add the Helicone-Auth header.
[AI SDK by vercel](#ai-sdk-by-vercel)
--------------------------------------
1. **Set Environment Variables:**
* `HELICONE_API_KEY`
* `OPENAI_API_KEY`
2. **Configure the OpenAI client:**
import { createOpenAI } from "@ai-sdk/openai";
import { streamText } from "ai";
const openai = createOpenAI({
baseURL: "https://oai.helicone.ai/v1",
headers: {
"Helicone-Auth": `Bearer ${process.env.HELICONE_API_KEY}`,
},
});
export async function POST(req: Request) {
const { prompt } = await req.json();
return streamText({
model: openai("gpt-4o"),
prompt,
});
}
[LangChain Integration (Python)](#langchain-integration-python)
----------------------------------------------------------------
1. **Set Environment Variables:**
* `HELICONE_API_KEY`
* `OPENAI_API_KEY`
2. **Configure ChatOpenAI:**
from langchain.chat_models import ChatOpenAI
import os
llm = ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0,
openai_api_base="https://oai.helicone.ai/v1",
openai_api_key=os.environ["OPENAI_API_KEY"],
openai_api_headers={"Helicone-Auth": f"Bearer {os.environ['HELICONE_API_KEY']}"}
)
[Summary](#summary)
--------------------
Update your API base URL to `https://oai.helicone.ai/v1` and add the `Helicone-Auth` header with your API key either in your Vercel AI SDK or LangChain configuration.
[LangChain LangServe\
\
Previous Page](/docs/runtimes/langserve)
[LocalRuntime\
\
Next Page](/docs/runtimes/custom/local)
### On this page
[AI SDK by vercel](#ai-sdk-by-vercel)
[LangChain Integration (Python)](#langchain-integration-python)
[Summary](#summary)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/runtimes/helicone.mdx)
Open AssistantOpen Assistant
---
# User Authorization | assistant-ui
User Authorization
==================
The assistant-ui API can be directly accessed by your frontend. This elliminates the need for a backend server from your side, except for authorization of your users.
This document explains how you can setup your server to authorize users to access the assistant-ui API.
[Workspaces](#workspaces)
--------------------------
Authorization is granted to a workspace. Depending on the structure of your app, you might want to use user\_ids as the workspace\_id, or you might want to use a more complex structure. For example, if your app supports multiple "projects", you might want to use the project\_id + user\_id as the workspace id (thread history scoped to user+project pairs).
[Workspace Auth Tokens](#workspace-auth-tokens)
------------------------------------------------
assistant-ui issues workspace auth tokens. These tokens give access to the assistant-ui API for a specific workspace. Tokens are short lived (5 minutes), so the client needs to periodically request a new token (handled by assistant-ui).
There are two supported approaches to obtain a workspace auth token:
* Direct integration with your auth provider
* From a backend server / serverless function
### [Choosing the right approach](#choosing-the-right-approach)
Direct integration with your auth provider:
* simpler to setup and maintain
* assigns a workspace\_id to every user (by using the user\_id as the workspace\_id)
* requires a supported auth provider (Clerk, Auth0, Supabase, Firebase, Stytch, Kinde, ...)
Backend server:
* more complex to setup
* more flexible workspace structure (multi-user workspaces, workspaces per project, etc.)
* supports self hosted auth solutions, e.g. Auth.js
* requires a backend server / serverless function
You can always switch between the two approaches without any downtime or necessary database migrations. Choose direct integration with your auth provider if you can. Otherwise, use a backend server.
### [Auth Provider Integration](#auth-provider-integration)
In the dashboard, go to the "Auth Integrations" tab and add a new integration. Follow the steps to add your auth provider.
Then, pass in a function to `authToken` that returns an ID token from your auth provider. The following is an example for Clerk:
import { AssistantCloud } from "@assistant-ui/react";
const assistantCloud = new AssistantCloud({
authToken: () => clerkClient.sessions.getToken(auth(), "assistant-ui"),
});
### [Backend Integration](#backend-integration)
In the dashboard, go to the "API Keys" tab and add a new API key.
Then, in a backend route, call `assistantCloud.auth.getToken({ workspaceId })`.
The following is an example to get auth tokens for Clerk based on the org\_id and user\_id:
/app/api/assistant-ui-token/route.ts
import { AssistantCloud } from "@assistant-ui/react";
const assistantCloud = new AssistantCloud({
apiKey: ,
});
export const POST = async (req: Request) => {
const { org_id, user_id } = auth();
if (!user_id) throw new Error("User not authenticated");
const workspaceId = org_id ? `${org_id}:${user_id}` : user_id;
const authToken = assistantCloud.auth.tokens.create({ workspaceId });
return new Response(token);
};
client.ts
import { AssistantCloud } from "@assistant-ui/react";
const assistantCloud = new AssistantCloud({
authToken: () =>
fetch("/api/assistant-ui-token", { method: "POST" }).then((r) => r.json()),
});
### [Clerk](#clerk)
First, go to the Clerk dashboard and under "Configure" tab, "JWT Templates" section, create a new template. Choose a blank template and name it "assistant-ui".
As the "Claims" field, enter the following:
{
"aud": "assistant-ui"
}
**Note:** The aud claim ensures that the JWT is only valid for the assistant-ui API.
You can leave everything else as default. Take note of the "Issuer" and "JWKS Endpoint" fields.
Then, In the assistant-cloud dashboard, navigate to the "Auth Rules" tab and create a new rule. Choose "Clerk" and enter the Issuer and JWKS Endpoint from the previous step. As the "Audience" field, enter "assistant-ui".
[Overview\
\
Previous Page](/docs/cloud/overview)
[Chat History for AI SDK\
\
Next Page](/docs/cloud/persistence/ai-sdk)
### On this page
[Workspaces](#workspaces)
[Workspace Auth Tokens](#workspace-auth-tokens)
[Choosing the right approach](#choosing-the-right-approach)
[Auth Provider Integration](#auth-provider-integration)
[Backend Integration](#backend-integration)
[Clerk](#clerk)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/cloud/authorization.mdx)
Open AssistantOpen Assistant
---
# AssistantSidebar | assistant-ui
AssistantSidebar
================
[Overview](#overview)
----------------------
A chat sidebar show on the right side of the screen. Useful for co-pilot use cases.
[Getting Started](#getting-started)
------------------------------------
### [Add `assistant-sidebar`](#add-assistant-sidebar)
npx shadcn@latest add "https://r.assistant-ui.com/assistant-sidebar"
This adds `/components/assistant-ui/assistant-sidebar.tsx` to your project, which you can adjust as needed.
### [Use in your application](#use-in-your-application)
/app/page.tsx
import { AssistantSidebar } from "@/components/assistant-ui/assistant-sidebar";
export default function Home() {
return (
{/* your app */}
);
}
[AssistantModal\
\
Previous Page](/docs/ui/AssistantModal)
[Markdown\
\
Next Page](/docs/ui/Markdown)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Add `assistant-sidebar`](#add-assistant-sidebar)
[Use in your application](#use-in-your-application)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/ui/AssistantSidebar.mdx)
Open AssistantOpen Assistant
---
# Using old React versions | assistant-ui
Using old React versions
========================
Older React Versions
Older React versions are not continuously tested. If you encounter any issues with integration, please contact us for help by joining our [Discord](https://discord.gg/S9dwgCNEFs)
.
This guide provides instructions for configuring assistant-ui to work with React 18 or older versions.
[React 18](#react-18)
----------------------
If you're using React 18, you need to update the shadcn/ui components to work with `forwardRef`. Specifically, you need to modify the Button component.
### [Updating the Button Component](#updating-the-button-component)
Navigate to your button component file (typically `/components/ui/button.tsx`) and wrap the Button component with `forwardRef`:
// Before
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot : "button";
return (
);
}
// After
const Button = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<"button"> &
VariantProps & {
asChild?: boolean;
}
>(({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
);
});
Button.displayName = "Button";
**Note:** If you're using a lower React version (17 or 16), you'll also need to follow the instructions for that version.
[React 17](#react-17)
----------------------
For React 17 compatibility, in addition to the modifications outlined for React 18, you must also include a polyfill for the `useSyncExternalStore` hook (utilized by zustand).
### [Patching Zustand with patch-package](#patching-zustand-with-patch-package)
Since the assistant-ui uses zustand internally, which depends on `useSyncExternalStore`, you'll need to patch the zustand package directly:
1. Install the required packages:
npm install use-sync-external-store patch-package
# or
yarn add use-sync-external-store patch-package
2. Add a postinstall script to your package.json:
{
"scripts": {
"postinstall": "patch-package"
}
}
3. Create a patch for zustand by creating a file at `patches/zustand+5.x.x.patch` (replace 5.x.x with the installed zustand version, which you can find by examining `package-lock.json` or `yarn.lock`) with the following content:
diff --git a/node_modules/zustand/react.js b/node_modules/zustand/react.js
index 7599cfb..64530a8 100644
--- a/node_modules/zustand/react.js
+++ b/node_modules/zustand/react.js
@@ -1,6 +1,6 @@
'use strict';
-var React = require('react');
+var React = require('use-sync-external-store/shim');
var vanilla = require('zustand/vanilla');
const identity = (arg) => arg;
@@ -10,7 +10,7 @@ function useStore(api, selector = identity) {
() => selector(api.getState()),
() => selector(api.getInitialState())
);
- React.useDebugValue(slice);
+ //React.useDebugValue(slice);
return slice;
}
const createImpl = (createState) => {
4. Run the postinstall script to apply the patch:
npm run postinstall
# or
yarn postinstall
This patch replaces the React import in zustand with the polyfill from `use-sync-external-store/shim` and comments out the `useDebugValue` call which isn't needed.
**Note:** If you're using React 16, you'll also need to follow the instructions for that version.
[React 16](#react-16)
----------------------
Incomplete Section
This section is incomplete and contributions are welcome. If you're using React 16 and have successfully integrated assistant-ui, please consider contributing to this documentation.
For React 16 compatibility, you need to apply all the steps for **React 18** and **React 17** above.
[Additional Resources](#additional-resources)
----------------------------------------------
If you encounter any issues with React compatibility, please:
1. Check that all required dependencies are installed
2. Ensure your component modifications are correctly implemented
3. Join our [Discord](https://discord.gg/S9dwgCNEFs)
community for direct support
[Custom Scrollbar\
\
Previous Page](/docs/legacy/styled/Scrollbar)
### On this page
[React 18](#react-18)
[Updating the Button Component](#updating-the-button-component)
[React 17](#react-17)
[Patching Zustand with patch-package](#patching-zustand-with-patch-package)
[React 16](#react-16)
[Additional Resources](#additional-resources)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/react-compatibility.mdx)
Open AssistantOpen Assistant
---
# makeAssistantVisible | assistant-ui
makeAssistantVisible
====================
`makeAssistantVisible` is a higher-order component (HOC) that makes React components "visible" by the assistant, allowing it to understand and interact with the component's HTML structure.
[Usage](#usage)
----------------
import { makeAssistantVisible } from "@assistant-ui/react";
const Button = ({ onClick, children }) => (
);
// Basic usage - makes component HTML readable
const ReadableButton = makeAssistantVisible(Button);
// With clickable configuration
const ClickableButton = makeAssistantVisible(Button, {
clickable: true, // Enables the click tool
});
[API Reference](#api-reference)
--------------------------------
### [Parameters](#parameters)
* `Component`: The base React component to enhance
* `config`: Optional configuration object
* `clickable`: When true, enables the assistant to programmatically click the component
### [Behavior](#behavior)
The HOC will:
1. Make the component's HTML structure available to the assistant via the system context
2. Optionally provide a `click` tool if `clickable` is true
3. Handle nested readable components (only the outermost component's HTML is provided)
4. Forward refs and maintain component props
[Example](#example)
--------------------
// Create a readable form input
const Input = ({ label, ...props }) => (
);
const ReadableInput = makeAssistantVisible(Input);
// Use in your component
function Form() {
return (
);
}
[Technical Details](#technical-details)
----------------------------------------
When a component is made readable:
* It's wrapped in a `ReadableContext.Provider` to handle nesting
* The component's `outerHTML` is provided as system context
* If `clickable` is true, a unique `data-click-id` is added and a `click` tool is provided
* The click tool uses `querySelector` and simulates a click event
* All props and refs are properly forwarded to maintain component functionality
### On this page
[Usage](#usage)
[API Reference](#api-reference)
[Parameters](#parameters)
[Behavior](#behavior)
[Example](#example)
[Technical Details](#technical-details)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/copilots/make-assistant-readable.mdx)
Open AssistantOpen Assistant
---
# Chat History for AI SDK | assistant-ui
Chat History
Chat History for AI SDK
=======================
[Overview](#overview)
----------------------
With the help of assistant-cloud, you can add thread management and thread history capabilities to assistant-ui.
This guide will walk you through the process of integrating assistant-cloud with the AI SDK by Vercel.
### [Prerequisites](#prerequisites)
You need an assistant-cloud account to follow this guide.
You can sign up here: [https://cloud.assistant-ui.com/](https://cloud.assistant-ui.com/)
### [Setting up an assistant-cloud project](#setting-up-an-assistant-cloud-project)
To get started, follow the steps below:
* Create a new project on the assistant-cloud dashboard.
* Navigate to the "Settings" tab and copy the Frontend API URL.
* Add this URL to your .env file
NEXT_PUBLIC_ASSISTANT_BASE_URL=https://
### [Displaying a ThreadList component](#displaying-a-threadlist-component)
Now, you can add a ThreadList component to your application. This component will display a list of threads and allow users to switch between them.
npx shadcn@latest add "https://r.assistant-ui.com/thread-list"
[User Authorization\
\
Previous Page](/docs/cloud/authorization)
[Chat History for LangGraph Cloud\
\
Next Page](/docs/cloud/persistence/langgraph)
### On this page
[Overview](#overview)
[Prerequisites](#prerequisites)
[Setting up an assistant-cloud project](#setting-up-an-assistant-cloud-project)
[Displaying a ThreadList component](#displaying-a-threadlist-component)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/cloud/persistence/ai-sdk.mdx)
Open AssistantOpen Assistant
---
# Chat History for LangGraph Cloud | assistant-ui
Chat History
Chat History for LangGraph Cloud
================================
[Overview](#overview)
----------------------
With the help of assistant-cloud, you can add thread management and thread history capabilities to assistant-ui.
This guide will walk you through the process of integrating assistant-cloud with LangGraph Cloud.
### [Prerequisites](#prerequisites)
You need an assistant-cloud account to follow this guide.
You can sign up here: [https://cloud.assistant-ui.com/](https://cloud.assistant-ui.com/)
### [Setting up an assistant-cloud project](#setting-up-an-assistant-cloud-project)
To get started, follow the steps below:
* Create a new project on the assistant-cloud dashboard.
* Navigate to the "Settings" tab and copy the Frontend API URL.
* Add this URL to your .env file
NEXT_PUBLIC_ASSISTANT_BASE_URL=https://
### [Connecting the runtime provider](#connecting-the-runtime-provider)
Now that we have everything set up, let's write the code for the runtime provider.
The code below is a simple LangGraph runtime provider that uses the assistant-cloud API to create and manage threads.
const useMyLangGraphRuntime = () => {
const threadListItemRuntime = useThreadListItemRuntime();
const runtime = useLangGraphRuntime({
stream: async function* (messages) {
const { externalId } = await threadListItemRuntime.initialize();
if (!externalId) throw new Error("Thread not found");
return sendMessage({ threadId: externalId, messages });
},
onSwitchToThread: async (externalId) => {
const state = await getThreadState(externalId);
return {
messages:
(state.values as { messages?: LangChainMessage[] }).messages ?? [],
};
},
});
return runtime;
};
export function MyRuntimeProvider({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const { getToken } = useAuth();
const cloud = useMemo(
() =>
new AssistantCloud({
baseUrl: process.env["NEXT_PUBLIC_ASSISTANT_BASE_URL"]!,
authToken: async () => getToken({ template: "assistant-ui" }),
}),
[getToken],
);
const runtime = useCloudThreadListRuntime({
cloud,
runtimeHook: useMyLangGraphRuntime,
create: async () => {
const { thread_id } = await createThread();
return { externalId: thread_id };
},
});
return (
{children}
);
}
Observe that the `useMyLangGraphRuntime` hook is extracted into a separate function. This hook will be mounted multiple times, once per active thread.
### [Displaying a ThreadList component](#displaying-a-threadlist-component)
Now, you can add a ThreadList component to your application. This component will display a list of threads and allow users to switch between them.
npx shadcn@latest add "https://r.assistant-ui.com/thread-list"
[Chat History for AI SDK\
\
Previous Page](/docs/cloud/persistence/ai-sdk)
[Overview\
\
Next Page](/docs/api-reference/overview)
### On this page
[Overview](#overview)
[Prerequisites](#prerequisites)
[Setting up an assistant-cloud project](#setting-up-an-assistant-cloud-project)
[Connecting the runtime provider](#connecting-the-runtime-provider)
[Displaying a ThreadList component](#displaying-a-threadlist-component)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/cloud/persistence/langgraph.mdx)
Open AssistantOpen Assistant
---
# ExternalStoreRuntime | assistant-ui
Custom Backend
ExternalStoreRuntime
====================
[Overview](#overview)
----------------------
If you need full control over the state of the messages on the frontend, use ExternalStoreRuntime.
With LocalRuntime, the chat history state is managed by assistant-ui. This gives you built-in support for thread management, message editing, reloading and branch switching.
Use the `ExternalStoreRuntime` if you want to manage the message state yourself via any react state management library.
This runtime requires a `ExternalStoreAdapter` handles communication between `assistant-ui`and your state. Unless you are storing messages as `ThreadMessage`, you need to define a `convertMessage` function to convert your messages to `ThreadMessage`.
@/app/MyRuntimeProvider.tsx
import { useState, ReactNode } from "react";
import {
useExternalStoreRuntime,
ThreadMessageLike,
AppendMessage,
AssistantRuntimeProvider,
} from "@assistant-ui/react";
const convertMessage = (message: MyMessage): ThreadMessageLike => {
return {
role: message.role,
content: [{ type: "text", text: message.content }],
};
};
export function MyRuntimeProvider({
children,
}: Readonly<{
children: ReactNode;
}>) {
const [isRunning, setIsRunning] = useState(false);
const [messages, setMessages] = useState([]);
const onNew = async (message: AppendMessage) => {
if (message.content[0]?.type !== "text")
throw new Error("Only text messages are supported");
const input = message.content[0].text;
setMessages((currentConversation) => [\
...currentConversation,\
{ role: "user", content: input },\
]);
setIsRunning(true);
const assistantMessage = await backendApi(input);
setMessages((currentConversation) => [\
...currentConversation,\
assistantMessage,\
]);
setIsRunning(false);
};
const runtime = useExternalStoreRuntime({
isRunning,
messages,
convertMessage,
onNew,
});
return (
{children}
);
}
[Accessing External Store Messages](#accessing-external-store-messages)
------------------------------------------------------------------------
You can use the `getExternalStoreMessages` utility to convert `ThreadMessage`s back to your own message type.
const MyAssistantMessage = () => {
const myMessages = useMessage((m) => getExternalStoreMessages(m));
// ...
};
Keep in mind that `getExternalStoreMessages` may return multiple messages. This is because assistant-ui merges adjacent assistant and tool messages into a single assistant message.
You can do the same operation for individual content parts as well:
const WeatherToolUI = makeAssistantToolUI({
render: () => {
const myMessages = useContentPart((p) => getExternalStoreMessages(p));
// ...
},
});
[LocalRuntime\
\
Previous Page](/docs/runtimes/custom/local)
[Overview\
\
Next Page](/docs/cloud/overview)
### On this page
[Overview](#overview)
[Accessing External Store Messages](#accessing-external-store-messages)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/runtimes/custom/external-store.mdx)
Open AssistantOpen Assistant
---
# useChatRuntime | assistant-ui
AI SDK by Vercel
useChatRuntime
==============
[Overview](#overview)
----------------------
Integration with the Vercel AI SDK UI's `useChat` hook.
It allows integration with OpenAI, Anthropic, Mistral, Perplexity, AWS Bedrock, Azure, Google Gemini, Hugging Face, Fireworks, Cohere, LangChain, Replicate, Ollama, and more.
[Getting Started](#getting-started)
------------------------------------
### [Create a Next.JS project](#create-a-nextjs-project)
npx create-next-app@latest my-app
cd my-app
### [Install Vercel AI SDK and `@assistant-ui/react`](#install-vercel-ai-sdk-and-assistant-uireact)
npm install @assistant-ui/react @assistant-ui/react-ai-sdk ai @ai-sdk/openai
### [Setup a backend route under `/api/chat`](#setup-a-backend-route-under-apichat)
`@/app/api/chat/route.ts`
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages: convertToCoreMessages(messages),
});
return result.toDataStreamResponse();
}
### [Define a `MyRuntimeProvider` component](#define-a-myruntimeprovider-component)
`@/app/MyRuntimeProvider.tsx`
"use client";
import { useChat } from "ai/react";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
export function MyRuntimeProvider({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const runtime = useChatRuntime({
api: "/api/chat",
});
return (
{children}
);
}
### [Wrap your app in `MyRuntimeProvider`](#wrap-your-app-in-myruntimeprovider)
`@/app/layout.tsx`
import { MyRuntimeProvider } from '@/app/MyRuntimeProvider';
...
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
{children}
)
}
[Accessing AI SDK Messages](#accessing-ai-sdk-messages)
--------------------------------------------------------
You can use the `getExternalStoreMessages` utility to convert `ThreadMessage`s back to `Message`s from AI SDK.
const MyAssistantMessage = () => {
const aiSDKMessages = useMessage((m) => getExternalStoreMessages(m));
// ...
};
const WeatherToolUI = makeAssistantToolUI({
render: () => {
const aiSDKMessage = useContentPart((p) => getExternalStoreMessages(p)[0]);
// ...
},
});
[Picking a Runtime\
\
Previous Page](/docs/runtimes/pick-a-runtime)
[useChat Hook Integration (Legacy)\
\
Next Page](/docs/runtimes/ai-sdk/use-chat-hook)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Create a Next.JS project](#create-a-nextjs-project)
[Install Vercel AI SDK and `@assistant-ui/react`](#install-vercel-ai-sdk-and-assistant-uireact)
[Setup a backend route under `/api/chat`](#setup-a-backend-route-under-apichat)
[Define a `MyRuntimeProvider` component](#define-a-myruntimeprovider-component)
[Wrap your app in `MyRuntimeProvider`](#wrap-your-app-in-myruntimeprovider)
[Accessing AI SDK Messages](#accessing-ai-sdk-messages)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/runtimes/ai-sdk/use-chat.mdx)
Open AssistantOpen Assistant
---
# LocalRuntime | assistant-ui
Custom Backend
LocalRuntime
============
[Overview](#overview)
----------------------
With LocalRuntime, the chat history state is managed by assistant-ui. This gives you built-in support for thread management, message editing, reloading and branch switching.
If you need full control over the state of the messages on the frontend, use ExternalStoreRuntime instead.
`assistant-ui` integrates with any custom REST API. To do so, you define a custom `ChatModelAdapter` and pass it to the `useLocalRuntime` hook.
[Getting Started](#getting-started)
------------------------------------
### [Create a Next.JS project](#create-a-nextjs-project)
npx create-next-app@latest my-app
cd my-app
### [Install `@assistant-ui/react`](#install-assistant-uireact)
npm install @assistant-ui/react
### [Define a `MyRuntimeProvider` component](#define-a-myruntimeprovider-component)
Update the `MyModelAdapter` below to integrate with your own custom API.
@/app/MyRuntimeProvider.tsx
"use client";
import type { ReactNode } from "react";
import {
AssistantRuntimeProvider,
useLocalRuntime,
type ChatModelAdapter,
} from "@assistant-ui/react";
const MyModelAdapter: ChatModelAdapter = {
async run({ messages, abortSignal }) {
// TODO replace with your own API
const result = await fetch("", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
// forward the messages in the chat to the API
body: JSON.stringify({
messages,
}),
// if the user hits the "cancel" button or escape keyboard key, cancel the request
signal: abortSignal,
});
const data = await result.json();
return {
content: [\
{\
type: "text",\
text: data.text,\
},\
],
};
},
};
export function MyRuntimeProvider({
children,
}: Readonly<{
children: ReactNode;
}>) {
const runtime = useLocalRuntime(MyModelAdapter);
return (
{children}
);
}
### [Wrap your app in `MyRuntimeProvider`](#wrap-your-app-in-myruntimeprovider)
@/app/layout.tsx
import type { ReactNode } from "react";
import { MyRuntimeProvider } from "@/app/MyRuntimeProvider";
export default function RootLayout({
children,
}: Readonly<{
children: ReactNode;
}>) {
return (
{children}
);
}
[Streaming](#streaming)
------------------------
Declare the `run` function as an `AsyncGenerator` (`async *run`). This allows you to `yield` the results as they are generated.
@/app/MyRuntimeProvider.tsx
const MyModelAdapter: ChatModelAdapter = {
async *run({ messages, abortSignal, context }) {
const stream = await backendApi({ messages, abortSignal, context });
let text = "";
for await (const part of stream) {
text += part.choices[0]?.delta?.content || "";
yield {
content: [{ type: "text", text }],
};
}
},
};
[Resuming a Run](#resuming-a-run)
----------------------------------
The `unstable_resumeRun` method is experimental and may change in future releases.
In some advanced scenarios, you might need to resume a run with a custom stream. The `ThreadRuntime.unstable_resumeRun` method allows you to do this by providing an async generator that yields chat model run results.
import { useThreadRuntime, type ChatModelRunResult } from "@assistant-ui/react";
// Get the thread runtime
const thread = useThreadRuntime();
// Create a custom stream
async function* createCustomStream(): AsyncGenerator {
let text = "Initial response";
yield {
content: [{ type: "text", text }]
};
// Simulate delay
await new Promise(resolve => setTimeout(resolve, 500));
text = "Initial response. And here's more content...";
yield {
content: [{ type: "text", text }]
};
}
// Resume a run with the custom stream
thread.unstable_resumeRun({
parentId: "message-id", // ID of the message to respond to
stream: createCustomStream() // The stream to use for resuming
});
This is particularly useful for:
* Implementing custom streaming logic
* Resuming conversations from external sources
* Creating demo or testing environments with predefined response patterns
For more detailed information, see the [ThreadRuntime.unstable\_resumeRun](/docs/api-reference/runtimes/ThreadRuntime-unstable-resumeRun)
API reference.
[Helicone\
\
Previous Page](/docs/runtimes/helicone)
[ExternalStoreRuntime\
\
Next Page](/docs/runtimes/custom/external-store)
### On this page
[Overview](#overview)
[Getting Started](#getting-started)
[Create a Next.JS project](#create-a-nextjs-project)
[Install `@assistant-ui/react`](#install-assistant-uireact)
[Define a `MyRuntimeProvider` component](#define-a-myruntimeprovider-component)
[Wrap your app in `MyRuntimeProvider`](#wrap-your-app-in-myruntimeprovider)
[Streaming](#streaming)
[Resuming a Run](#resuming-a-run)
[Edit on Github](https://github.com/assistant-ui/assistant-ui/blob/main/apps/docs/content/docs/runtimes/custom/local.mdx)
Open AssistantOpen Assistant
---