UNPKG

ai

Version:

AI SDK by Vercel - The AI Toolkit for TypeScript and JavaScript

537 lines (484 loc) 12.7 kB
import { InferToolInput, InferToolOutput, Tool, ToolCall, } from '@ai-sdk/provider-utils'; import { ToolSet } from '../generate-text'; import { ProviderMetadata } from '../types/provider-metadata'; import { DeepPartial } from '../util/deep-partial'; import { ValueOf } from '../util/value-of'; /** * The data types that can be used in the UI message for the UI message data parts. */ export type UIDataTypes = Record<string, unknown>; export type UITool = { input: unknown; output: unknown | undefined; }; /** * Infer the input and output types of a tool so it can be used as a UI tool. */ export type InferUITool<TOOL extends Tool> = { input: InferToolInput<TOOL>; output: InferToolOutput<TOOL>; }; /** * Infer the input and output types of a tool set so it can be used as a UI tool set. */ export type InferUITools<TOOLS extends ToolSet> = { [NAME in keyof TOOLS & string]: InferUITool<TOOLS[NAME]>; }; export type UITools = Record<string, UITool>; /** * AI SDK UI Messages. They are used in the client and to communicate between the frontend and the API routes. */ export 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. * * System messages should be avoided (set the system prompt on the server instead). * They can have text parts. * * User messages can have text parts and file parts. * * Assistant messages can have text, reasoning, tool invocation, and file parts. */ parts: Array<UIMessagePart<DATA_PARTS, TOOLS>>; } export type UIMessagePart< DATA_TYPES extends UIDataTypes, TOOLS extends UITools, > = | TextUIPart | ReasoningUIPart | ToolUIPart<TOOLS> | DynamicToolUIPart | SourceUrlUIPart | SourceDocumentUIPart | FileUIPart | DataUIPart<DATA_TYPES> | StepStartUIPart; /** * A text part of a message. */ export type TextUIPart = { type: 'text'; /** * The text content. */ text: string; /** * The state of the text part. */ state?: 'streaming' | 'done'; /** * The provider metadata. */ providerMetadata?: ProviderMetadata; }; /** * A reasoning part of a message. */ export type ReasoningUIPart = { type: 'reasoning'; /** * The reasoning text. */ text: string; /** * The state of the reasoning part. */ state?: 'streaming' | 'done'; /** * The provider metadata. */ providerMetadata?: ProviderMetadata; }; /** * A source part of a message. */ export type SourceUrlUIPart = { type: 'source-url'; sourceId: string; url: string; title?: string; providerMetadata?: ProviderMetadata; }; /** * A document source part of a message. */ export type SourceDocumentUIPart = { type: 'source-document'; sourceId: string; mediaType: string; title: string; filename?: string; providerMetadata?: ProviderMetadata; }; /** * A file part of a message. */ export type FileUIPart = { type: 'file'; /** * IANA media type of the file. * * @see https://www.iana.org/assignments/media-types/media-types.xhtml */ 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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs). */ url: string; /** * The provider metadata. */ providerMetadata?: ProviderMetadata; }; /** * A step boundary part of a message. */ export type StepStartUIPart = { type: 'step-start'; }; export type DataUIPart<DATA_TYPES extends UIDataTypes> = ValueOf<{ [NAME in keyof DATA_TYPES & string]: { type: `data-${NAME}`; id?: string; data: DATA_TYPES[NAME]; }; }>; type asUITool<TOOL extends UITool | Tool> = TOOL extends Tool ? InferUITool<TOOL> : TOOL; /** * Check if a message part is a data part. */ export function isDataUIPart<DATA_TYPES extends UIDataTypes>( part: UIMessagePart<DATA_TYPES, UITools>, ): part is DataUIPart<DATA_TYPES> { return part.type.startsWith('data-'); } /** * A UI tool invocation contains all the information needed to render a tool invocation in the UI. * It can be derived from a tool without knowing the tool name, and can be used to define * UI components for the tool. */ export type UIToolInvocation<TOOL extends UITool | Tool> = { /** * ID of the tool call. */ toolCallId: string; title?: string; /** * Whether the tool call was executed by the provider. */ providerExecuted?: boolean; } & ( | { state: 'input-streaming'; input: DeepPartial<asUITool<TOOL>['input']> | undefined; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval?: never; } | { state: 'input-available'; input: asUITool<TOOL>['input']; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval?: never; } | { state: 'approval-requested'; input: asUITool<TOOL>['input']; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval: { id: string; approved?: never; reason?: never; }; } | { state: 'approval-responded'; input: asUITool<TOOL>['input']; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval: { id: string; approved: boolean; reason?: string; }; } | { state: 'output-available'; input: asUITool<TOOL>['input']; output: asUITool<TOOL>['output']; errorText?: never; callProviderMetadata?: ProviderMetadata; preliminary?: boolean; approval?: { id: string; approved: true; reason?: string; }; } | { state: 'output-error'; // TODO AI SDK 6: change to 'error' state input: asUITool<TOOL>['input'] | undefined; rawInput?: unknown; // TODO AI SDK 6: remove this field, input should be unknown output?: never; errorText: string; callProviderMetadata?: ProviderMetadata; approval?: { id: string; approved: true; reason?: string; }; } | { state: 'output-denied'; input: asUITool<TOOL>['input']; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval: { id: string; approved: false; reason?: string; }; } ); export type ToolUIPart<TOOLS extends UITools = UITools> = ValueOf<{ [NAME in keyof TOOLS & string]: { type: `tool-${NAME}`; } & UIToolInvocation<TOOLS[NAME]>; }>; export type DynamicToolUIPart = { type: 'dynamic-tool'; /** * Name of the tool that is being called. */ toolName: string; /** * ID of the tool call. */ toolCallId: string; title?: string; /** * Whether the tool call was executed by the provider. */ providerExecuted?: boolean; } & ( | { state: 'input-streaming'; input: unknown | undefined; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval?: never; } | { state: 'input-available'; input: unknown; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval?: never; } | { state: 'approval-requested'; input: unknown; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval: { id: string; approved?: never; reason?: never; }; } | { state: 'approval-responded'; input: unknown; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval: { id: string; approved: boolean; reason?: string; }; } | { state: 'output-available'; input: unknown; output: unknown; errorText?: never; callProviderMetadata?: ProviderMetadata; preliminary?: boolean; approval?: { id: string; approved: true; reason?: string; }; } | { state: 'output-error'; // TODO AI SDK 6: change to 'error' state input: unknown; output?: never; errorText: string; callProviderMetadata?: ProviderMetadata; approval?: { id: string; approved: true; reason?: string; }; } | { state: 'output-denied'; input: unknown; output?: never; errorText?: never; callProviderMetadata?: ProviderMetadata; approval: { id: string; approved: false; reason?: string; }; } ); /** * Type guard to check if a message part is a text part. */ export function isTextUIPart( part: UIMessagePart<UIDataTypes, UITools>, ): part is TextUIPart { return part.type === 'text'; } /** * Type guard to check if a message part is a file part. */ export function isFileUIPart( part: UIMessagePart<UIDataTypes, UITools>, ): part is FileUIPart { return part.type === 'file'; } /** * Type guard to check if a message part is a reasoning part. */ export function isReasoningUIPart( part: UIMessagePart<UIDataTypes, UITools>, ): part is ReasoningUIPart { return part.type === 'reasoning'; } /** * Check if a message part is a static tool part. * * Static tools are tools for which the types are known at development time. */ export function isStaticToolUIPart<TOOLS extends UITools>( part: UIMessagePart<UIDataTypes, TOOLS>, ): part is ToolUIPart<TOOLS> { return part.type.startsWith('tool-'); } /** * Check if a message part is a dynamic tool part. * * Dynamic tools are tools for which the input and output types are unknown. */ export function isDynamicToolUIPart( part: UIMessagePart<UIDataTypes, UITools>, ): part is DynamicToolUIPart { return part.type === 'dynamic-tool'; } /** * Check if a message part is a tool part. * * Tool parts are either static or dynamic tools. * * Use `isStaticToolUIPart` or `isDynamicToolUIPart` to check the type of the tool. */ export function isToolUIPart<TOOLS extends UITools>( part: UIMessagePart<UIDataTypes, TOOLS>, ): part is ToolUIPart<TOOLS> | DynamicToolUIPart { return isStaticToolUIPart(part) || isDynamicToolUIPart(part); } /** * @deprecated Use isToolUIPart instead. */ export const isToolOrDynamicToolUIPart = isToolUIPart; /** * Returns the name of the static tool. * * The possible values are the keys of the tool set. */ export function getStaticToolName<TOOLS extends UITools>( part: ToolUIPart<TOOLS>, ): keyof TOOLS { return part.type.split('-').slice(1).join('-') as keyof TOOLS; } /** * Returns the name of the tool (static or dynamic). * * This function will not restrict the name to the keys of the tool set. * If you need to restrict the name to the keys of the tool set, use `getStaticToolName` instead. */ export function getToolName( part: ToolUIPart<UITools> | DynamicToolUIPart, ): string { return isDynamicToolUIPart(part) ? part.toolName : getStaticToolName(part); } /** * @deprecated Use getToolName instead. */ export const getToolOrDynamicToolName = getToolName; export type InferUIMessageMetadata<T extends UIMessage> = T extends UIMessage<infer METADATA> ? METADATA : unknown; export type InferUIMessageData<T extends UIMessage> = T extends UIMessage<unknown, infer DATA_TYPES> ? DATA_TYPES : UIDataTypes; export type InferUIMessageTools<T extends UIMessage> = T extends UIMessage<unknown, UIDataTypes, infer TOOLS> ? TOOLS : UITools; export type InferUIMessageToolOutputs<UI_MESSAGE extends UIMessage> = InferUIMessageTools<UI_MESSAGE>[keyof InferUIMessageTools<UI_MESSAGE>]['output']; export type InferUIMessageToolCall<UI_MESSAGE extends UIMessage> = | ValueOf<{ [NAME in keyof InferUIMessageTools<UI_MESSAGE>]: ToolCall< NAME & string, InferUIMessageTools<UI_MESSAGE>[NAME] extends { input: infer INPUT } ? INPUT : never > & { dynamic?: false }; }> | (ToolCall<string, unknown> & { dynamic: true }); export type InferUIMessagePart<UI_MESSAGE extends UIMessage> = UIMessagePart< InferUIMessageData<UI_MESSAGE>, InferUIMessageTools<UI_MESSAGE> >;