`UIMessage`

UIMessage

UI 렌더링과 클라이언트 기능에 필요한 전체 메시지 상태를 나타내는 타입이에요. 애플리케이션 상태의 진실의 원천(source of truth) 역할을 해요.

출처: 문서

본문

UIMessage는 애플리케이션 상태의 진실의 원천(source of truth) 역할을 해요. 메타데이터, 데이터 파트, 모든 컨텍스트 정보를 포함한 전체 메시지 기록을 나타내죠. 모델에 전달되는 상태나 컨텍스트를 나타내는 ModelMessage와 달리, UIMessage는 UI 렌더링과 클라이언트 측 기능에 필요한 전체 애플리케이션 상태를 담고 있어요.

타입 안전성

UIMessage는 타입 안전하게 설계되었으며, 애플리케이션 전반에서 올바른 타이핑을 보장하는 세 가지 제네릭 파라미터를 받아요:

  1. METADATA — 추가 메시지 정보를 위한 커스텀 메타데이터 타입
  2. DATA_PARTS — 구조화된 데이터 컴포넌트를 위한 커스텀 데이터 파트 타입
  3. TOOLS — 타입 안전한 툴 상호작용을 위한 툴 정의

나만의 UIMessage 타입 만들기

애플리케이션용 커스텀 타입의 UIMessage를 만드는 예시예요:

import { InferUITools, ToolSet, UIMessage, tool } from 'ai';
import z from 'zod';

const metadataSchema = z.object({
  someMetadata: z.string().datetime(),
});

type MyMetadata = z.infer<typeof metadataSchema>;

const dataPartSchema = z.object({
  someDataPart: z.object({}),
  anotherDataPart: z.object({}),
});

type MyDataPart = z.infer<typeof dataPartSchema>;

const tools = {
  someTool: tool({}),
} satisfies ToolSet;

type MyTools = InferUITools<typeof tools>;

export type MyUIMessage = UIMessage<MyMetadata, MyDataPart, MyTools>;

UIMessage 인터페이스

interface UIMessage<
  METADATA = unknown,
  DATA_PARTS extends UIDataTypes = UIDataTypes,
  TOOLS extends UITools = UITools,
> {
  /**
   * A unique identifier for the message.
   */
  id: string;

  /**
   * The role of the message.
   */
  role: 'system' | 'user' | 'assistant';

  /**
   * The metadata of the message.
   */
  metadata?: METADATA;

  /**
   * The parts of the message. Use this for rendering the message in the UI.
   */
  parts: Array<UIMessagePart<DATA_PARTS, TOOLS>>;
}

UIMessagePart 타입들

TextUIPart

메시지의 텍스트 파트예요.

type TextUIPart = {
  type: 'text';
  /**
   * The text content.
   */
  text: string;
  /**
   * The state of the text part.
   */
  state?: 'streaming' | 'done';
};

ReasoningUIPart

메시지의 추론(reasoning) 파트예요.

type ReasoningUIPart = {
  type: 'reasoning';
  /**
   * The reasoning part ID.
   */
  id?: string;
  /**
   * The reasoning text.
   */
  text: string;
  /**
   * The state of the reasoning part.
   */
  state?: 'streaming' | 'done';
  /**
   * The provider metadata.
   */
  providerMetadata?: Record<string, any>;
};

ToolUIPart

툴 호출과 그 결과를 나타내는 메시지의 툴 파트예요.

타입은 툴 이름을 기반으로 해요. 예를 들어 `someTool`이라는 툴은 `tool-someTool`이 돼요.
type ToolUIPart<TOOLS extends UITools = UITools> = ValueOf<{
  [NAME in keyof TOOLS & string]: {
    type: `tool-${NAME}`;
    toolCallId: string;
  } & (
    | {
        state: 'input-streaming';
        input: DeepPartial<TOOLS[NAME]['input']> | undefined;
        providerExecuted?: boolean;
        output?: never;
        errorText?: never;
      }
    | {
        state: 'input-available';
        input: TOOLS[NAME]['input'];
        providerExecuted?: boolean;
        output?: never;
        errorText?: never;
      }
    | {
        state: 'approval-requested';
        input: TOOLS[NAME]['input'];
        output?: never;
        errorText?: never;
        approval: {
          id: string;
          approved?: never;
          descriptor?: unknown;
          requestReason?: string;
          reason?: never;
          isAutomatic?: boolean;
          signature?: string;
        };
      }
    | {
        state: 'approval-responded';
        input: TOOLS[NAME]['input'];
        output?: never;
        errorText?: never;
        approval: {
          id: string;
          approved: boolean;
          descriptor?: unknown;
          requestReason?: string;
          reason?: string;
          isAutomatic?: boolean;
          signature?: string;
        };
      }
    | {
        state: 'output-available';
        input: TOOLS[NAME]['input'];
        output: TOOLS[NAME]['output'];
        errorText?: never;
        providerExecuted?: boolean;
      }
    | {
        state: 'output-error';
        input: TOOLS[NAME]['input'];
        output?: never;
        errorText: string;
        providerExecuted?: boolean;
      }
  );
}>;

approval.descriptor에는 승인 요청 스트림 청크에 approvalDescriptor로 제공된 선택적 불투명(opaque) 메타데이터가 담겨요. 툴 파트가 approval-requested에서 approval-responded로 전환될 때와 이후 승인을 포함한 출력 상태에서 보존돼요.

ToolOutputErrorUIPart

실행에 실패한 정적 또는 동적 툴 파트예요. 메시지를 렌더링할 때 isToolOutputErrorUIPart 타입 가드를 사용하면 코드에서 툴 상태 판별자(discriminator)를 직접 확인할 필요가 없어요.

import { isToolOutputErrorUIPart, type UIMessage } from 'ai';

function ToolError({ part }: { part: UIMessage['parts'][number] }) {
  if (!isToolOutputErrorUIPart(part)) {
    return null;
  }

  return <div role="alert">{part.errorText}</div>;
}

제네릭 ToolOutputErrorUIPart<TOOLS> 타입은 정적 툴의 입력 타입을 보존하면서 동적 툴 오류도 포함해요:

type ToolOutputErrorUIPart<TOOLS extends UITools = UITools> = Extract<
  ToolUIPart<TOOLS> | DynamicToolUIPart,
  { state: 'output-error' }
>;

CustomContentUIPart

프로바이더별 커스텀 콘텐츠 파트예요.

type CustomContentUIPart = {
  type: 'custom';
  /**
   * The kind of custom content, in the format `{provider}.{provider-type}`.
   */
  kind: `${string}.${string}`;
  /**
   * The provider metadata.
   */
  providerMetadata?: Record<string, any>;
};

SourceUrlUIPart

메시지의 소스 URL 파트예요.

type SourceUrlUIPart = {
  type: 'source-url';
  sourceId: string;
  url: string;
  title?: string;
  providerMetadata?: Record<string, any>;
};

SourceDocumentUIPart

메시지의 문서 소스 파트예요.

type SourceDocumentUIPart = {
  type: 'source-document';
  sourceId: string;
  mediaType: string;
  title: string;
  filename?: string;
  providerMetadata?: Record<string, any>;
};

FileUIPart

메시지의 파일 파트예요.

type FileUIPart = {
  type: 'file';
  /**
   * IANA media type of the file.
   */
  mediaType: string;
  /**
   * Optional filename of the file.
   */
  filename?: string;
  /**
   * The URL of the file.
   * It can either be a URL to a hosted file or a Data URL.
   */
  url: string;
};

DataUIPart

커스텀 데이터 타입을 위한 메시지의 데이터 파트예요.

타입은 데이터 파트 이름을 기반으로 해요. 예를 들어 `someDataPart`라는 데이터 파트는 `data-someDataPart`가 돼요.
type DataUIPart<DATA_TYPES extends UIDataTypes> = ValueOf<{
  [NAME in keyof DATA_TYPES & string]: {
    type: `data-${NAME}`;
    id?: string;
    data: DATA_TYPES[NAME];
  };
}>;

StepStartUIPart

메시지의 단계(step) 경계 파트예요.

type StepStartUIPart = {
  type: 'step-start';
};

더 알아보기 (Learn more)

전체 사이트맵