route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
237 lines • 10.1 kB
JavaScript
"use strict";
/**
* Unified OpenAI Client - 统一OpenAI客户端实现
* 项目所有者: Jason Zhang
*
* 合并Pure Client和SDK Client的功能,消除重复代码
* 遵循零硬编码、零Fallback、零沉默失败原则
* 符合六层清晰架构:Provider只做API调用,所有转换在Transformer层
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnifiedOpenAIClient = void 0;
exports.createUnifiedOpenAIClient = createUnifiedOpenAIClient;
const openai_1 = __importDefault(require("openai"));
const logger_1 = require("@/utils/logger");
const simple_session_manager_1 = require("@/session/simple-session-manager");
const response_validation_1 = require("@/utils/response-validation");
const config_validation_1 = require("@/utils/config-validation");
/**
* 统一OpenAI客户端 - 合并重复实现
* 基于项目记忆中的成功重构经验
*/
class UnifiedOpenAIClient {
name;
type = 'openai-unified';
config;
openaiClient;
validatedConfig;
sessionManager;
constructor(config, providerId) {
this.name = providerId;
this.config = config;
// 🚨 严格配置验证 - 零fallback原则
this.validatedConfig = (0, config_validation_1.validateProviderConfig)(config, providerId, config.sdkOptions);
// 初始化统一OpenAI客户端
this.openaiClient = new openai_1.default({
apiKey: this.validatedConfig.apiKey,
baseURL: this.validatedConfig.baseURL,
timeout: this.validatedConfig.sdkOptions.timeout,
maxRetries: this.validatedConfig.sdkOptions.maxRetries,
defaultHeaders: this.validatedConfig.sdkOptions.defaultHeaders
});
// 初始化会话管理
this.sessionManager = (0, simple_session_manager_1.getSimpleSessionManager)(this.validatedConfig.port);
logger_1.logger.info('Unified OpenAI Client initialized', {
providerId,
baseURL: this.validatedConfig.baseURL,
hasApiKey: true,
port: this.validatedConfig.port,
clientType: config.clientType || 'unified',
architecture: 'six-layer-clean'
});
}
/**
* 🎯 发送非流式请求 - 统一实现
* 假设request.metadata.openaiRequest包含已转换的OpenAI格式请求
*/
async sendRequest(request) {
const originalRequestId = request.metadata?.requestId || 'unknown';
const sessionId = request.metadata?.sessionId;
const conversationId = request.metadata?.conversationId;
// 生成会话ID(如果有会话信息)
let requestId = originalRequestId;
if (sessionId && conversationId) {
requestId = this.sessionManager.generateRequestId(sessionId, conversationId, false);
request.metadata = { ...request.metadata, requestId, originalRequestId };
}
try {
// 🔍 从metadata中获取已转换的OpenAI请求
const openaiRequest = request.metadata?.openaiRequest;
if (!openaiRequest) {
throw new Error('Missing openaiRequest in metadata - transformer step required');
}
logger_1.logger.debug('Unified OpenAI API call', {
model: openaiRequest.model,
hasTools: !!(openaiRequest.tools && openaiRequest.tools.length > 0),
messageCount: openaiRequest.messages?.length || 0,
requestId,
provider: this.name
});
// 🎯 纯粹的API调用,不做转换
const response = await this.openaiClient.chat.completions.create(openaiRequest);
// 🔄 包装为BaseResponse格式进行验证
const baseResponse = this.wrapResponse(response, requestId);
// 🚨 严格响应验证
(0, response_validation_1.validateNonStreamingResponse)(baseResponse, requestId, this.name);
logger_1.logger.debug('Unified OpenAI API response received', {
responseId: response.id,
model: response.model,
hasChoices: !!(response.choices && response.choices.length > 0),
finishReason: response.choices?.[0]?.finish_reason,
requestId,
provider: this.name
});
// 🔄 返回包装后的BaseResponse,保留原始响应在metadata中
baseResponse.metadata = {
...baseResponse.metadata,
openaiResponse: response,
providerId: this.name,
responseId: response.id
};
return baseResponse;
}
catch (error) {
logger_1.logger.error('Unified OpenAI API call failed', {
error: error instanceof Error ? error.message : String(error),
provider: this.name,
requestId
});
throw (0, response_validation_1.handleProviderError)(error, requestId, this.name, 'non-streaming');
}
}
/**
* 🎯 发送流式请求 - 统一实现
*/
async *sendStreamRequest(request) {
const originalRequestId = request.metadata?.requestId || 'unknown';
const sessionId = request.metadata?.sessionId;
const conversationId = request.metadata?.conversationId;
// 生成会话ID(如果有会话信息)
let requestId = originalRequestId;
if (sessionId && conversationId) {
requestId = this.sessionManager.generateRequestId(sessionId, conversationId, true);
request.metadata = { ...request.metadata, requestId, originalRequestId };
}
try {
// 🔍 从metadata中获取已转换的OpenAI请求
const openaiRequest = request.metadata?.openaiRequest;
if (!openaiRequest) {
throw new Error('Missing openaiRequest in metadata - transformer step required');
}
// 确保流式标志
openaiRequest.stream = true;
logger_1.logger.debug('Unified OpenAI streaming API call', {
model: openaiRequest.model,
hasTools: !!(openaiRequest.tools && openaiRequest.tools.length > 0),
requestId,
provider: this.name
});
// 🎯 纯粹的流式API调用
const streamingRequest = { ...openaiRequest, stream: true };
const stream = await this.openaiClient.chat.completions.create(streamingRequest);
for await (const chunk of stream) {
// 🔄 将原始chunk放入metadata,由transformer处理转换
const chunkWithMetadata = {
...request,
metadata: {
...request.metadata,
openaiChunk: chunk,
providerId: this.name,
chunkId: chunk.id
}
};
yield chunkWithMetadata;
}
logger_1.logger.debug('Unified OpenAI streaming API completed', {
requestId,
provider: this.name
});
}
catch (error) {
logger_1.logger.error('Unified OpenAI streaming API failed', {
error: error instanceof Error ? error.message : String(error),
provider: this.name,
requestId
});
throw (0, response_validation_1.handleProviderError)(error, requestId, this.name, 'streaming');
}
}
/**
* 🔧 健康检查 - 统一实现
*/
async isHealthy() {
try {
const response = await this.openaiClient.chat.completions.create({
model: this.validatedConfig.defaultModel,
messages: [{ role: 'user', content: 'health check' }],
max_tokens: 1,
temperature: 0
});
const isHealthy = !!(response?.choices?.[0]);
logger_1.logger.debug('Health check completed', {
provider: this.name,
healthy: isHealthy,
model: this.validatedConfig.defaultModel
});
return isHealthy;
}
catch (error) {
logger_1.logger.warn('Health check failed', {
provider: this.name,
error: error instanceof Error ? error.message : String(error)
});
return false;
}
}
/**
* 🎯 获取支持的模型 - 统一实现
*/
async getSupportedModels() {
// 从原始配置中返回支持的模型,不硬编码
if (Array.isArray(this.config.models)) {
return this.config.models;
}
// 默认返回配置的默认模型
return this.config.defaultModel ? [this.config.defaultModel] : [];
}
/**
* 🔄 包装原始响应为BaseResponse格式
*/
wrapResponse(rawResponse, requestId) {
return {
id: rawResponse.id || `provider-${Date.now()}`,
model: rawResponse.model,
role: 'assistant',
content: rawResponse.choices?.[0]?.message?.content ?
[{ type: 'text', text: rawResponse.choices[0].message.content }] : [],
stop_reason: rawResponse.choices?.[0]?.finish_reason || 'unknown',
usage: rawResponse.usage,
metadata: {
requestId,
provider: this.name,
rawResponse // 保留原始响应供外部转换使用
}
};
}
}
exports.UnifiedOpenAIClient = UnifiedOpenAIClient;
/**
* 🎯 工厂函数:创建统一OpenAI客户端
*/
function createUnifiedOpenAIClient(config, providerId) {
return new UnifiedOpenAIClient(config, providerId);
}
//# sourceMappingURL=unified-client.js.map