UNPKG

route-claudecode

Version:

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

490 lines 20.8 kB
"use strict"; /** * CodeWhisperer 增强客户端 * 集成增强认证管理器和智能重试机制的完整实现 * 基于 AIClient-2-API 的优秀架构设计,完全符合demo3标准 * 项目所有者: Jason Zhang */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EnhancedCodeWhispererClient = void 0; const axios_1 = __importDefault(require("axios")); const logger_1 = require("@/utils/logger"); const auth_1 = require("./auth"); const retry_manager_1 = require("./retry-manager"); const enhanced_auth_config_1 = require("./enhanced-auth-config"); const parser_1 = require("./parser"); // import { ResponsePipeline } from '@/pipeline/response-pipeline'; class EnhancedCodeWhispererClient { auth; retryManager; httpClient; config; parser; // private responsePipeline: ResponsePipeline; requestCount = 0; constructor(config) { // 初始化配置和管理器 this.auth = auth_1.CodeWhispererAuth.getInstance(); this.config = { credentials: enhanced_auth_config_1.DEFAULT_CREDENTIAL_CONFIG, region: typeof enhanced_auth_config_1.DEFAULT_REGION_CONFIG.region === 'string' ? { region: enhanced_auth_config_1.DEFAULT_REGION_CONFIG.region } : enhanced_auth_config_1.DEFAULT_REGION_CONFIG.region, ...config }; this.retryManager = new retry_manager_1.RetryManager(this.config.retry, logger_1.logger); this.parser = new parser_1.CodeWhispererParser(); // 🔧 关键修复:集成响应流水线,符合demo3标准 // Note: ResponsePipeline will be injected via dependency injection when available // For now, we'll handle pipeline processing in the provider layer // 创建 HTTP 客户端 this.httpClient = this.createHttpClient(); this.log('info', 'Enhanced CodeWhisperer Client initialized with pipeline integration', { region: this.config.region?.region, authMethod: this.config.authMethod, retryConfig: this.config.retry, credentialSources: this.config.credentials.priorityOrder, pipelineIntegrated: true }); } /** * 创建配置化的 HTTP 客户端 - 完全符合demo3 header标准 */ createHttpClient() { const client = axios_1.default.create({ timeout: this.config.retry?.timeoutMs || 120000, headers: { 'Content-Type': 'application/json', 'User-Agent': this.config.userAgent || 'CodeWhisperer-Router/2.7.0', // 🔧 关键修复:确保header与demo3标准一致 'Accept': 'application/json', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' } }); // 请求拦截器 - 自动添加认证头,符合demo3多种认证方式标准 client.interceptors.request.use(async (config) => { try { const { token, profileArn, authMethod } = await this.auth.getAuthInfo(); // 🔧 关键修复:支持多种认证头格式,与demo3标准一致 config.headers.Authorization = `Bearer ${token}`; // 添加CodeWhisperer特有的认证头 if (profileArn) { config.headers['X-Profile-Arn'] = profileArn; } if (authMethod) { config.headers['X-Auth-Method'] = authMethod; } this.log('debug', 'Request interceptor: Added comprehensive authorization headers', { url: config.url, method: config.method?.toUpperCase(), hasAuth: !!config.headers.Authorization, hasProfileArn: !!config.headers['X-Profile-Arn'], hasAuthMethod: !!config.headers['X-Auth-Method'], authMethod }); } catch (error) { this.log('error', 'Request interceptor: Failed to get authentication info', { error: error instanceof Error ? error.message : String(error) }); throw error; } return config; }); // 响应拦截器 - 自动处理认证错误 client.interceptors.response.use((response) => response, async (error) => { const originalRequest = error.config; // 检查是否为认证错误且未重试过 if (error.response?.status === 403 && !originalRequest._retried) { originalRequest._retried = true; this.log('warn', 'Response interceptor: 403 error, attempting token refresh', { url: originalRequest.url, status: error.response.status }); try { // 强制刷新 token await this.auth.refreshToken(); // 重新获取 token 并重试请求 const newToken = await this.auth.getToken(); originalRequest.headers.Authorization = `Bearer ${newToken}`; return client(originalRequest); } catch (refreshError) { this.log('error', 'Response interceptor: Token refresh failed', { error: refreshError instanceof Error ? refreshError.message : String(refreshError) }); return Promise.reject(refreshError); } } return Promise.reject(error); }); return client; } /** * 构建 API URL */ buildApiUrl() { const region = this.config.region?.region || 'us-east-1'; const template = this.config.region?.baseUrl || enhanced_auth_config_1.DEFAULT_REGION_CONFIG.baseUrl; return template?.replace('{{region}}', region) || `https://codewhisperer.${region}.amazonaws.com/generateAssistantResponse`; } /** * 处理流式请求 - 完整的错误处理和重试机制 */ async handleStreamRequest(anthropicReq, writeSSE, onError) { const requestId = `enhanced_stream_${++this.requestCount}_${Date.now()}`; const startTime = Date.now(); try { this.log('info', 'Starting enhanced stream request', { requestId, model: anthropicReq.model, messageCount: anthropicReq.messages.length, hasTools: !!anthropicReq.tools?.length, region: this.config.region?.region }); // 通过重试管理器执行请求 await this.retryManager.executeWithRetry(async () => { await this.executeStreamRequest(requestId, anthropicReq, writeSSE, onError); }); const duration = Date.now() - startTime; this.log('info', 'Enhanced stream request completed successfully', { requestId, duration, region: this.config.region?.region }); } catch (error) { const duration = Date.now() - startTime; this.log('error', 'Enhanced stream request failed', { requestId, duration, error: error instanceof Error ? error.message : String(error), region: this.config.region?.region }); onError(`Enhanced CodeWhisperer stream request failed: ${error instanceof Error ? error.message : String(error)}`, error); } } /** * 执行实际的流式请求 - 集成响应流水线处理 */ async executeStreamRequest(requestId, anthropicReq, writeSSE, onError) { // 转换请求格式 const codewhispererReq = await this.transformAnthropicToCodeWhisperer(anthropicReq); const apiUrl = this.buildApiUrl(); this.log('debug', 'Executing stream request with pipeline integration', { requestId, apiUrl, profileArn: codewhispererReq.profileArn, conversationId: codewhispererReq.conversationState.conversationId, pipelineEnabled: true }); try { const response = await this.httpClient.post(apiUrl, codewhispererReq, { responseType: 'stream', headers: { 'Accept': 'text/event-stream', 'Cache-Control': 'no-cache' } }); // 🔧 关键修复:集成响应流水线处理流式数据 const pipelineContext = { requestId, provider: 'codewhisperer', model: anthropicReq.model, isStreaming: true, timestamp: Date.now(), originalModel: anthropicReq.model }; // 处理流式响应 let buffer = ''; response.data.on('data', async (chunk) => { buffer += chunk.toString(); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.trim()) { try { // 先解析流式数据 const parsedData = this.parseStreamLine(line); if (parsedData) { // 通过响应流水线处理 // const processedData = await this.responsePipeline.process(parsedData, pipelineContext); const processedData = parsedData; // 暂时直接使用原数据 // 发送处理后的数据 if (processedData && processedData.content) { writeSSE('content_block_delta', { delta: { text: processedData.content } }); } } } catch (parseError) { this.log('warn', 'Failed to process stream line through pipeline', { requestId, line: line.substring(0, 100), error: parseError instanceof Error ? parseError.message : String(parseError) }); } } } }); response.data.on('end', () => { this.log('debug', 'Stream response ended with pipeline processing', { requestId }); }); response.data.on('error', (streamError) => { const retryableError = retry_manager_1.RetryManager.createRetryableErrorFromResponse(streamError, 'Stream data error'); this.log('error', 'Stream data error', { requestId, error: streamError.message, isRetryable: retryableError.isRetryable }); throw retryableError; }); } catch (error) { // 创建包含错误信息的 RetryableError const retryableError = retry_manager_1.RetryManager.createRetryableErrorFromResponse(error, 'Stream request failed'); this.log('error', 'Stream request execution failed', { requestId, error: error instanceof Error ? error.message : String(error), statusCode: retryableError.statusCode, isRetryable: retryableError.isRetryable }); throw retryableError; } } /** * 处理非流式请求 */ async handleNonStreamRequest(anthropicReq) { const requestId = `enhanced_nonstream_${++this.requestCount}_${Date.now()}`; const startTime = Date.now(); try { this.log('info', 'Starting enhanced non-stream request', { requestId, model: anthropicReq.model, messageCount: anthropicReq.messages.length, region: this.config.region?.region }); // 通过重试管理器执行请求 const result = await this.retryManager.executeWithRetry(async () => { return this.executeNonStreamRequest(requestId, anthropicReq); }); const duration = Date.now() - startTime; this.log('info', 'Enhanced non-stream request completed successfully', { requestId, duration, region: this.config.region?.region }); return result; } catch (error) { const duration = Date.now() - startTime; this.log('error', 'Enhanced non-stream request failed', { requestId, duration, error: error instanceof Error ? error.message : String(error), region: this.config.region?.region }); throw error; } } /** * 执行实际的非流式请求 - 集成响应流水线处理 */ async executeNonStreamRequest(requestId, anthropicReq) { // 转换请求格式 const codewhispererReq = await this.transformAnthropicToCodeWhisperer(anthropicReq); const apiUrl = this.buildApiUrl(); this.log('debug', 'Executing non-stream request with pipeline integration', { requestId, apiUrl, profileArn: codewhispererReq.profileArn, pipelineEnabled: true }); try { const response = await this.httpClient.post(apiUrl, codewhispererReq, { headers: { 'Accept': 'application/json' } }); // 🔧 关键修复:集成响应流水线,符合demo3标准架构 const pipelineContext = { requestId, provider: 'codewhisperer', model: anthropicReq.model, isStreaming: false, timestamp: Date.now(), originalModel: anthropicReq.model }; this.log('debug', 'Processing response through pipeline', { requestId, bufferLength: Buffer.from(response.data).length, pipelineContext }); // 先使用parser解析CodeWhisperer特有的响应格式 const responseBuffer = Buffer.from(response.data); const events = this.parser.parseEvents(responseBuffer); const parsedResponse = this.parser.buildNonStreamResponse(events, anthropicReq.model); // 然后通过响应流水线进行统一处理 // const processedResponse = await this.responsePipeline.process(parsedResponse, pipelineContext); const processedResponse = parsedResponse; // 暂时直接使用原数据 this.log('debug', 'Pipeline processing completed', { requestId, eventCount: events.length, contentBlocks: processedResponse.content?.length || 0, hasToolUse: processedResponse.content?.some((c) => c.type === 'tool_use') || false, pipelineProcessed: true }); return processedResponse; } catch (error) { const retryableError = retry_manager_1.RetryManager.createRetryableErrorFromResponse(error, 'Non-stream request failed'); this.log('error', 'Non-stream request execution failed', { requestId, error: error instanceof Error ? error.message : String(error), statusCode: retryableError.statusCode, isRetryable: retryableError.isRetryable }); throw retryableError; } } /** * 转换 Anthropic 请求为 CodeWhisperer 格式 */ async transformAnthropicToCodeWhisperer(anthropicReq) { const profileArn = await this.auth.getProfileArn(); // 获取最新的用户消息内容 const latestUserMessage = anthropicReq.messages[anthropicReq.messages.length - 1]; const content = Array.isArray(latestUserMessage.content) ? latestUserMessage.content.map(c => typeof c === 'string' ? c : c.text || '').join('') : latestUserMessage.content; // 构建历史消息 const history = []; for (let i = 0; i < anthropicReq.messages.length - 1; i++) { const msg = anthropicReq.messages[i]; const messageContent = Array.isArray(msg.content) ? msg.content.map(c => typeof c === 'string' ? c : c.text || '').join('') : msg.content; if (msg.role === 'user') { history.push({ userInputMessage: { content: messageContent, modelId: anthropicReq.model, origin: 'chat' } }); } else if (msg.role === 'assistant') { history.push({ assistantResponseMessage: { content: messageContent, toolUses: [] } }); } } return { profileArn, conversationState: { chatTriggerType: 'manual', conversationId: `conv_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`, currentMessage: { userInputMessage: { content, modelId: anthropicReq.model, origin: 'chat', userInputMessageContext: {} } }, history } }; } /** * 解析流式数据行 - 返回解析后的数据供流水线处理 */ parseStreamLine(line) { if (line.startsWith('data: ')) { const data = line.substring(6); if (data === '[DONE]') { return null; } try { const parsed = JSON.parse(data); return parsed; } catch (parseError) { // 忽略解析错误,继续处理其他行 this.log('debug', 'Failed to parse stream data', { data: data.substring(0, 100), error: parseError instanceof Error ? parseError.message : String(parseError) }); return null; } } return null; } /** * 健康检查 */ async healthCheck() { try { const tokenValid = await this.auth.validateToken(); return { healthy: tokenValid, type: 'enhanced-codewhisperer', message: tokenValid ? `Ready in region ${this.config.region?.region || 'us-east-1'}` : 'Authentication failed' }; } catch (error) { return { healthy: false, type: 'enhanced-codewhisperer', message: `Health check failed: ${error instanceof Error ? error.message : String(error)}` }; } } /** * 获取客户端类型 */ getClientType() { return 'realtime'; // 增强客户端基于实时处理 } /** * 获取当前配置 */ getConfig() { return this.config; } /** * 更新配置 */ updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; // 重新创建 HTTP 客户端以应用新配置 this.httpClient = this.createHttpClient(); this.log('info', 'Enhanced client configuration updated', { region: this.config.region?.region, authMethod: this.config.authMethod }); } /** * 日志输出方法 */ log(level, message, meta) { if (!this.config.enableDebugLog && level === 'debug') { return; } if (logger_1.logger) { logger_1.logger[level]?.(message, meta); } else { console.log(`[EnhancedCodeWhispererClient] ${level.toUpperCase()}: ${message}`, meta || ''); } } } exports.EnhancedCodeWhispererClient = EnhancedCodeWhispererClient; //# sourceMappingURL=enhanced-client.js.map