OpenAI Assistants
The useAssistant hook allows you to handle the client state when interacting with an OpenAI compatible assistant API.
This hook is useful when you want to integrate assistant capabilities into your application,
with the UI updated automatically as the assistant is streaming its execution.
The useAssistant hook is supported in @ai-sdk/react, ai/svelte, and ai/vue.
Example
'use client';
import { Message, useAssistant } from '@ai-sdk/react';
export default function Chat() { const { status, messages, input, submitMessage, handleInputChange } = useAssistant({ api: '/api/assistant' });
return ( <div> {messages.map((m: Message) => ( <div key={m.id}> <strong>{`${m.role}: `}</strong> {m.role !== 'data' && m.content} {m.role === 'data' && ( <> {(m.data as any).description} <br /> <pre className={'bg-gray-200'}> {JSON.stringify(m.data, null, 2)} </pre> </> )} </div> ))}
{status === 'in_progress' && <div />}
<form onSubmit={submitMessage}> <input disabled={status !== 'awaiting_message'} value={input} placeholder="What is the temperature in the living room?" onChange={handleInputChange} /> </form> </div> );}import { AssistantResponse } from 'ai';import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY || '',});
// Allow streaming responses up to 30 secondsexport const maxDuration = 30;
export async function POST(req: Request) { // Parse the request body const input: { threadId: string | null; message: string; } = await req.json();
// Create a thread if needed const threadId = input.threadId ?? (await openai.beta.threads.create({})).id;
// Add a message to the thread const createdMessage = await openai.beta.threads.messages.create(threadId, { role: 'user', content: input.message, });
return AssistantResponse( { threadId, messageId: createdMessage.id }, async ({ forwardStream, sendDataMessage }) => { // Run the assistant on the thread const runStream = openai.beta.threads.runs.stream(threadId, { assistant_id: process.env.ASSISTANT_ID ?? (() => { throw new Error('ASSISTANT_ID is not set'); })(), });
// forward run status would stream message deltas let runResult = await forwardStream(runStream);
// status can be: queued, in_progress, requires_action, cancelling, cancelled, failed, completed, or expired while ( runResult?.status === 'requires_action' && runResult.required_action?.type === 'submit_tool_outputs' ) { const tool_outputs = runResult.required_action.submit_tool_outputs.tool_calls.map( (toolCall: any) => { const parameters = JSON.parse(toolCall.function.arguments);
switch (toolCall.function.name) { // configure your tool calls here
default: throw new Error( `Unknown tool call function: ${toolCall.function.name}`, ); } }, );
runResult = await forwardStream( openai.beta.threads.runs.submitToolOutputsStream( threadId, runResult.id, { tool_outputs }, ), ); } }, );}Customized UI
useAssistant also provides ways to manage the chat message and input states via code and show loading and error states.
Loading and error states
To show a loading spinner while the assistant is running the thread, you can use the status state returned by the useAssistant hook:
const { status, ... } = useAssistant()
return( <> {status === "in_progress" ? <Spinner /> : null} </>)Similarly, the error state reflects the error object thrown during the fetch request. It can be used to display an error message, or show a toast notification:
const { error, ... } = useAssistant()
useEffect(() => { if (error) { toast.error(error.message) }}, [error])
// Or display the error message in the UI:return ( <> {error ? <div>{error.message}</div> : null} </>)Controlled input
In the initial example, we have handleSubmit and handleInputChange callbacks that manage the input changes and form submissions. These are handy for common use cases, but you can also use uncontrolled APIs for more advanced scenarios such as form validation or customized components.
The following example demonstrates how to use more granular APIs like append with your custom input and submit button components:
const { append } = useAssistant();
return ( <> <MySubmitButton onClick={() => { // Send a new message to the AI provider append({ role: 'user', content: input, }); }} /> </>);Configure Request Options
By default, the useAssistant hook sends a HTTP POST request to the /api/assistant endpoint with the prompt as part of the request body. You can customize the request by passing additional options to the useAssistant hook:
const { messages, input, handleInputChange, handleSubmit } = useAssistant({ api: '/api/custom-completion', headers: { Authorization: 'your_token', }, body: { user_id: '123', }, credentials: 'same-origin',});In this example, the useAssistant hook sends a POST request to the /api/custom-completion endpoint with the specified headers, additional body fields, and credentials for that fetch request. On your server side, you can handle the request with these additional information.