UNPKG

route-claudecode

Version:

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

187 lines 7.23 kB
"use strict"; /** * CodeWhisperer Streaming Handler - 统一流式处理 * 处理CodeWhisperer流式响应,转换为Anthropic SSE格式 * 项目所有者: Jason Zhang */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CodeWhispererStreamingHandlerImpl = void 0; exports.createCodeWhispererStreamingHandler = createCodeWhispererStreamingHandler; const logger_1 = require("@/utils/logger"); const response_validation_1 = require("@/utils/response-validation"); /** * CodeWhisperer流式处理器实现 */ class CodeWhispererStreamingHandlerImpl { config; constructor(config) { this.config = config; } /** * 处理流式请求 */ async *processStreamRequest(request) { const requestId = request.metadata?.requestId || 'unknown'; try { // 🔄 使用transformer转换请求 const cwRequest = { ...this.config.transformer.transformBaseToCodeWhisperer(request), stream: true }; logger_1.logger.debug('Sending streaming request to CodeWhisperer', { model: cwRequest.model, hasTools: !!(cwRequest.tools && cwRequest.tools.length > 0), messageCount: cwRequest.messages.length, requestId, provider: this.config.providerName }, requestId, 'provider'); // 🎯 发送流式请求到CodeWhisperer const response = await this.config.httpClient.post('/chat/completions', cwRequest, { responseType: 'stream' }); let messageId = `msg_${Date.now()}`; let hasStarted = false; let chunkCount = 0; let hasValidContent = false; let finishReason; // 处理流式响应 const stream = this.parseSSEStream(response.data); for await (const chunk of stream) { chunkCount++; // 🚨 验证流式chunk (0, response_validation_1.validateStreamingChunk)(chunk, requestId, this.config.providerName, chunkCount); // 跟踪有效内容 if ((0, response_validation_1.isValidContentChunk)(chunk)) { hasValidContent = true; } // 发送message_start事件 if (!hasStarted) { yield { event: 'message_start', data: { type: 'message_start', message: { id: messageId, type: 'message', role: 'assistant', content: [], model: request.metadata?.originalModel || cwRequest.model, stop_reason: null, stop_sequence: null, usage: { input_tokens: 0, output_tokens: 0 } } } }; hasStarted = true; } // 🎯 转换和转发chunk const convertedChunk = this.convertCodeWhispererChunkToAnthropic(chunk, messageId); if (convertedChunk) { // 提取finish reason if (convertedChunk.event === 'message_delta' && convertedChunk.data?.delta?.stop_reason) { finishReason = convertedChunk.data.delta.stop_reason; } yield convertedChunk; } } // 🚨 确保流式响应产生了有效内容 if (chunkCount === 0) { const error = new Error('CodeWhisperer streaming request produced no chunks - potential silent failure'); console.error(`🚨 [${this.config.providerName}] STREAMING SILENT FAILURE DETECTED:`); console.error(` Request ID: ${requestId}`); console.error(` Chunks: ${chunkCount}`); console.error(` Valid Content: ${hasValidContent}`); throw error; } logger_1.logger.debug('CodeWhisperer streaming request completed successfully', { chunkCount, hasValidContent, finishReason, requestId, provider: this.config.providerName }, requestId, 'provider'); } catch (error) { logger_1.logger.error('CodeWhisperer streaming request failed', { error: error instanceof Error ? error.message : String(error), provider: this.config.providerName, model: request.model, requestId }, requestId, 'provider'); throw error; } } /** * 解析SSE流 */ async *parseSSEStream(stream) { let buffer = ''; for await (const chunk of stream) { buffer += chunk.toString(); const lines = buffer.split('\n'); buffer = lines.pop() || ''; // 保留不完整的行 for (const line of lines) { if (line.startsWith('data: ')) { const data = line.slice(6).trim(); if (data === '[DONE]') { return; } try { const parsed = JSON.parse(data); yield parsed; } catch (error) { logger_1.logger.warn('Failed to parse SSE data', { line, error }); } } } } } /** * 转换CodeWhisperer chunk为Anthropic格式 */ convertCodeWhispererChunkToAnthropic(chunk, messageId) { if (!chunk || typeof chunk !== 'object') { return null; } // CodeWhisperer通常直接使用Anthropic格式 // 但需要确保格式标准化 if (chunk.type === 'content_block_delta') { return { event: 'content_block_delta', data: chunk }; } if (chunk.type === 'content_block_start') { return { event: 'content_block_start', data: chunk }; } if (chunk.type === 'message_delta') { return { event: 'message_delta', data: chunk }; } if (chunk.type === 'message_stop') { return { event: 'message_stop', data: chunk }; } // 如果是其他格式,尝试转换 return { event: 'ping', data: chunk }; } } exports.CodeWhispererStreamingHandlerImpl = CodeWhispererStreamingHandlerImpl; /** * 创建CodeWhisperer流式处理器 */ function createCodeWhispererStreamingHandler(config) { return new CodeWhispererStreamingHandlerImpl(config); } //# sourceMappingURL=codewhisperer-streaming-handler.js.map