content/docs/07-reference/02-ai-sdk-ui/50-direct-chat-transport.mdx
DirectChatTransportA transport that directly communicates with an Agent in-process, without going through HTTP. This is useful for:
Unlike DefaultChatTransport which sends HTTP requests to an API endpoint, DirectChatTransport invokes the agent's stream() method directly and converts the result to a UI message stream.
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
__PROVIDER_IMPORT__;
const agent = new ToolLoopAgent({
model: __MODEL__,
instructions: 'You are a helpful assistant.',
});
export default function Chat() {
const { messages, sendMessage, status } = useChat({
transport: new DirectChatTransport({ agent }),
});
// ... render chat UI
}
<Snippet text={import { DirectChatTransport } from "ai"} prompt={false} />
<PropertiesTable
content={[
{
name: 'agent',
type: 'Agent',
isRequired: true,
description:
'The Agent instance to use for generating responses. The agent will be called with stream() for each message.',
},
{
name: 'options',
type: 'CALL_OPTIONS',
isOptional: true,
description:
'Options to pass to the agent when calling it. These are agent-specific options defined when creating the agent.',
},
{
name: 'originalMessages',
type: 'UIMessage[]',
isOptional: true,
description:
'The original messages. If provided, persistence mode is assumed, and a message ID is provided for the response message.',
},
{
name: 'generateMessageId',
type: 'IdGenerator',
isOptional: true,
description:
'Generate a message ID for the response message. If not provided, no message ID will be set for the response message.',
},
{
name: 'messageMetadata',
type: '(options: { part: TextStreamPart }) => METADATA | undefined',
isOptional: true,
description:
'Extracts message metadata that will be sent to the client. Called on start and finish events.',
},
{
name: 'sendReasoning',
type: 'boolean',
isOptional: true,
description: 'Send reasoning parts to the client. Defaults to true.',
},
{
name: 'sendSources',
type: 'boolean',
isOptional: true,
description: 'Send source parts to the client. Defaults to false.',
},
{
name: 'sendFinish',
type: 'boolean',
isOptional: true,
description:
'Send the finish event to the client. Set to false if you are using additional streamText calls that send additional data. Defaults to true.',
},
{
name: 'sendStart',
type: 'boolean',
isOptional: true,
description:
'Send the message start event to the client. Set to false if you are using additional streamText calls and the message start event has already been sent. Defaults to true.',
},
{
name: 'onError',
type: '(error: unknown) => string',
isOptional: true,
description:
"Process an error, e.g. to log it. Defaults to () => 'An error occurred.'. Return the error message to include in the data stream.",
},
]}
/>
sendMessages()Sends messages to the agent and returns a streaming response. This method validates and converts UI messages to model messages, calls the agent's stream() method, and returns the result as a UI message stream.
const stream = await transport.sendMessages({
chatId: 'chat-123',
trigger: 'submit-message',
messages: [...],
abortSignal: controller.signal,
});
<PropertiesTable content={[ { name: 'chatId', type: 'string', description: 'Unique identifier for the chat session.', }, { name: 'trigger', type: "'submit-message' | 'regenerate-message'", description: 'The type of message submission - either new message or regeneration.', }, { name: 'messageId', type: 'string | undefined', description: 'ID of the message to regenerate, or undefined for new messages.', }, { name: 'messages', type: 'UIMessage[]', description: 'Array of UI messages representing the conversation history.', }, { name: 'abortSignal', type: 'AbortSignal | undefined', description: 'Signal to abort the request if needed.', }, { name: 'headers', type: 'Record<string, string> | Headers', isOptional: true, description: 'Additional headers (ignored by DirectChatTransport).', }, { name: 'body', type: 'object', isOptional: true, description: 'Additional body properties (ignored by DirectChatTransport).', }, { name: 'metadata', type: 'unknown', isOptional: true, description: 'Custom metadata (ignored by DirectChatTransport).', }, ]} />
Returns a Promise<ReadableStream<UIMessageChunk>> - a stream of UI message chunks that can be processed by the chat UI.
reconnectToStream()Direct transport does not support reconnection since there is no persistent server-side stream to reconnect to.
Always returns Promise<null>.
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';
const agent = new ToolLoopAgent({
model: openai('gpt-4o'),
instructions: 'You are a helpful assistant.',
});
export default function Chat() {
const { messages, sendMessage, status } = useChat({
transport: new DirectChatTransport({ agent }),
});
return (
<div>
{messages.map(message => (
<div key={message.id}>
{message.role === 'user' ? 'User: ' : 'AI: '}
{message.parts.map((part, index) =>
part.type === 'text' ? <span key={index}>{part.text}</span> : null,
)}
</div>
))}
<button onClick={() => sendMessage({ text: 'Hello!' })}>Send</button>
</div>
);
}
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const weatherTool = tool({
description: 'Get the current weather',
parameters: z.object({
location: z.string().describe('The city and state'),
}),
execute: async ({ location }) => {
return `The weather in ${location} is sunny and 72°F.`;
},
});
const agent = new ToolLoopAgent({
model: openai('gpt-4o'),
instructions: 'You are a helpful assistant with access to weather data.',
tools: { weather: weatherTool },
});
export default function Chat() {
const { messages, sendMessage } = useChat({
transport: new DirectChatTransport({ agent }),
});
// ... render chat UI with tool results
}
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';
const agent = new ToolLoopAgent<{ userId: string }>({
model: openai('gpt-4o'),
prepareCall: ({ options, ...rest }) => ({
...rest,
providerOptions: {
openai: { user: options.userId },
},
}),
});
export default function Chat({ userId }: { userId: string }) {
const { messages, sendMessage } = useChat({
transport: new DirectChatTransport({
agent,
options: { userId },
}),
});
// ... render chat UI
}
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';
const agent = new ToolLoopAgent({
model: openai('o1-preview'),
});
export default function Chat() {
const { messages, sendMessage } = useChat({
transport: new DirectChatTransport({
agent,
sendReasoning: true,
}),
});
return (
<div>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, index) => {
if (part.type === 'text') {
return <p key={index}>{part.text}</p>;
}
if (part.type === 'reasoning') {
return (
<pre key={index} style={{ opacity: 0.6 }}>
{part.text}
</pre>
);
}
return null;
})}
</div>
))}
</div>
);
}