自定义插件开发
自定义插件允许开发者为不同的 AI 服务提供商创建专门的协议适配器。插件的核心价值是自动处理不同 API 的响应格式转换,让开发者无需手动编写响应处理逻辑,实现开箱即用。
插件架构
插件系统基于统一的接口设计,每个插件都需要实现 IPluginInstance
接口:
typescript
interface IPluginInstance {
init(options: any): void;
}
插件通过 responseInterceptor
方法自动拦截和转换 API 响应,将不同服务商的响应格式统一为标准格式。
创建自定义插件
实际案例:基于 OpenAI 插件
OpenAI 插件是一个很好的参考实现,展示了如何处理流式响应:
typescript
import type { IPluginInstance } from '@tencent/ssv-ai-sdk-shared';
class OpenAIPlugin implements IPluginInstance {
private responseInterceptor(res: any) {
if (!res) return res;
try {
// 处理 OpenAI 流式响应格式
if (res.choices?.[0]?.delta?.content) {
return {
...res,
content: res.choices[0].delta.content, // 提取增量内容
messageID: res.id, // 消息 ID
};
}
} catch (e) {
console.error('OpenAI 响应处理错误:', e);
}
return res;
}
init(options: any): void {
['chatEndpoint', 'completionEndpoint'].forEach((endpoint) => {
if (!options.api[endpoint]) return;
// 支持字符串配置的简化语法
if (typeof options.api[endpoint] === 'string') {
options.api[endpoint] = {
url: options.api[endpoint],
};
}
// 自动注入响应拦截器
if (typeof options.api[endpoint] === 'object') {
options.api[endpoint].responseInterceptor =
this.responseInterceptor.bind(this);
}
});
}
}
适配不同 API 格式的示例
示例:流式响应 API
适用于支持 Server-Sent Events (SSE) 的流式 API:
typescript
class StreamAPIPlugin implements IPluginInstance {
private responseInterceptor(res: any) {
if (!res) return res;
try {
return {
...res,
content: res.delta?.content,
messageID: res.stream_id || res.id,
};
} catch (e) {
console.error('流式 API 响应处理错误:', e);
return res;
}
}
init(options: any): void {
['chatEndpoint'].forEach((endpoint) => {
if (!options.api[endpoint]) return;
if (typeof options.api[endpoint] === 'string') {
options.api[endpoint] = { url: options.api[endpoint] };
}
if (typeof options.api[endpoint] === 'object') {
options.api[endpoint].responseInterceptor =
this.responseInterceptor.bind(this);
}
});
}
}
在项目中使用自定义插件
React 中使用
jsx
import React from 'react';
import { useChat } from '@tencent/ssv-ai-sdk-react';
import { CustomPlugin } from './path/to/your/custom-plugin';
function ChatComponent() {
const { messages, input, handleInput, chat, stop, assistantStatus, status } =
useChat({
api: {
// 支持字符串格式的简化配置
chatEndpoint: 'https://your-api.com/chat',
},
mode: 'stream',
streamFormat: 'incremental',
// 插件自动处理响应格式转换
plugins: [new CustomPlugin()],
});
const handleSubmit = async (e) => {
e.preventDefault();
if (!input.trim()) return;
await chat({
prompt: input.trim(),
});
};
return (
<div className="chat-container">
<div className="messages">
{messages.map((message, index) => (
<div key={index} className={`message ${message.role}`}>
<strong>{message.role}:</strong> {message.content}
</div>
))}
{assistantStatus !== 'idle' && (
<div className="assistant-status">AI 状态: {assistantStatus}</div>
)}
</div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={input}
onChange={(e) => handleInput(e.target.value)}
placeholder="输入消息..."
disabled={status === 'loading'}
/>
<button type="submit" disabled={status === 'loading' || !input.trim()}>
发送
</button>
{status === 'loading' && (
<button type="button" onClick={stop}>
停止
</button>
)}
</form>
</div>
);
}
export default ChatComponent;