mcpField
X
x-chat-provider

x-chat-provider

github.com/ant-design开发4.8k81 次安装2 天前更新
代码仓库

关于 x-chat-provider

x-chat-provider 是一项将流式 API 接入 Ant Design X Chat Provider 的技能。它介绍内置的 OpenAI、DeepSeek 和透传 Provider 的适用场景,并说明如何自定义响应转换。

主要功能

  1. 01选择内置 Provider
  2. 02实现自定义 Provider
  3. 03转换请求与消息格式
  4. 04适配非标准流
  5. 05监控回调并配置重试

适用场景

  1. 01接入标准 OpenAI API
  2. 02接入标准 DeepSeek API
  3. 03适配私有 SSE API
  4. 04渲染原始响应数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ant-design/x --skill x-chat-provider

安装后在 Claude Code 中输入/x-chat-provider确认已加载。

附带文件reference/EXAMPLES.md

详细介绍

🎯 Skill 定位

此 Skill 专注于解决一个问题:如何快速将你的流式界面接入 Ant Design X 的 Chat Provider。

不涉及:useXChat 用法教程(这是另一个 Skill)。

目录

📦 技术栈概览

层级包名核心用途
UI 层@ant-design/xReact UI 组件库
逻辑层@ant-design/x-sdk开发工具包
渲染层@ant-design/x-markdownMarkdown 渲染器
// ✅ Correct import examples
import { Bubble } from '@ant-design/x';
import { AbstractChatProvider, OpenAIChatProvider } from '@ant-design/x-sdk';
import XRequest from '@ant-design/x-sdk';

🚀 快速开始

🎯 Provider 选择决策树
graph TD
    A[Start] --> B{Use standard OpenAI/DeepSeek API?}
    B -->|Yes| C[Use built-in Provider]
    B -->|No| D{Raw data format as message?}
    D -->|Yes| E[Use DefaultChatProvider]
    D -->|No| F[Custom Provider]
    C --> G[OpenAIChatProvider / DeepSeekChatProvider]
    E --> H[Pass-through, no conversion needed]
    F --> I[Four-step custom Provider]
🏭 内置 Provider 概览
Provider 类型适用场景导入
OpenAIChatProvider标准 OpenAI API 格式import { OpenAIChatProvider } from '@ant-design/x-sdk'
DeepSeekChatProvider标准 DeepSeek API 格式import { DeepSeekChatProvider } from '@ant-design/x-sdk'
DefaultChatProvider直接传递原始响应,无需格式转换import { DefaultChatProvider } from '@ant-design/x-sdk'

⚠️ 导出名称为 OpenAIChatProvider / DeepSeekChatProvider / DefaultChatProvider,请注意拼写

DefaultChatProvider 使用场景

DefaultChatProvider 直接传递原始响应数据,不进行任何转换。适用于:

  • 接口响应格式已经符合你希望显示的内容
  • 你希望完全控制 Bubble.List 的 contentRender,以渲染消息
import { DefaultChatProvider, XRequest } from '@ant-design/x-sdk';

interface ChatInput {
  query: string;
  stream?: boolean;
}

interface ChatOutput {
  choices: Array<{ message: { content: string; role: string } }>;
}

// DefaultChatProvider generic: <ChatMessage, Input, Output>
// ChatMessage is your Output type (passed through directly)
const provider = new DefaultChatProvider<ChatOutput | ChatInput, ChatInput, ChatOutput>({
  request: XRequest('https://your-api.com/chat', {
    manual: true,
    params: { stream: false },
  }),
});

// Render using contentRender in Bubble.List's role config
// role={{ assistant: { contentRender(content) { return content?.choices?.[0]?.message?.content } } }}

⚠️ 使用 DefaultChatProvider 时,ChatMessage 通常是你的 Output 类型或联合类型;渲染时需要使用 contentRender

📋 实现自定义 Provider 的四个步骤

第 1 步:分析接口格式 ⏱️ 2 分钟

信息类型示例值
接口 URLhttps://your-api.com/chat
请求方法JSON、POST
响应格式Server-Sent Events
认证方式Bearer Token

第 2 步:创建 Provider 类 ⏱️ 5 分钟

// MyChatProvider.ts
import { AbstractChatProvider } from '@ant-design/x-sdk';
import type { TransformMessage } from '@ant-design/x-sdk';
import type { XRequestOptions } from '@ant-design/x-sdk';

interface MyInput {
  query: string;
  model?: string;
  stream?: boolean;
}

interface MyOutput {
  content: string;
  finish_reason?: string;
}

interface MyMessage {
  content: string;
  role: 'user' | 'assistant';
}

export class MyChatProvider extends AbstractChatProvider<MyMessage, MyInput, MyOutput> {
  // Parameter conversion: merge onRequest params + XRequest default params
  // options comes from XRequest(url, options), can access options.params etc.
  transformParams(
    requestParams: Partial<MyInput>,
    options: XRequestOptions<MyInput, MyOutput, MyMessage>,
  ): MyInput {
    return {
      ...(options?.params || {}),
      query: requestParams.query || '',
      model: 'gpt-3.5-turbo',
      stream: true,
    };
  }

  // Local message: convert onRequest params to the user-side display message (can return array)
  transformLocalMessage(requestParams: Partial<MyInput>): MyMessage {
    return {
      content: requestParams.query || '',
      role: 'user',
    };
  }

  // Response conversion:
  // info.originMessage: previous content of this message (for stream accumulation)
  // info.chunk: current streaming chunk
  // info.chunks: all received chunks (used in onSuccess)
  // info.status: current status
  // ⚠️ Return only MyMessage type; do NOT add a status field
  transformMessage(info: TransformMessage<MyMessage, MyOutput>): MyMessage {
    const { originMessage, chunk } = info;

    if (!chunk?.content || chunk.content === '[DONE]') {
      return { ...(originMessage || { content: '', role: 'assistant' }) };
    }

    return {
      content: `${originMessage?.content || ''}${chunk.content}`,
      role: 'assistant',
    };
  }
}

第 3 步:验证 ⏱️ 1 分钟

检查项说明
仅有 3 个方法transformParams、transformLocalMessage、transformMessage
transformParams 签名必须包含第二个参数 options: XRequestOptions<...>
返回值中没有 statustransformMessage 的返回值没有 status 字段
没有请求方法确认未实现 request 方法
类型检查通过tsc --noEmit 无错误

第 4 步:使用 Provider ⏱️ 1 分钟

import { MyChatProvider } from './MyChatProvider';
import XRequest from '@ant-design/x-sdk';

// ⚠️ Must pass manual: true, otherwise AbstractChatProvider constructor will throw
const provider = new MyChatProvider({
  request: XRequest('https://your-api.com/chat', {
    manual: true,
    headers: {
      Authorization: 'Bearer your-token',
      'Content-Type': 'application/json',
    },
    params: {
      model: 'gpt-3.5-turbo',
      stream: true,
    },
  }),
});

export { provider };

🔑 核心类型和导出项

从 @ant-design/x-sdk 导出的关键类型:

import type {
  // OpenAI standard message format
  XModelMessage, // { role: string; content: string | { text: string; type: string } }
  XModelParams, // Full OpenAI request params type (model, messages, stream, temperature, etc.)
  XModelResponse, // Full OpenAI response type (choices, usage, etc.)

  // SSE stream field types
  SSEFields, // 'data' | 'event' | 'id' | 'retry'
  SSEOutput, // Partial<Record<SSEFields, any>>

  // Provider related
  TransformMessage, // { originMessage, chunk, chunks, status, responseHeaders }

  // XRequest related
  XRequestOptions, // Full request config
  XRequestCallbacks, // { onUpdate, onSuccess, onError }

  // Message related
  MessageInfo, // { id, message, status, extraInfo }
} from '@ant-design/x-sdk';
XModelMessage 结构(OpenAI 消息格式)
// XModelMessage is the standard OpenAI message format
// Used for OpenAIChatProvider / DeepSeekChatProvider ChatMessage generic
const userMessage: XModelMessage = { role: 'user', content: 'Hello' };
const systemMessage: XModelMessage = { role: 'system', content: 'You are an assistant' };
const developerMessage: XModelMessage = { role: 'developer', content: 'System prompt' };
SSEOutput 和 SSEFields
// SSEOutput is the type for raw SSE stream data
// { data?: string; event?: string; id?: string; retry?: number }
// DeepSeekChatProvider uses Partial<Record<SSEFields, XModelResponse>>

import { DeepSeekChatProvider, XRequest } from '@ant-design/x-sdk';
import type { SSEFields, XModelParams, XModelResponse } from '@ant-design/x-sdk';

const provider = new DeepSeekChatProvider({
  request: XRequest<XModelParams, Partial<Record<SSEFields, XModelResponse>>>(
    'https://api.deepseek.com/v1/chat/completions',
    {
      manual: true,
      params: { model: 'deepseek-chat', stream: true },
    },
  ),
});

⚙️ XRequest 高级配置

callbacks

callbacks 可在 Provider 层监控请求事件。callbacks 中的第三个参数是经 transformMessage 处理的 MessageInfo:

const provider = new OpenAIChatProvider({
  request: XRequest<XModelParams, XModelResponse, XModelMessage>(BASE_URL, {
    manual: true,
    callbacks: {
      // onUpdate: triggered on each streaming chunk arrival
      // chunk: current chunk; responseHeaders: response headers; message: current MessageInfo
      onUpdate: (chunk, responseHeaders, message) => {
        console.log('Stream update:', message?.message?.content);
      },
      // onSuccess: triggered when all chunks are received
      // chunks: all chunks array; message: final MessageInfo
      onSuccess: (chunks, responseHeaders, message) => {
        console.log('Request complete:', message?.message?.content);
        // Good place for analytics, logging, etc.
      },
      // onError: triggered on request failure (including AbortError)
      // error: error object; errorInfo: extra error info; message: MessageInfo at failure
      onError: (error, errorInfo, responseHeaders, message) => {
        console.error('Request failed:', error.message);
      },
    },
    params: { model: 'gpt-4o', stream: true },
  }),
});

⚠️ callbacks 和 useXChat 的 requestFallback 并不冲突——两者都会执行。callbacks 更适合用于日志记录和报告;requestFallback 用于控制 UI 显示。

retryInterval 重试

const request = XRequest('https://your-api.com/chat', {
  manual: true,
  // Retry interval after failure (ms)
  retryInterval: 3000,
  // Max retry count (unlimited if not set)
  retryTimes: 3,
  // onError can also return a number to dynamically set retry interval
  callbacks: {
    onError: (error) => {
      if (error.name === 'AbortError') return; // Don't retry on user cancel
      return 5000; // Return number = retry after 5s (higher priority than retryInterval)
    },
  },
});

transformStream 自定义流

当服务端返回非标准 SSE 流格式时使用:

const request = XRequest('https://your-api.com/chat', {
  manual: true,
  // Fixed TransformStream
  transformStream: new TransformStream({
    transform(chunk, controller) {
      controller.enqueue(JSON.parse(chunk));
    },
  }),
  // Or decide dynamically based on URL and response headers
  transformStream: (baseURL, responseHeaders) => {
    if (responseHeaders.get('x-stream-type') === 'ndjson') {
      return new TransformStream({/* ... */});
    }
    return undefined; // Use default SSE parsing
  },
});

🔧 常见场景适配

📖 完整示例:EXAMPLES.md

场景类型难度描述
标准 OpenAI🟢直接使用内置的 OpenAIChatProvider
标准 DeepSeek🟢直接使用内置的 DeepSeekChatProvider
透传原始数据🟢使用 DefaultChatProvider
私有 SSE API🟡四步自定义 Provider
多字段响应🟡自定义 Provider + 复杂 ChatMessage
非 SSE 流🔴自定义 Provider + transformStream

⚠️ 重要提醒

🚨 强制规则:绝不编写请求方法!
// ❌ Serious error
class MyProvider extends AbstractChatProvider {
  async request(params: any) {
    /* Forbidden! */
  }
}

// ✅ Only correct approach: implement only the three conversion methods
class MyProvider extends AbstractChatProvider {
  transformParams(params, options) {
    /* ... */
  }
  transformLocalMessage(params) {
    /* ... */
  }
  transformMessage(info) {
    /* ... */
  }
}
⚠️ transformMessage 不得返回 status
// ❌ Wrong
transformMessage(info) {
  return { content: '...', status: 'error' }; // ❌ status is managed by the framework
}

// ✅ Correct
transformMessage(info) {
  return { content: '...' }; // ✅
}
⚠️ Provider 实例化说明
// ✅ In React components, use useState to ensure only created once
const [provider] = React.useState(
  new MyChatProvider({
    request: XRequest(URL, { manual: true }),
  }),
);

// ❌ Don't create directly in render function (creates new instance on every render)
// const provider = new MyChatProvider(...); // inside component body causes issues

⚡ 快速检查清单

创建 Provider 前:

  • 已获取接口文档和响应格式
  • 已确认是否需要自定义(或内置 Provider 是否足够)
  • 已定义 Input、Output、ChatMessage 类型

完成后:

  • 只实现了三个必需方法
  • transformParams 包含第二个参数 options
  • transformMessage 的返回值不含 status 字段
  • XRequest 配置了 manual: true
  • 绝不实现请求方法
  • 在 React 组件中使用 useState 包装 Provider
  • 类型检查通过(tsc --noEmit)

🚨 开发规则

  • 如果用户未明确要求测试用例,不要添加测试文件
  • 完成后必须检查类型:运行 tsc --noEmit,确保没有类型错误
  • 保持代码整洁:移除所有未使用的变量和导入

🔗 参考资源

📚 核心参考文档

🌐 SDK 官方文档

💻 示例代码

评论

支持 Markdown