- 新增日志系统配置选项,支持日志级别、输出模式、文件大小等设置 - 添加当日日志文件下载功能,可通过Web界面直接下载 - 将console.log/error替换为结构化logger,提升日志可管理性 - 在日志页面添加自动滚动到底部功能 - 更新配置示例文件,包含完整的日志配置参数
392 lines
14 KiB
JavaScript
392 lines
14 KiB
JavaScript
/**
|
||
* 协议转换模块 - 新架构版本
|
||
* 使用重构后的转换器架构
|
||
*
|
||
* 这个文件展示了如何使用新的转换器架构
|
||
* 可以逐步替换原有的 convert.js
|
||
*/
|
||
|
||
import { v4 as uuidv4 } from 'uuid';
|
||
import logger from '../utils/logger.js';
|
||
import { MODEL_PROTOCOL_PREFIX, getProtocolPrefix } from '../utils/common.js';
|
||
import { ConverterFactory } from '../converters/ConverterFactory.js';
|
||
import {
|
||
generateResponseCreated,
|
||
generateResponseInProgress,
|
||
generateOutputItemAdded,
|
||
generateContentPartAdded,
|
||
generateOutputTextDone,
|
||
generateContentPartDone,
|
||
generateOutputItemDone,
|
||
generateResponseCompleted
|
||
} from '../providers/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);
|
||
|
||
// 如果目标协议为 forward,直接返回原始数据,无需转换
|
||
if (toProtocol === MODEL_PROTOCOL_PREFIX.FORWARD || fromProtocol === MODEL_PROTOCOL_PREFIX.FORWARD) {
|
||
logger.info(`[Convert] Target protocol is forward, skipping conversion`);
|
||
return data;
|
||
}
|
||
|
||
// 从工厂获取转换器
|
||
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) {
|
||
logger.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,
|
||
};
|
||
|
||
|