@ai-sdk/rsc
Version:
[React Server Components](https://react.dev/reference/rsc/server-components) for the [AI SDK](https://ai-sdk.dev/docs):
383 lines (371 loc) • 14.7 kB
TypeScript
import { LanguageModelV2 } from '@ai-sdk/provider';
import { ProviderOptions, InferSchema } from '@ai-sdk/provider-utils';
import { ReactNode } from 'react';
import * as z3 from 'zod/v3';
import * as z4 from 'zod/v4';
import { Schema, CallSettings, Prompt, ToolChoice, FinishReason, LanguageModelUsage, CallWarning } from 'ai';
type JSONValue = string | number | boolean | JSONObject | JSONArray;
interface JSONObject {
[x: string]: JSONValue;
}
interface JSONArray extends Array<JSONValue> {
}
type AIAction<T = any, R = any> = (...args: T[]) => Promise<R>;
type AIActions<T = any, R = any> = Record<string, AIAction<T, R>>;
type ServerWrappedAction<T = unknown> = (aiState: T, ...args: unknown[]) => Promise<[Promise<T>, unknown]>;
type ServerWrappedActions<T = unknown> = Record<string, ServerWrappedAction<T>>;
type InternalAIProviderProps<AIState = any, UIState = any> = {
children: React.ReactNode;
initialUIState: UIState;
initialAIState: AIState;
initialAIStatePatch: undefined | Promise<AIState>;
wrappedActions: ServerWrappedActions<AIState>;
wrappedSyncUIState?: ServerWrappedAction<AIState>;
};
type AIProviderProps<AIState = any, UIState = any, Actions = any> = {
children: React.ReactNode;
initialAIState?: AIState;
initialUIState?: UIState;
/** $ActionTypes is only added for type inference and is never used at runtime **/
$ActionTypes?: Actions;
};
type AIProvider<AIState = any, UIState = any, Actions = any> = (props: AIProviderProps<AIState, UIState, Actions>) => Promise<React.ReactElement>;
type InferAIState<T, Fallback> = T extends AIProvider<infer AIState, any, any> ? AIState : Fallback;
type InferUIState<T, Fallback> = T extends AIProvider<any, infer UIState, any> ? UIState : Fallback;
type InferActions<T, Fallback> = T extends AIProvider<any, any, infer Actions> ? Actions : Fallback;
type InternalAIStateStorageOptions = {
onSetAIState?: OnSetAIState<any>;
};
type OnSetAIState<S> = ({ key, state, done, }: {
key: string | number | symbol | undefined;
state: S;
done: boolean;
}) => void | Promise<void>;
type OnGetUIState<S> = AIAction<void, S | undefined>;
type ValueOrUpdater<T> = T | ((current: T) => T);
type MutableAIState<AIState> = {
get: () => AIState;
update: (newState: ValueOrUpdater<AIState>) => void;
done: ((newState: AIState) => void) | (() => void);
};
/**
* Get the current AI state.
* If `key` is provided, it will return the value of the specified key in the
* AI state, if it's an object. If it's not an object, it will throw an error.
*
* @example const state = getAIState() // Get the entire AI state
* @example const field = getAIState('key') // Get the value of the key
*/
declare function getAIState<AI extends AIProvider = any>(): Readonly<InferAIState<AI, any>>;
declare function getAIState<AI extends AIProvider = any>(key: keyof InferAIState<AI, any>): Readonly<InferAIState<AI, any>[typeof key]>;
/**
* Get the mutable AI state. Note that you must call `.done()` when finishing
* updating the AI state.
*
* @example
* ```tsx
* const state = getMutableAIState()
* state.update({ ...state.get(), key: 'value' })
* state.update((currentState) => ({ ...currentState, key: 'value' }))
* state.done()
* ```
*
* @example
* ```tsx
* const state = getMutableAIState()
* state.done({ ...state.get(), key: 'value' }) // Done with a new state
* ```
*/
declare function getMutableAIState<AI extends AIProvider = any>(): MutableAIState<InferAIState<AI, any>>;
declare function getMutableAIState<AI extends AIProvider = any>(key: keyof InferAIState<AI, any>): MutableAIState<InferAIState<AI, any>[typeof key]>;
declare function createAI<AIState = any, UIState = any, Actions extends AIActions = {}>({ actions, initialAIState, initialUIState, onSetAIState, onGetUIState, }: {
actions: Actions;
initialAIState?: AIState;
initialUIState?: UIState;
/**
* This function is called whenever the AI state is updated by an Action.
* You can use this to persist the AI state to a database, or to send it to a
* logging service.
*/
onSetAIState?: OnSetAIState<AIState>;
/**
* This function is used to retrieve the UI state based on the AI state.
* For example, to render the initial UI state based on a given AI state, or
* to sync the UI state when the application is already loaded.
*
* If returning `undefined`, the client side UI state will not be updated.
*
* This function must be annotated with the `"use server"` directive.
*
* @example
* ```tsx
* onGetUIState: async () => {
* 'use server';
*
* const currentAIState = getAIState();
* const externalAIState = await loadAIStateFromDatabase();
*
* if (currentAIState === externalAIState) return undefined;
*
* // Update current AI state and return the new UI state
* const state = getMutableAIState()
* state.done(externalAIState)
*
* return <div>...</div>;
* }
* ```
*/
onGetUIState?: OnGetUIState<UIState>;
}): AIProvider<AIState, UIState, Actions>;
type Streamable = ReactNode | Promise<ReactNode>;
type Renderer<T extends Array<any>> = (...args: T) => Streamable | Generator<Streamable, Streamable, void> | AsyncGenerator<Streamable, Streamable, void>;
type RenderTool<INPUT_SCHEMA extends z4.core.$ZodType | z3.Schema | Schema = any> = {
description?: string;
inputSchema: INPUT_SCHEMA;
generate?: Renderer<[
InferSchema<INPUT_SCHEMA>,
{
toolName: string;
toolCallId: string;
}
]>;
};
type RenderText = Renderer<[
{
/**
* The full text content from the model so far.
*/
content: string;
/**
* The new appended text content from the model since the last `text` call.
*/
delta: string;
/**
* Whether the model is done generating text.
* If `true`, the `content` will be the final output and this call will be the last.
*/
done: boolean;
}
]>;
type RenderResult = {
value: ReactNode;
} & Awaited<ReturnType<LanguageModelV2['doStream']>>;
/**
* `streamUI` is a helper function to create a streamable UI from LLMs.
*/
declare function streamUI<TOOLS extends {
[name: string]: z4.core.$ZodType | z3.Schema | Schema;
} = {}>({ model, tools, toolChoice, system, prompt, messages, maxRetries, abortSignal, headers, initial, text, providerOptions, onFinish, ...settings }: CallSettings & Prompt & {
/**
* The language model to use.
*/
model: LanguageModelV2;
/**
* The tools that the model can call. The model needs to support calling tools.
*/
tools?: {
[name in keyof TOOLS]: RenderTool<TOOLS[name]>;
};
/**
* The tool choice strategy. Default: 'auto'.
*/
toolChoice?: ToolChoice<TOOLS>;
text?: RenderText;
initial?: ReactNode;
/**
Additional provider-specific options. They are passed through
to the provider from the AI SDK and enable provider-specific
functionality that can be fully encapsulated in the provider.
*/
providerOptions?: ProviderOptions;
/**
* Callback that is called when the LLM response and the final object validation are finished.
*/
onFinish?: (event: {
/**
* The reason why the generation finished.
*/
finishReason: FinishReason;
/**
* The token usage of the generated response.
*/
usage: LanguageModelUsage;
/**
* The final ui node that was generated.
*/
value: ReactNode;
/**
* Warnings from the model provider (e.g. unsupported settings)
*/
warnings?: CallWarning[];
/**
* Optional response data.
*/
response?: {
/**
* Response headers.
*/
headers?: Record<string, string>;
};
}) => Promise<void> | void;
}): Promise<RenderResult>;
type StreamableUIWrapper = {
/**
* The value of the streamable UI. This can be returned from a Server Action and received by the client.
*/
readonly value: React.ReactNode;
/**
* This method updates the current UI node. It takes a new UI node and replaces the old one.
*/
update(value: React.ReactNode): StreamableUIWrapper;
/**
* This method is used to append a new UI node to the end of the old one.
* Once appended a new UI node, the previous UI node cannot be updated anymore.
*
* @example
* ```jsx
* const ui = createStreamableUI(<div>hello</div>)
* ui.append(<div>world</div>)
*
* // The UI node will be:
* // <>
* // <div>hello</div>
* // <div>world</div>
* // </>
* ```
*/
append(value: React.ReactNode): StreamableUIWrapper;
/**
* This method is used to signal that there is an error in the UI stream.
* It will be thrown on the client side and caught by the nearest error boundary component.
*/
error(error: any): StreamableUIWrapper;
/**
* This method marks the UI node as finalized. You can either call it without any parameters or with a new UI node as the final state.
* Once called, the UI node cannot be updated or appended anymore.
*
* This method is always **required** to be called, otherwise the response will be stuck in a loading state.
*/
done(...args: [React.ReactNode] | []): StreamableUIWrapper;
};
/**
* Create a piece of changeable UI that can be streamed to the client.
* On the client side, it can be rendered as a normal React node.
*/
declare function createStreamableUI(initialValue?: React.ReactNode): StreamableUIWrapper;
declare const __internal_curr: unique symbol;
declare const __internal_error: unique symbol;
/**
* StreamableValue is a value that can be streamed over the network via AI Actions.
* To read the streamed values, use the `readStreamableValue` or `useStreamableValue` APIs.
*/
type StreamableValue<T = any, E = any> = {
[__internal_curr]?: T;
[__internal_error]?: E;
};
/**
* Create a wrapped, changeable value that can be streamed to the client.
* On the client side, the value can be accessed via the readStreamableValue() API.
*/
declare function createStreamableValue<T = any, E = any>(initialValue?: T | ReadableStream<T>): StreamableValueWrapper<T, E>;
type StreamableValueWrapper<T, E> = {
/**
* The value of the streamable. This can be returned from a Server Action and
* received by the client. To read the streamed values, use the
* `readStreamableValue` or `useStreamableValue` APIs.
*/
readonly value: StreamableValue<T, E>;
/**
* This method updates the current value with a new one.
*/
update(value: T): StreamableValueWrapper<T, E>;
/**
* This method is used to append a delta string to the current value. It
* requires the current value of the streamable to be a string.
*
* @example
* ```jsx
* const streamable = createStreamableValue('hello');
* streamable.append(' world');
*
* // The value will be 'hello world'
* ```
*/
append(value: T): StreamableValueWrapper<T, E>;
/**
* This method is used to signal that there is an error in the value stream.
* It will be thrown on the client side when consumed via
* `readStreamableValue` or `useStreamableValue`.
*/
error(error: any): StreamableValueWrapper<T, E>;
/**
* This method marks the value as finalized. You can either call it without
* any parameters or with a new value as the final state.
* Once called, the value cannot be updated or appended anymore.
*
* This method is always **required** to be called, otherwise the response
* will be stuck in a loading state.
*/
done(...args: [T] | []): StreamableValueWrapper<T, E>;
};
/**
* `readStreamableValue` takes a streamable value created via the `createStreamableValue().value` API,
* and returns an async iterator.
*
* ```js
* // Inside your AI action:
*
* async function action() {
* 'use server'
* const streamable = createStreamableValue();
*
* streamable.update(1);
* streamable.update(2);
* streamable.done(3);
* // ...
* return streamable.value;
* }
* ```
*
* And to read the value:
*
* ```js
* const streamableValue = await action()
* for await (const v of readStreamableValue(streamableValue)) {
* console.log(v)
* }
* ```
*
* This logs out 1, 2, 3 on console.
*/
declare function readStreamableValue<T = unknown>(streamableValue: StreamableValue<T>): AsyncIterable<T | undefined>;
/**
* `useStreamableValue` is a React hook that takes a streamable value created via the `createStreamableValue().value` API,
* and returns the current value, error, and pending state.
*
* This is useful for consuming streamable values received from a component's props. For example:
*
* ```js
* function MyComponent({ streamableValue }) {
* const [data, error, pending] = useStreamableValue(streamableValue);
*
* if (pending) return <div>Loading...</div>;
* if (error) return <div>Error: {error.message}</div>;
*
* return <div>Data: {data}</div>;
* }
* ```
*/
declare function useStreamableValue<T = unknown, Error = unknown>(streamableValue?: StreamableValue<T>): [data: T | undefined, error: Error | undefined, pending: boolean];
declare function useUIState<AI extends AIProvider = any>(): [InferUIState<AI, any>, (v: InferUIState<AI, any> | ((v_: InferUIState<AI, any>) => InferUIState<AI, any>)) => void];
declare function useAIState<AI extends AIProvider = any>(): [
InferAIState<AI, any>,
(newState: ValueOrUpdater<InferAIState<AI, any>>) => void
];
declare function useAIState<AI extends AIProvider = any>(key: keyof InferAIState<AI, any>): [
InferAIState<AI, any>[typeof key],
(newState: ValueOrUpdater<InferAIState<AI, any>[typeof key]>) => void
];
declare function useActions<AI extends AIProvider = any>(): InferActions<AI, any>;
declare function useSyncUIState(): () => Promise<void>;
export { AIAction, AIActions, AIProvider, AIProviderProps, InferAIState, InferActions, InferUIState, InternalAIProviderProps, InternalAIStateStorageOptions, JSONValue, MutableAIState, OnGetUIState, OnSetAIState, ServerWrappedAction, ServerWrappedActions, StreamableValue, ValueOrUpdater, createAI, createStreamableUI, createStreamableValue, getAIState, getMutableAIState, readStreamableValue, streamUI, useAIState, useActions, useStreamableValue, useSyncUIState, useUIState };