UNPKG

route-claudecode

Version:

Advanced routing and transformation system for Claude Code outputs to multiple AI providers

579 lines 23.2 kB
"use strict"; /** * CodeWhisperer 缓冲式客户端 * 现有实现的重命名版本,确保向后兼容 * 项目所有者: Jason Zhang */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CodeWhispererBufferedClient = void 0; const axios_1 = __importDefault(require("axios")); const logger_1 = require("@/utils/logger"); const auth_1 = require("./auth"); const converter_1 = require("./converter"); const parser_1 = require("./parser"); const performance_metrics_1 = require("./config/performance-metrics"); const types_1 = require("./types"); class CodeWhispererBufferedClient { auth; converter; parser; config; streamingConfig; metrics; httpTimeout; maxRetries; tokenBlacklisted = false; // Token状态管理 consecutiveRefreshFailures = 0; maxRefreshFailures = 3; // 最大连续刷新失败次数 constructor(streamingConfig) { this.streamingConfig = streamingConfig || { implementation: 'buffered', realtimeOptions: { enableZeroDelay: false, maxConcurrentStreams: 100, binaryFrameSize: 1024 * 1024, toolCallStrategy: 'buffered', enableCompression: false, }, performanceMetrics: { enableProfiling: false, collectLatencyData: false, memoryUsageTracking: false, metricsIntervalMs: 5000, }, fallback: { enableFallback: true, fallbackToBuffered: true, maxFailuresBeforeFallback: 3, }, }; this.config = (0, types_1.createCodeWhispererConfig)(); this.auth = auth_1.CodeWhispererAuth.getInstance(); this.converter = new converter_1.CodeWhispererConverter(this.config); this.parser = new parser_1.CodeWhispererParser(); this.metrics = new performance_metrics_1.CodeWhispererPerformanceMetrics(this.streamingConfig); this.httpTimeout = 60000; // 60秒超时 this.maxRetries = 3; // Token刷新重试次数 logger_1.logger.debug('CodeWhispererBufferedClient初始化完成', { endpoint: this.config.endpoint, timeout: this.httpTimeout, maxRetries: this.maxRetries, maxRefreshFailures: this.maxRefreshFailures, }); } /** * 处理流式请求 - 重构优化版本 */ async handleStreamRequest(anthropicReq, writeSSE, onError) { const requestId = `buffered_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`; try { const requestInfo = this.createRequestInfo(anthropicReq, requestId); logger_1.logger.info('开始处理缓冲式流式请求', requestInfo); // 开始性能跟踪 this.metrics.startTracking(requestId, 'buffered'); // 使用重试机制执行流式请求 await this.executeWithRetry(async () => { // 获取认证信息 const { accessToken, profileArn, authMethod } = await this.getAuthInfo(); // 构建和验证请求 const cwReq = await this.buildAndValidateRequest(anthropicReq, profileArn, authMethod); const cwReqBody = JSON.stringify(cwReq); // 发送HTTP请求 const responseBuffer = await this.sendHttpRequest(accessToken, cwReqBody, requestId); // 处理响应和事件流 await this.processStreamResponse(responseBuffer, anthropicReq, writeSSE, requestId); }, 'buffered-stream', requestId); // 结束性能跟踪 this.metrics.endTracking(requestId, true); } catch (error) { // 结束性能跟踪(失败) this.metrics.endTracking(requestId, false, error instanceof Error ? error.message : String(error)); await this.handleStreamError(error, onError); } } /** * 检查token是否可用 (类似OpenAI的Key可用性检查) */ isTokenAvailable() { if (this.tokenBlacklisted) { logger_1.logger.warn('Token已被拉黑,不可用', { consecutiveRefreshFailures: this.consecutiveRefreshFailures, maxRefreshFailures: this.maxRefreshFailures }); return false; } return true; } /** * 标记token为不可用 (类似OpenAI的blacklist机制) */ markTokenUnavailable(reason, requestId) { this.tokenBlacklisted = true; logger_1.logger.error(`CodeWhisperer token已拉黑`, { reason, consecutiveRefreshFailures: this.consecutiveRefreshFailures, maxRefreshFailures: this.maxRefreshFailures, requestId }); } /** * 报告token刷新失败 (类似OpenAI的error reporting) */ reportRefreshFailure(error, requestId) { this.consecutiveRefreshFailures++; if (this.consecutiveRefreshFailures >= this.maxRefreshFailures) { this.markTokenUnavailable(`连续${this.consecutiveRefreshFailures}次刷新失败`, requestId); } logger_1.logger.warn('Token刷新失败计数', { consecutiveRefreshFailures: this.consecutiveRefreshFailures, maxRefreshFailures: this.maxRefreshFailures, tokenBlacklisted: this.tokenBlacklisted, requestId, error: error instanceof Error ? error.message : error }); } /** * 报告token刷新成功 (重置失败计数) */ reportRefreshSuccess(requestId) { if (this.consecutiveRefreshFailures > 0) { logger_1.logger.info('Token刷新成功,重置失败计数', { previousFailures: this.consecutiveRefreshFailures, requestId }); this.consecutiveRefreshFailures = 0; } } /** * 执行带重试机制的操作 (类似OpenAI和Gemini的executeWithRetry) */ async executeWithRetry(requestFn, operation, requestId) { // 首先检查token是否可用 if (!this.isTokenAvailable()) { throw new Error('CodeWhisperer token不可用: token已被拉黑,无法执行请求'); } let lastError; for (let attempt = 0; attempt < this.maxRetries; attempt++) { try { // 清除auth缓存以确保获取最新token if (attempt > 0) { this.auth.clearCache(); logger_1.logger.info(`Token重试第${attempt}次`, { requestId, operation }); } return await requestFn(); } catch (error) { lastError = error; // 检查是否是403 token过期错误 if (axios_1.default.isAxiosError(error) && error.response?.status === 403) { logger_1.logger.warn(`检测到403错误,尝试刷新token (尝试 ${attempt + 1}/${this.maxRetries})`, { requestId, operation, errorMessage: error.message }); try { // 尝试刷新token await this.auth.refreshToken(); this.reportRefreshSuccess(requestId); logger_1.logger.info('Token刷新成功,准备重试', { requestId, operation, attempt: attempt + 1 }); // 如果不是最后一次尝试,继续循环重试 if (attempt < this.maxRetries - 1) { continue; } } catch (refreshError) { this.reportRefreshFailure(refreshError, requestId); logger_1.logger.error('Token刷新失败', { requestId, operation, attempt: attempt + 1, refreshError: refreshError instanceof Error ? refreshError.message : refreshError, tokenBlacklisted: this.tokenBlacklisted }); // 如果token已被拉黑,立即抛出错误 if (this.tokenBlacklisted) { throw new Error(`CodeWhisperer token已拉黑: 连续刷新失败${this.consecutiveRefreshFailures}次`); } // 如果token刷新失败,抛出刷新错误 throw new Error(`Token刷新失败: ${refreshError instanceof Error ? refreshError.message : refreshError}`); } } else { // 非403错误,不需要重试,直接抛出 logger_1.logger.error(`非token错误,不进行重试`, { requestId, operation, statusCode: axios_1.default.isAxiosError(error) ? error.response?.status : 'unknown', errorMessage: error instanceof Error ? error.message : error }); throw error; } } } // 如果所有重试都失败,抛出最后一个错误 logger_1.logger.error(`所有重试尝试都失败`, { requestId, operation, maxRetries: this.maxRetries, tokenBlacklisted: this.tokenBlacklisted, lastError: lastError instanceof Error ? lastError.message : lastError }); throw lastError; } /** * 获取认证信息 - 修复支持authMethod */ async getAuthInfo() { // 🚨 关键修复:使用auth的新getAuthInfo方法获取完整认证信息 const authInfo = await this.auth.getAuthInfo(); logger_1.logger.debug('获取认证信息成功', { tokenLength: authInfo.token.length, profileArnLength: authInfo.profileArn.length, authMethod: authInfo.authMethod, strategy: 'enhanced-auth-info' }); return { accessToken: authInfo.token, profileArn: authInfo.profileArn, authMethod: authInfo.authMethod }; } /** * 构建和验证请求 - 修复支持authMethod条件判断 */ async buildAndValidateRequest(anthropicReq, profileArn, authMethod) { // 🚨 关键修复:传递authMethod给converter,支持demo3的条件判断逻辑 const cwReq = await this.converter.buildCodeWhispererRequest(anthropicReq, profileArn, authMethod); logger_1.logger.debug('构建CodeWhisperer请求完成', { conversationId: cwReq.conversationState.conversationId, authMethod, hasProfileArn: !!cwReq.profileArn, strategy: 'demo3-conditional-logic' }); const validation = this.converter.validateRequest(cwReq); if (!validation.isValid) { throw new Error(`请求格式验证失败: ${validation.errors.join(', ')}`); } if (validation.warnings.length > 0) { logger_1.logger.warn('请求验证警告', { warnings: validation.warnings }); } return cwReq; } /** * 发送HTTP请求 */ async sendHttpRequest(accessToken, requestBody, requestId) { logger_1.logger.debug('发送CodeWhisperer请求', { requestId, requestSize: requestBody.length, endpoint: this.config.endpoint, }); const response = await axios_1.default.post(this.config.endpoint, requestBody, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', 'Accept': 'text/event-stream', }, responseType: 'arraybuffer', timeout: this.httpTimeout, }); if (response.status !== 200) { throw new Error(`HTTP请求失败,状态码: ${response.status}`); } const responseBuffer = Buffer.from(response.data); logger_1.logger.debug('收到CodeWhisperer响应', { requestId, responseSize: responseBuffer.length, }); return responseBuffer; } /** * 处理流式响应 */ async processStreamResponse(responseBuffer, anthropicReq, writeSSE, requestId) { // 解析响应事件 const events = this.parser.parseEvents(responseBuffer); if (events.length === 0) { throw new Error('没有解析到任何事件'); } // 生成消息ID const messageId = `msg_${Date.now()}`; // 发送流式事件序列 this.sendInitialEvents(messageId, anthropicReq, writeSSE); const outputTokens = await this.sendParsedEvents(events, writeSSE, requestId); this.sendFinalEvents(outputTokens, writeSSE); logger_1.logger.info('缓冲式流式请求处理完成', { requestId, eventCount: events.length, outputTokens, }); } /** * 发送初始事件 */ sendInitialEvents(messageId, anthropicReq, writeSSE) { // 发送开始事件 const messageStart = { type: 'message_start', message: { id: messageId, type: 'message', role: 'assistant', content: [], model: anthropicReq.model, stop_reason: null, stop_sequence: null, usage: { input_tokens: Math.max(1, Math.floor(this.calculateMessageLength(anthropicReq) / 4)), output_tokens: 1, }, }, }; writeSSE('message_start', messageStart); // 发送ping事件 writeSSE('ping', { type: 'ping' }); // 发送content_block_start事件 const contentBlockStart = { content_block: { text: '', type: 'text', }, index: 0, type: 'content_block_start', }; writeSSE('content_block_start', contentBlockStart); } /** * 发送解析的事件 */ async sendParsedEvents(events, writeSSE, requestId) { let outputTokens = 0; for (const event of events) { writeSSE(event.event, event.data); // 记录性能指标 if (this.streamingConfig.performanceMetrics.collectLatencyData) { this.metrics.recordLatency(requestId, event); } // 计算输出token数量 if (event.event === 'content_block_delta' && event.data?.delta?.text) { outputTokens += Math.floor(event.data.delta.text.length / 4); } // 模拟延时(减少延时以提高性能) await this.sleep(Math.random() * 100); // 从300ms降到100ms } return outputTokens; } /** * 发送结束事件 */ sendFinalEvents(outputTokens, writeSSE) { writeSSE('content_block_stop', { index: 0, type: 'content_block_stop', }); writeSSE('message_delta', { type: 'message_delta', delta: { stop_reason: 'end_turn', stop_sequence: null, }, usage: { output_tokens: Math.max(1, outputTokens), }, }); writeSSE('message_stop', { type: 'message_stop', }); } /** * 处理流式请求错误 */ async handleStreamError(error, onError) { logger_1.logger.error('缓冲式流式请求处理失败', error); // executeWithRetry已经处理了token重试,这里只需要处理最终错误 const errorMessage = error instanceof Error ? error.message : String(error); onError(`CodeWhisperer缓冲式请求失败: ${errorMessage}`, error); } /** * 处理非流式请求 - 重构优化版本 */ async handleNonStreamRequest(anthropicReq) { const requestId = `nonstream_buffered_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`; try { const requestInfo = this.createRequestInfo(anthropicReq, requestId); logger_1.logger.info('开始处理缓冲式非流式请求', requestInfo); // 开始性能跟踪 this.metrics.startTracking(requestId, 'buffered'); // 使用重试机制执行非流式请求 const result = await this.executeWithRetry(async () => { // 获取认证信息 const { accessToken, profileArn, authMethod } = await this.getAuthInfo(); // 构建和验证请求 const cwReq = await this.buildAndValidateRequest(anthropicReq, profileArn, authMethod); const cwReqBody = JSON.stringify(cwReq); // 发送HTTP请求 const response = await this.sendNonStreamHttpRequest(accessToken, cwReqBody, requestId); // 处理响应 return await this.processNonStreamResponse(response, anthropicReq, requestId); }, 'buffered-nonstream', requestId); // 结束性能跟踪 this.metrics.endTracking(requestId, true); return result; } catch (error) { // 结束性能跟踪(失败) this.metrics.endTracking(requestId, false, error instanceof Error ? error.message : String(error)); return await this.handleNonStreamError(error, requestId); } } /** * 发送非流式HTTP请求 */ async sendNonStreamHttpRequest(accessToken, requestBody, requestId) { logger_1.logger.debug('发送CodeWhisperer缓冲式非流式请求', { requestId, requestSize: requestBody.length, endpoint: this.config.endpoint, }); const response = await axios_1.default.post(this.config.endpoint, requestBody, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, responseType: 'arraybuffer', timeout: this.httpTimeout, }); if (response.status !== 200) { throw new Error(`HTTP请求失败,状态码: ${response.status}`); } return Buffer.from(response.data); } /** * 处理非流式响应 */ async processNonStreamResponse(responseBuffer, anthropicReq, requestId) { const responseStr = responseBuffer.toString('utf8'); logger_1.logger.debug('收到CodeWhisperer缓冲式非流式响应', { requestId, responseSize: responseBuffer.length, }); // 检查错误响应 if (responseStr.includes('Improperly formed request.')) { logger_1.logger.error('CodeWhisperer返回格式错误', { requestId, response: responseStr.substring(0, 500) // 只记录前500字符 }); throw new Error(`请求格式错误: ${responseStr}`); } // 解析响应事件 const events = this.parser.parseEvents(responseBuffer); // 构建非流式响应 const anthropicResp = this.parser.buildNonStreamResponse(events, anthropicReq.model); logger_1.logger.info('缓冲式非流式请求处理完成', { requestId, eventCount: events.length, contentBlocks: anthropicResp.content?.length || 0, }); return anthropicResp; } /** * 处理非流式请求错误 */ async handleNonStreamError(error, requestId) { logger_1.logger.error('缓冲式非流式请求处理失败', { requestId, error }); // executeWithRetry已经处理了token重试,这里只需要抛出最终错误 throw error; } /** * 获取客户端类型 */ getClientType() { return 'buffered'; } /** * 健康检查 */ async healthCheck() { try { // 检查认证状态 const tokenAvailable = await this.auth.getToken().then(() => true).catch(() => false); const healthy = tokenAvailable && !this.tokenBlacklisted; return { healthy, type: 'buffered', message: healthy ? 'Buffered client is healthy' : `Issues: ${!tokenAvailable ? 'auth ' : ''}${this.tokenBlacklisted ? 'blacklisted' : ''}`, }; } catch (error) { return { healthy: false, type: 'buffered', message: error instanceof Error ? error.message : String(error), }; } } /** * 创建请求信息(用于日志) */ createRequestInfo(anthropicReq, requestId) { return { requestId, model: anthropicReq.model, messageCount: anthropicReq.messages.length, hasTools: !!(anthropicReq.tools && anthropicReq.tools.length > 0), hasSystem: !!(anthropicReq.system && anthropicReq.system.length > 0), contentLength: this.calculateMessageLength(anthropicReq), }; } /** * 计算消息长度(用于token估算)- 优化版本 */ calculateMessageLength(anthropicReq) { let totalLength = 0; // 计算系统消息长度 if (anthropicReq.system) { for (const sysMsg of anthropicReq.system) { totalLength += sysMsg.text.length; } } // 计算常规消息长度 for (const message of anthropicReq.messages) { totalLength += this.calculateContentLength(message.content); } return totalLength; } /** * 计算内容长度 */ calculateContentLength(content) { if (typeof content === 'string') { return content.length; } if (Array.isArray(content)) { return content.reduce((total, block) => { if (typeof block === 'string') { return total + block.length; } if (block && typeof block === 'object' && 'text' in block) { return total + (block.text?.length || 0); } return total; }, 0); } return 0; } /** * 睡眠函数(用于模拟延时) */ sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } exports.CodeWhispererBufferedClient = CodeWhispererBufferedClient; //# sourceMappingURL=client-buffered.js.map