Skip to content
On this page

自定义插件开发

自定义插件允许开发者为不同的 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;

Released under the MIT License.