ai
Version:
AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.
354 lines (307 loc) • 10.2 kB
text/mdx
title: DirectChatTransport
description: API Reference for the DirectChatTransport class.
# `DirectChatTransport`
A transport that directly communicates with an [Agent](/docs/reference/ai-sdk-core/agent) in-process, without going through HTTP. This is useful for:
- Server-side rendering scenarios
- Testing without network
- Single-process applications
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.
```tsx
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
}
```
## Import
<Snippet text={`import { DirectChatTransport } from "ai"`} prompt={false} />
## Constructor
### Parameters
<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 (generated with `generateMessageId`, or reused from the last message when it is an assistant 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 (unless the original messages are provided and the last message is an assistant 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: 'onStepEnd',
type: 'UIMessageStreamOnStepEndCallback',
isOptional: true,
description:
'Called after each step is converted to UI message parts. Receives messages, isContinuation, and the accumulated responseMessage, including step metadata, for inspection or persistence.',
},
{
name: 'onStepFinish',
type: 'UIMessageStreamOnStepFinishCallback',
isOptional: true,
description: 'Deprecated alias for onStepEnd.',
},
{
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. Handles stream errors. Also handles failed tool calls (tool execution errors and invalid tool calls) when `redactToolErrors` is enabled, which is the default when this callback is provided.",
},
{
name: 'redactToolErrors',
type: 'boolean',
isOptional: true,
description:
'Replace the error text of failed tool calls (tool execution errors and invalid tool calls) with the result of onError. By default, the client receives the same error text as the model, because the UI messages are sent back to the model on the next request and a different text changes the message history. Defaults to true when onError is provided, and to false otherwise.',
},
]}
/>
## Methods
### `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.
```ts
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
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.
#### Returns
Always returns `Promise<null>`.
## Examples
### Basic Usage
```tsx
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';
const agent = new ToolLoopAgent({
model: openai('gpt-6-astra'),
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>
);
}
```
### With Agent Tools
```tsx
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',
inputSchema: 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-6-astra'),
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
}
```
### With Custom Agent Options
```tsx
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-6-astra'),
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
}
```
### With Reasoning
```tsx
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>
);
}
```