LLM 适配器
LLM 适配器为不同的 AI 模型提供统一的调用接口。
适配器架构
┌─────────────────────────────────────────┐
│ AbstractClient │
│ - sendMessage() │
│ - streamResponse() │
│ - 消息格式转换 │
│ - 工具调用解析 │
├────────┬────────┬────────┬──────────────┤
│OpenAI │ Claude │ Gemini │ 其他兼容API │
│Client │ Client │ Client │ │
└────────┴────────┴────────┴──────────────┘AbstractClient
所有适配器的抽象基类,位于 src/core/adapters/AbstractClient.js:
javascript
// 核心功能
export class AbstractClient {
constructor(options) {
this.options = options
this.historyManager = options.historyManager || new DefaultHistoryManager()
}
// 发送消息(子类实现)
async sendMessage(context, option) {
throw new Error('Not implemented')
}
// 流式响应
async *streamResponse(context, option) {
throw new Error('Not implemented')
}
}
// 工具调用解析(支持多种格式)
export function parseXmlToolCalls(text) {
// 支持 <tools>、<tool_call>、JSON 等多种格式
// 自动修复格式错误的 JSON
}OpenAIClient
支持 OpenAI API 及所有兼容接口:
javascript
// src/core/adapters/openai/OpenAIClient.js
import OpenAI from 'openai'
import { AbstractClient } from '../AbstractClient.js'
export class OpenAIClient extends AbstractClient {
constructor(options) {
super(options)
this.client = new OpenAI({
apiKey: options.apiKey,
baseURL: options.baseUrl
})
}
async sendMessage(context, option) {
// 转换消息格式
const messages = this.convertMessages(context.history)
// 调用 API
const response = await this.client.chat.completions.create({
model: option.model,
messages,
tools: option.tools,
stream: option.stream
})
return this.parseResponse(response)
}
}ClaudeClient
支持 Anthropic Claude API:
javascript
// src/core/adapters/claude/ClaudeClient.js
import Anthropic from '@anthropic-ai/sdk'
import { AbstractClient } from '../AbstractClient.js'
export class ClaudeClient extends AbstractClient {
constructor(options) {
super(options)
this.client = new Anthropic({
apiKey: options.apiKey
})
}
async sendMessage(context, option) {
// Claude 使用不同的消息格式
const { system, messages } = this.convertToClaude(context.history)
const response = await this.client.messages.create({
model: option.model,
max_tokens: option.maxTokens || 4096,
system,
messages,
tools: this.convertTools(option.tools)
})
return this.parseClaudeResponse(response)
}
}GeminiClient
支持 Google Gemini API:
javascript
// src/core/adapters/gemini/GeminiClient.js
import { GoogleGenerativeAI } from '@google/generative-ai'
import { AbstractClient } from '../AbstractClient.js'
export class GeminiClient extends AbstractClient {
constructor(options) {
super(options)
this.genAI = new GoogleGenerativeAI(options.apiKey)
}
async sendMessage(context, option) {
const model = this.genAI.getGenerativeModel({
model: option.model
})
const chat = model.startChat({
history: this.convertToGemini(context.history)
})
const result = await chat.sendMessage(context.lastMessage)
return this.parseGeminiResponse(result)
}
}消息格式转换
插件使用统一的内部格式(Chaite 格式),通过 Converter 系统在不同 API 格式间转换:
javascript
// src/core/utils/converter.js
// 注册转换器
registerFromChaiteConverter('openai', chaiteToOpenAI)
registerIntoChaiteConverter('openai', openAIToChaite)
// 使用转换器
const openaiMessages = getFromChaiteConverter('openai')(chaiteMessages)
const chaiteMessages = getIntoChaiteConverter('openai')(openaiMessages)导出接口
javascript
// src/core/adapters/index.js
export { AbstractClient, parseXmlToolCalls } from './AbstractClient.js'
export { OpenAIClient } from './openai/OpenAIClient.js'
export { GeminiClient } from './gemini/GeminiClient.js'
export { ClaudeClient } from './claude/ClaudeClient.js'
// 转换器注册
export {
registerFromChaiteConverter,
registerIntoChaiteConverter,
getFromChaiteConverter,
getIntoChaiteConverter
} from '../utils/converter.js'工具调用解析
parseXmlToolCalls 函数支持多种工具调用格式:
<tools>JSON</tools>- XML 包裹的 JSON<tool_call>...</tool_call>- XML 格式{"tool_calls": [...]}- 裸 JSON- 函数调用风格
funcName({...}) - 自动修复格式错误的 JSON
扩展适配器
添加新的 LLM 适配器:
javascript
// 1. 继承 AbstractClient
import { AbstractClient } from './AbstractClient.js'
export class MyClient extends AbstractClient {
async sendMessage(context, option) {
// 实现聊天逻辑
}
}
// 2. 注册消息转换器
import './my-converter.js' // 注册 Chaite <-> MyAPI 转换
// 3. 在 index.js 中导出
export { MyClient } from './my/MyClient.js'