DirectChatTransport — HTTP 없이 에이전트와 직접 통신

DirectChatTransport — HTTP 없이 에이전트와 직접 통신

DirectChatTransport는 Agent와 in-process로 직접 통신하는 트랜스포트예요. HTTP를 거치지 않고요. 다음과 같은 상황에서 유용해요:

  • 서버 사이드 렌더링 시나리오
  • 네트워크 없이 테스트하기
  • 단일 프로세스 애플리케이션

출처: 문서

본문

Agent와 in-process로 직접 통신하는 트랜스포트예요. HTTP를 거치지 않고요. 다음과 같은 상황에서 유용해요:

  • 서버 사이드 렌더링 시나리오
  • 네트워크 없이 테스트하기
  • 단일 프로세스 애플리케이션

DefaultChatTransport가 API 엔드포인트에 HTTP 요청을 보내는 것과 달리, DirectChatTransport는 에이전트의 stream() 메서드를 직접 호출하고 결과를 UI 메시지 스트림으로 변환해요.

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

import { DirectChatTransport } from "ai"

생성자 (Constructor)

파라미터

  • agent: Agent (필수) — 응답 생성에 사용할 Agent 인스턴스. 각 메시지에 대해 에이전트가 stream()으로 호출돼요.
  • options: CALL_OPTIONS (선택) — 에이전트를 호출할 때 넘길 옵션. 에이전트를 만들 때 정의한 에이전트 특정 옵션이에요.
  • originalMessages: UIMessage[] (선택) — 원본 메시지. 제공하면 영속 모드로 간주되고 응답 메시지에 메시지 ID가 제공돼요.
  • generateMessageId: IdGenerator (선택) — 응답 메시지의 메시지 ID를 생성해요. 제공하지 않으면 응답 메시지에 메시지 ID가 설정되지 않아요.
  • messageMetadata: (options: { part: TextStreamPart }) => METADATA | undefined (선택) — 클라이언트로 보낼 메시지 메타데이터를 추출해요. start와 finish 이벤트에서 호출돼요.
  • sendReasoning: boolean (선택) — reasoning 부분을 클라이언트로 보낼지 여부. 기본값은 true.
  • sendSources: boolean (선택) — source 부분을 클라이언트로 보낼지 여부. 기본값은 false.
  • sendFinish: boolean (선택) — finish 이벤트를 클라이언트로 보낼지 여부. 추가 데이터를 보내는 추가 streamText 호출을 사용한다면 false로 설정하세요. 기본값은 true.
  • sendStart: boolean (선택) — 메시지 start 이벤트를 클라이언트로 보낼지 여부. 추가 streamText 호출을 사용하고 메시지 start 이벤트를 이미 보냈다면 false로 설정하세요. 기본값은 true.
  • onError: (error: unknown) => string (선택) — 에러를 처리합니다 (예: 로깅). 기본값은 () => 'An error occurred.'. 데이터 스트림에 포함할 에러 메시지를 반환해요.

메서드

sendMessages()

메시지를 에이전트로 보내고 스트리밍 응답을 반환해요. 이 메서드는 UI 메시지를 검증하고 모델 메시지로 변환하며, 에이전트의 stream() 메서드를 호출하고 결과를 UI 메시지 스트림으로 반환해요.

const stream = await transport.sendMessages({
  chatId: 'chat-123',
  trigger: 'submit-message',
  messages: [...],
  abortSignal: controller.signal,
});
  • chatId: string — 채팅 세션의 고유 식별자.
  • trigger: 'submit-message' | 'regenerate-message' — 메시지 제출 유형 — 새 메시지 또는 재생성.
  • messageId: string | undefined — 재생성할 메시지의 ID, 새 메시지면 undefined.
  • messages: UIMessage[] — 대화 기록을 나타내는 UI 메시지 배열.
  • abortSignal: AbortSignal | undefined — 필요 시 요청을 중단하는 신호.
  • headers: Record<string, string> | Headers (선택) — 추가 헤더 (DirectChatTransport는 무시).
  • body: object (선택) — 추가 body 속성 (DirectChatTransport는 무시).
  • metadata: unknown (선택) — 커스텀 메타데이터 (DirectChatTransport는 무시).
반환값

Promise<ReadableStream<UIMessageChunk>>를 반환해요 — 채팅 UI가 처리할 수 있는 UI 메시지 청크의 스트림이에요.

reconnectToStream()

직접 트랜스포트는 재연결할 영속 서버 측 스트림이 없으므로 재연결을 지원하지 않아요.

반환값

항상 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-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>
  );
}

에이전트 도구와 함께

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
}

커스텀 에이전트 옵션과 함께

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
}

Reasoning과 함께

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>
  );
}

더 알아보기 (Learn more)