Custom Chat Provider
When the built-in Chat Provider doesn't meet your needs, you can implement the abstract class AbstractChatProvider (which only contains three abstract methods) to convert data from different model providers or Agentic services into a unified format that useXChat can consume, enabling seamless integration and switching between different models and agents.
AbstractChatProvider is an abstract class used to define the interface for Chat Provider. When you need to use a custom data service, you can inherit from AbstractChatProvider and implement its methods. You can refer to the Playground - Toolbox for examples.
type MessageStatus = 'local' | 'loading' | 'updating' | 'success' | 'error';interface ChatProviderConfig<Input, Output> {request: XRequestClass<Input, Output> | (() => XRequestClass<Input, Output>);}interface TransformMessage<ChatMessage, Output> {originMessage?: ChatMessage;chunk: Output;chunks: Output[];status: MessageStatus;}abstract class AbstractChatProvider<ChatMessage, Input, Output> {constructor(config: ChatProviderConfig<Input, Output>): void;/*** Transform parameters passed to onRequest. You can merge or additionally process them with the params in the request configuration when instantiating the Provider* @param requestParams Request parameters* @param options Request configuration, from the request configuration when instantiating the Provider*/abstract transformParams(requestParams: Partial<Input>,options: XRequestOptions<Input, Output>,): Input;/*** Convert parameters passed to onRequest into a local (user-sent) ChatMessage for message rendering* @param requestParams Parameters passed to onRequest*/abstract transformLocalMessage(requestParams: Partial<Input>): ChatMessage;/*** Can transform messages when updating returned data, and will also update messages* @param info*/abstract transformMessage(info: TransformMessage<ChatMessage, Output>): ChatMessage;}
Below is a custom Provider example showing how to customize a Chat Provider. Detailed explanations follow the code example.
// Type definitionstype CustomInput = {query: string;};type CustomOutput = {data: string;};type CustomMessage = {content: string;role: 'user' | 'assistant';};class CustomProvider<ChatMessage extends CustomMessage = CustomMessage,Input extends CustomInput = CustomInput,Output extends CustomOutput = CustomOutput,> extends AbstractChatProvider<ChatMessage, Input, Output> {transformParams(requestParams: Partial<Input>, options: XRequestOptions<Input, Output>): Input {if (typeof requestParams !== 'object') {throw new Error('requestParams must be an object');}return {...(options?.params || {}),...(requestParams || {}),} as Input;}transformLocalMessage(requestParams: Partial<Input>): ChatMessage {return {content: requestParams.query,role: 'user',} as unknown as ChatMessage;}transformMessage(info: TransformMessage<ChatMessage, Output>): ChatMessage {const { originMessage, chunk } = info || {};if (!chunk || !chunk?.data || (chunk?.data && chunk?.data?.includes('[DONE]'))) {return {content: originMessage?.content || '',role: 'assistant',} as ChatMessage;}const chunkJson = JSON.parse(chunk.data);const content = originMessage?.content || '';return {content: `${content || ''}${chunkJson.data || ''}`,role: 'assistant',} as ChatMessage;}}
Agentic service streaming interface https://xxx.agent.com/api/stream.Request parameters:
{"query": "Help me summarize today's tech news"}
Response data:
id:1data: "Okay,"id:2data: "I'll help you"id:3data: "summarize today's"id:4data: "tech news,"
CustomInput and CustomOutput types.CustomInput type:
{query: string;}
Since we only need to convert the data string to JSON and concatenate the internal data field, the CustomOutput type is:
{data: string;}
useXChat to be directly consumable by Bubble.List, so we can define CustomMessage as:{content: string;role: 'user' | 'assistant';}
AbstractChatProvider and implement its methods to get CustomProvider. AbstractChatProvider only requires implementing three methods.transformParams is used to transform parameters passed to onRequest. You can merge or additionally process them with the params in the request configuration when instantiating the Provider.transformLocalMessage converts parameters passed to onRequest into a local (user-sent) ChatMessage for user message rendering, and will also update messages for message list rendering.transformMessage can transform data into ChatMessage data type when updating returned data, and will also update messages for message list rendering.CustomProvider and pass it to useXChat to complete the custom Provider usage.const [provider] = React.useState(new CustomProvider({request: XRequest<CustomInput, CustomOutput>('https://xxx.agent.com/api/stream', {manual: true,}),}),);const { onRequest, messages, setMessages, setMessage, isRequesting, abort, onReload } = useXChat({provider,});
onRequest({query: "Help me summarize today's tech news",});
We welcome community contributions for new Chat Providers! Please follow these specifications for Chat Provider development.
This guide will help you contribute to Ant Design. Please take a few minutes to read this contribution guide before submitting an issue or pull request.
Chat Providers should follow these specifications:
packages/x-sdk/src/chat-providers directory.packages/x-sdk/src/chat-providers/type directory.Chat Provider theme files should follow these naming rules:
[Vendor][Type][Version].ts.[Vendor][ModelName].ts.This is an example using a custom provider, demonstrating how to extend AbstractChatProvider to implement custom data processing logic.