x-chat-provider について
x-chat-providerは、ストリーミングAPIをAnt Design X Chat Providerに接続するスキルです。OpenAI、DeepSeek、パススルーの各Providerの選び方と、独自のレスポンス変換方法を説明します。
主な機能
- 01組み込みProviderの選択
- 02カスタムProviderの実装
- 03リクエストとメッセージ形式の変換
- 04非標準ストリームへの対応
- 05コールバック監視と再試行設定
活用シーン
- 01標準OpenAI APIへの接続
- 02標準DeepSeek APIへの接続
- 03プライベートSSE APIへの適応
- 04生のレスポンスデータの表示
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ant-design/ x --skill x-chat-providerインストール後、Claude Code で/x-chat-providerを入力して読み込まれたか確認します。
同梱ファイルreference/EXAMPLES.md
詳細
🎯 スキルの位置づけ
このスキルは、1つの問題の解決に特化しています:ストリーミングインターフェースを Ant Design X の Chat Provider にすばやく適応させる方法です。
対象外:useXChat の使い方チュートリアル(これは別のスキルです)。
目次
- 📦 技術スタックの概要
- 🚀 クイックスタート
- 📋 カスタム Provider を実装する 4 つの手順
- 🔑 コアの型とエクスポート
- ⚙️ XRequest の高度な設定
- 🔧 よくあるシナリオへの適応
- ⚠️ 重要な注意事項
- ⚡ クイックチェックリスト
- 🚨 開発ルール
- 🔗 参考リソース
📦 技術スタックの概要
| レイヤー | パッケージ名 | 主な用途 |
|---|---|---|
| UI レイヤー | @ant-design/x | React UI コンポーネントライブラリ |
| ロジックレイヤー | @ant-design/x-sdk | 開発ツールキット |
| レンダリングレイヤー | @ant-design/x-markdown | Markdown レンダラー |
// ✅ 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 を実装する 4 つの手順
手順 1:インターフェース形式を分析する ⏱️ 2 分
| 情報の種類 | 値の例 |
|---|---|
| インターフェース URL | https://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 のシグネチャ | 2 番目のパラメーター options: XRequestOptions<...> を含める必要があります |
| 戻り値に status がない | transformMessage の戻り値に status フィールドがありません |
| request メソッドがない | 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 を使用すると、Provider レベルでリクエストイベントを監視できます。コールバックの第 3 パラメーターは、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が適しており、UI の表示制御にはrequestFallbackが適しています。
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 | 🟡 | 4 ステップのカスタム 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 の型を定義した
完了後:
- 必須の 3 つのメソッドのみを実装した
- transformParams に第 2 パラメーターの options が含まれている
- transformMessage の戻り値に status フィールドがない
- XRequest が manual: true で設定されている
- request メソッドを一切実装していない
- React コンポーネントで Provider を
useStateでラップした - 型チェックに合格した(
tsc --noEmit)
🚨 開発ルール
- ユーザーがテストケースを明示的に必要としていない場合は、テストファイルを追加しないでください
- 完了後は必ず型をチェックしてください:
tsc --noEmitを実行して型エラーがないことを確認します - コードをクリーンに保ってください:未使用の変数とインポートはすべて削除します
🔗 参考リソース
📚 コアリファレンスドキュメント
- EXAMPLES.md - 実用的なサンプルコード