- 添加OllamaConverter处理Ollama协议与其他协议的转换 - 实现Ollama处理器处理Ollama特定端点 - 支持Ollama API标准接口如/api/tags、/api/chat、/api/generate - 更新README文档添加Ollama使用说明和示例 - 优化模型前缀处理,支持通过前缀指定不同提供商 - 改进认证处理,允许空Bearer token以兼容VS Code Copilot等客户端
383 lines
No EOL
14 KiB
JavaScript
383 lines
No EOL
14 KiB
JavaScript
/**
|
|
* 协议转换模块 - 新架构版本
|
|
* 使用重构后的转换器架构
|
|
*
|
|
* 这个文件展示了如何使用新的转换器架构
|
|
* 可以逐步替换原有的 convert.js
|
|
*/
|
|
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { MODEL_PROTOCOL_PREFIX, getProtocolPrefix } from './common.js';
|
|
import { ConverterFactory } from './converters/ConverterFactory.js';
|
|
import {
|
|
generateResponseCreated,
|
|
generateResponseInProgress,
|
|
generateOutputItemAdded,
|
|
generateContentPartAdded,
|
|
generateOutputTextDone,
|
|
generateContentPartDone,
|
|
generateOutputItemDone,
|
|
generateResponseCompleted
|
|
} from './openai/openai-responses-core.mjs';
|
|
|
|
// =============================================================================
|
|
// 初始化:注册所有转换器
|
|
// =============================================================================
|
|
|
|
// =============================================================================
|
|
// 主转换函数
|
|
// =============================================================================
|
|
|
|
/**
|
|
* 通用数据转换函数(新架构版本)
|
|
* @param {object} data - 要转换的数据(请求体或响应)
|
|
* @param {string} type - 转换类型:'request', 'response', 'streamChunk', 'modelList'
|
|
* @param {string} fromProvider - 源模型提供商
|
|
* @param {string} toProvider - 目标模型提供商
|
|
* @param {string} [model] - 可选的模型名称(用于响应转换)
|
|
* @returns {object} 转换后的数据
|
|
* @throws {Error} 如果找不到合适的转换函数
|
|
*/
|
|
export function convertData(data, type, fromProvider, toProvider, model) {
|
|
try {
|
|
// 获取协议前缀
|
|
const fromProtocol = getProtocolPrefix(fromProvider);
|
|
const toProtocol = getProtocolPrefix(toProvider);
|
|
|
|
// 从工厂获取转换器
|
|
const converter = ConverterFactory.getConverter(fromProtocol);
|
|
|
|
if (!converter) {
|
|
throw new Error(`No converter found for protocol: ${fromProtocol}`);
|
|
}
|
|
|
|
// 根据类型调用相应的转换方法
|
|
switch (type) {
|
|
case 'request':
|
|
return converter.convertRequest(data, toProtocol);
|
|
|
|
case 'response':
|
|
return converter.convertResponse(data, toProtocol, model);
|
|
|
|
case 'streamChunk':
|
|
return converter.convertStreamChunk(data, toProtocol, model);
|
|
|
|
case 'modelList':
|
|
return converter.convertModelList(data, toProtocol);
|
|
|
|
default:
|
|
throw new Error(`Unsupported conversion type: ${type}`);
|
|
}
|
|
} catch (error) {
|
|
console.error(`Conversion error: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// 向后兼容的导出函数
|
|
// =============================================================================
|
|
|
|
/**
|
|
* 以下函数保持与原有API的兼容性
|
|
* 内部使用新的转换器架构
|
|
*/
|
|
|
|
// OpenAI 相关转换
|
|
export function toOpenAIRequestFromGemini(geminiRequest) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toOpenAIRequest(geminiRequest);
|
|
}
|
|
|
|
export function toOpenAIRequestFromClaude(claudeRequest) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.CLAUDE);
|
|
return converter.toOpenAIRequest(claudeRequest);
|
|
}
|
|
|
|
export function toOpenAIChatCompletionFromGemini(geminiResponse, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toOpenAIResponse(geminiResponse, model);
|
|
}
|
|
|
|
export function toOpenAIChatCompletionFromClaude(claudeResponse, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.CLAUDE);
|
|
return converter.toOpenAIResponse(claudeResponse, model);
|
|
}
|
|
|
|
export function toOpenAIStreamChunkFromGemini(geminiChunk, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toOpenAIStreamChunk(geminiChunk, model);
|
|
}
|
|
|
|
export function toOpenAIStreamChunkFromClaude(claudeChunk, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.CLAUDE);
|
|
return converter.toOpenAIStreamChunk(claudeChunk, model);
|
|
}
|
|
|
|
export function toOpenAIModelListFromGemini(geminiModels) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toOpenAIModelList(geminiModels);
|
|
}
|
|
|
|
export function toOpenAIModelListFromClaude(claudeModels) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.CLAUDE);
|
|
return converter.toOpenAIModelList(claudeModels);
|
|
}
|
|
|
|
// Claude 相关转换
|
|
export function toClaudeRequestFromOpenAI(openaiRequest) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI);
|
|
return converter.toClaudeRequest(openaiRequest);
|
|
}
|
|
|
|
export function toClaudeRequestFromOpenAIResponses(responsesRequest) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES);
|
|
return converter.toClaudeRequest(responsesRequest);
|
|
}
|
|
|
|
export function toClaudeChatCompletionFromOpenAI(openaiResponse, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI);
|
|
return converter.toClaudeResponse(openaiResponse, model);
|
|
}
|
|
|
|
export function toClaudeChatCompletionFromGemini(geminiResponse, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toClaudeResponse(geminiResponse, model);
|
|
}
|
|
|
|
export function toClaudeStreamChunkFromOpenAI(openaiChunk, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI);
|
|
return converter.toClaudeStreamChunk(openaiChunk, model);
|
|
}
|
|
|
|
export function toClaudeStreamChunkFromGemini(geminiChunk, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toClaudeStreamChunk(geminiChunk, model);
|
|
}
|
|
|
|
export function toClaudeModelListFromOpenAI(openaiModels) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI);
|
|
return converter.toClaudeModelList(openaiModels);
|
|
}
|
|
|
|
export function toClaudeModelListFromGemini(geminiModels) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toClaudeModelList(geminiModels);
|
|
}
|
|
|
|
// Gemini 相关转换
|
|
export function toGeminiRequestFromOpenAI(openaiRequest) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI);
|
|
return converter.toGeminiRequest(openaiRequest);
|
|
}
|
|
|
|
export function toGeminiRequestFromClaude(claudeRequest) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.CLAUDE);
|
|
return converter.toGeminiRequest(claudeRequest);
|
|
}
|
|
|
|
export function toGeminiRequestFromOpenAIResponses(responsesRequest) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES);
|
|
return converter.toGeminiRequest(responsesRequest);
|
|
}
|
|
|
|
// OpenAI Responses 相关转换
|
|
export function toOpenAIResponsesFromOpenAI(openaiResponse, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI);
|
|
return converter.toOpenAIResponsesResponse(openaiResponse, model);
|
|
}
|
|
|
|
export function toOpenAIResponsesFromClaude(claudeResponse, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.CLAUDE);
|
|
return converter.toOpenAIResponsesResponse(claudeResponse, model);
|
|
}
|
|
|
|
export function toOpenAIResponsesFromGemini(geminiResponse, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toOpenAIResponsesResponse(geminiResponse, model);
|
|
}
|
|
|
|
export function toOpenAIResponsesStreamChunkFromOpenAI(openaiChunk, model, requestId) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI);
|
|
return converter.toOpenAIResponsesStreamChunk(openaiChunk, model, requestId);
|
|
}
|
|
|
|
export function toOpenAIResponsesStreamChunkFromClaude(claudeChunk, model, requestId) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.CLAUDE);
|
|
return converter.toOpenAIResponsesStreamChunk(claudeChunk, model, requestId);
|
|
}
|
|
|
|
export function toOpenAIResponsesStreamChunkFromGemini(geminiChunk, model, requestId) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GEMINI);
|
|
return converter.toOpenAIResponsesStreamChunk(geminiChunk, model, requestId);
|
|
}
|
|
|
|
// 从 OpenAI Responses 转换到其他格式
|
|
export function toOpenAIRequestFromOpenAIResponses(responsesRequest) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES);
|
|
return converter.toOpenAIRequest(responsesRequest);
|
|
}
|
|
|
|
export function toOpenAIChatCompletionFromOpenAIResponses(responsesResponse, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES);
|
|
return converter.toOpenAIResponse(responsesResponse, model);
|
|
}
|
|
|
|
export function toOpenAIStreamChunkFromOpenAIResponses(responsesChunk, model) {
|
|
const converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES);
|
|
return converter.toOpenAIStreamChunk(responsesChunk, model);
|
|
}
|
|
|
|
// 辅助函数导出
|
|
export async function extractAndProcessSystemMessages(messages) {
|
|
const { Utils } = await import('./converters/utils.js');
|
|
return Utils.extractSystemMessages(messages);
|
|
}
|
|
|
|
export async function extractTextFromMessageContent(content) {
|
|
const { Utils } = await import('./converters/utils.js');
|
|
return Utils.extractText(content);
|
|
}
|
|
|
|
// =============================================================================
|
|
// 工具函数
|
|
// =============================================================================
|
|
|
|
/**
|
|
* 获取所有已注册的协议
|
|
* @returns {Array<string>} 协议前缀数组
|
|
*/
|
|
export function getRegisteredProtocols() {
|
|
return ConverterFactory.getRegisteredProtocols();
|
|
}
|
|
|
|
/**
|
|
* 检查协议是否已注册
|
|
* @param {string} protocol - 协议前缀
|
|
* @returns {boolean} 是否已注册
|
|
*/
|
|
export function isProtocolRegistered(protocol) {
|
|
return ConverterFactory.isProtocolRegistered(protocol);
|
|
}
|
|
|
|
/**
|
|
* 清除所有转换器缓存
|
|
*/
|
|
export function clearConverterCache() {
|
|
ConverterFactory.clearCache();
|
|
}
|
|
|
|
/**
|
|
* 获取转换器实例(用于高级用法)
|
|
* @param {string} protocol - 协议前缀
|
|
* @returns {BaseConverter} 转换器实例
|
|
*/
|
|
export function getConverter(protocol) {
|
|
return ConverterFactory.getConverter(protocol);
|
|
}
|
|
|
|
// =============================================================================
|
|
// 辅助函数 - 从原 convert.js 迁移
|
|
// =============================================================================
|
|
|
|
/**
|
|
* 生成 OpenAI 流式响应的停止块
|
|
* @param {string} model - 模型名称
|
|
* @returns {Object} OpenAI 流式停止块
|
|
*/
|
|
export function getOpenAIStreamChunkStop(model) {
|
|
return {
|
|
id: `chatcmpl-${uuidv4()}`,
|
|
object: "chat.completion.chunk",
|
|
created: Math.floor(Date.now() / 1000),
|
|
model: model,
|
|
system_fingerprint: "",
|
|
choices: [{
|
|
index: 0,
|
|
delta: {
|
|
content: "",
|
|
reasoning_content: ""
|
|
},
|
|
finish_reason: 'stop',
|
|
message: {
|
|
content: "",
|
|
reasoning_content: ""
|
|
}
|
|
}],
|
|
usage:{
|
|
prompt_tokens: 0,
|
|
completion_tokens: 0,
|
|
total_tokens: 0,
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 生成 OpenAI Responses 流式响应的开始事件
|
|
* @param {string} id - 响应 ID
|
|
* @param {string} model - 模型名称
|
|
* @returns {Array} 开始事件数组
|
|
*/
|
|
export function getOpenAIResponsesStreamChunkBegin(id, model) {
|
|
return [
|
|
generateResponseCreated(id, model),
|
|
generateResponseInProgress(id),
|
|
generateOutputItemAdded(id),
|
|
generateContentPartAdded(id)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 生成 OpenAI Responses 流式响应的结束事件
|
|
* @param {string} id - 响应 ID
|
|
* @returns {Array} 结束事件数组
|
|
*/
|
|
export function getOpenAIResponsesStreamChunkEnd(id) {
|
|
return [
|
|
generateOutputTextDone(id),
|
|
generateContentPartDone(id),
|
|
generateOutputItemDone(id),
|
|
generateResponseCompleted(id)
|
|
];
|
|
}
|
|
|
|
// =============================================================================
|
|
// 默认导出
|
|
// =============================================================================
|
|
|
|
export default {
|
|
convertData,
|
|
getRegisteredProtocols,
|
|
isProtocolRegistered,
|
|
clearConverterCache,
|
|
getConverter,
|
|
// 向后兼容的函数
|
|
toOpenAIRequestFromGemini,
|
|
toOpenAIRequestFromClaude,
|
|
toOpenAIChatCompletionFromGemini,
|
|
toOpenAIChatCompletionFromClaude,
|
|
toOpenAIStreamChunkFromGemini,
|
|
toOpenAIStreamChunkFromClaude,
|
|
toOpenAIModelListFromGemini,
|
|
toOpenAIModelListFromClaude,
|
|
toClaudeRequestFromOpenAI,
|
|
toClaudeChatCompletionFromOpenAI,
|
|
toClaudeChatCompletionFromGemini,
|
|
toClaudeStreamChunkFromOpenAI,
|
|
toClaudeStreamChunkFromGemini,
|
|
toClaudeModelListFromOpenAI,
|
|
toClaudeModelListFromGemini,
|
|
toGeminiRequestFromOpenAI,
|
|
toGeminiRequestFromClaude,
|
|
toOpenAIResponsesFromOpenAI,
|
|
toOpenAIResponsesFromClaude,
|
|
toOpenAIResponsesFromGemini,
|
|
toOpenAIResponsesStreamChunkFromOpenAI,
|
|
toOpenAIResponsesStreamChunkFromClaude,
|
|
toOpenAIResponsesStreamChunkFromGemini,
|
|
toOpenAIRequestFromOpenAIResponses,
|
|
toOpenAIChatCompletionFromOpenAIResponses,
|
|
toOpenAIStreamChunkFromOpenAIResponses,
|
|
toClaudeRequestFromOpenAIResponses,
|
|
toGeminiRequestFromOpenAIResponses,
|
|
}; |