UNPKG

route-claudecode

Version:

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

417 lines 14.6 kB
"use strict"; /** * CodeWhisperer Transformer - 统一格式转换 * 将CodeWhisperer格式与统一格式之间进行转换 * 项目所有者: Jason Zhang */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CodeWhispererTransformer = void 0; exports.createCodeWhispererTransformer = createCodeWhispererTransformer; const logger_1 = require("@/utils/logger"); class CodeWhispererTransformer { name = 'codewhisperer'; /** * 🎯 Convert BaseRequest (Anthropic format) to CodeWhisperer format * 这是Provider调用的主要入口点 */ transformBaseToCodeWhisperer(request) { if (!request) { throw new Error('BaseRequest is null or undefined - violates zero fallback principle'); } const cwRequest = { model: request.model, max_tokens: request.max_tokens || 131072, temperature: request.temperature, messages: this.convertAnthropicMessagesToCodeWhisperer(request.messages || []) }; // 处理系统消息 if (request.system) { cwRequest.system = typeof request.system === 'string' ? [{ type: 'text', text: request.system }] : request.system; } // 处理工具定义 if (request.tools && Array.isArray(request.tools) && request.tools.length > 0) { cwRequest.tools = this.convertAnthropicToolsToCodeWhisperer(request.tools); } console.log('🔄 [CODEWHISPERER-TRANSFORMER] BaseRequest -> CodeWhisperer:', { hasTools: !!(cwRequest.tools && cwRequest.tools.length > 0), toolCount: cwRequest.tools?.length || 0, messageCount: cwRequest.messages.length, model: cwRequest.model }); return cwRequest; } /** * 🎯 Convert CodeWhisperer response to BaseResponse (Anthropic format) * 这是Provider调用的主要出口点 */ transformCodeWhispererToBase(response, originalRequest) { if (!response) { throw new Error('CodeWhisperer response is null or undefined - silent failure detected'); } if (!response.content || response.content.length === 0) { throw new Error('CodeWhisperer response missing content - invalid response format'); } // 🎯 修复finish_reason映射 const finishReason = this.mapCodeWhispererStopReasonToAnthropic(response.stop_reason, this.hasToolCallsInContent(response.content)); const baseResponse = { id: response.id || `msg_${Date.now()}`, content: response.content, // CodeWhisperer already uses Anthropic content format model: originalRequest.metadata?.originalModel || response.model, role: 'assistant', stop_reason: finishReason, stop_sequence: null, usage: { input_tokens: response.usage.input_tokens || 0, output_tokens: response.usage.output_tokens || 0 } }; console.log('🔄 [CODEWHISPERER-TRANSFORMER] CodeWhisperer -> BaseResponse:', { hasTools: response.content.some((c) => c.type === 'tool_use'), toolCount: response.content.filter((c) => c.type === 'tool_use').length, stopReason: finishReason, contentBlocks: response.content.length }); return baseResponse; } /** * 转换请求到统一格式 */ transformRequestToUnified(request) { const unified = { model: request.model, messages: this.normalizeMessages(request.messages), max_tokens: request.max_tokens || 131072, temperature: request.temperature, stream: request.stream || false }; // 处理system消息 if (request.system && Array.isArray(request.system)) { unified.system = request.system.map(s => s.text).join('\n\n'); } // 处理tools if (request.tools && Array.isArray(request.tools)) { unified.tools = request.tools.map(tool => ({ type: 'function', function: { name: tool.name, description: tool.description, parameters: tool.input_schema } })); } logger_1.logger.debug('CodeWhisperer request transformed to unified', { model: unified.model, messageCount: unified.messages.length, hasSystem: !!unified.system, hasTools: !!(unified.tools && unified.tools.length > 0) }); return unified; } /** * 从统一格式转换请求 */ transformRequestFromUnified(unified) { const request = { model: unified.model, max_tokens: unified.max_tokens || 131072, messages: this.denormalizeMessages(unified.messages), stream: unified.stream || false, temperature: unified.temperature }; // 处理system消息 if (unified.system) { request.system = [{ type: 'text', text: unified.system }]; } // 处理tools if (unified.tools && unified.tools.length > 0) { request.tools = unified.tools.map(tool => ({ name: tool.function.name, description: tool.function.description, input_schema: tool.function.parameters })); } logger_1.logger.debug('Unified request transformed to CodeWhisperer', { model: request.model, messageCount: request.messages.length, hasSystem: !!(request.system && request.system.length > 0), hasTools: !!(request.tools && request.tools.length > 0) }); return request; } /** * 转换响应到统一格式 */ transformResponseToUnified(response) { const unified = { id: response.id, object: 'chat.completion', created: Math.floor(Date.now() / 1000), model: response.model, choices: [{ index: 0, message: { role: response.role, content: this.extractTextContent(response.content), tool_calls: this.extractToolCalls(response.content) }, finish_reason: this.mapStopReason(response.stop_reason) }], usage: { prompt_tokens: response.usage.input_tokens, completion_tokens: response.usage.output_tokens, total_tokens: response.usage.input_tokens + response.usage.output_tokens } }; logger_1.logger.debug('CodeWhisperer response transformed to unified', { id: unified.id, contentBlocks: response.content.length, stopReason: unified.choices[0].finish_reason }); return unified; } /** * 从统一格式转换响应 */ transformResponseFromUnified(unified) { const choice = unified.choices[0]; const response = { id: unified.id, type: 'message', model: unified.model, role: choice.message.role, content: this.buildContentFromUnified(choice.message), stop_reason: this.mapStopReason(choice.finish_reason), stop_sequence: null, usage: { input_tokens: unified.usage.prompt_tokens, output_tokens: unified.usage.completion_tokens } }; logger_1.logger.debug('Unified response transformed to CodeWhisperer', { id: response.id, contentBlocks: response.content.length, stopReason: response.stop_reason }); return response; } /** * 标准化消息格式 */ normalizeMessages(messages) { return messages.map(message => ({ role: message.role, content: this.normalizeMessageContent(message.content) })); } /** * 反标准化消息格式 */ denormalizeMessages(messages) { return messages.map(message => ({ role: message.role, content: this.denormalizeMessageContent(message.content) })); } /** * 标准化消息内容 */ normalizeMessageContent(content) { if (typeof content === 'string') { return content; } if (Array.isArray(content)) { return content.map(block => { if (block.type === 'text') { return { type: 'text', text: block.text }; } if (block.type === 'tool_use') { return { type: 'tool_use', id: block.id, name: block.name, input: block.input }; } return block; }); } return content; } /** * 反标准化消息内容 */ denormalizeMessageContent(content) { // CodeWhisperer使用与Anthropic相似的格式,直接返回 return content; } /** * 标准化内容块 */ normalizeContent(content) { return content.map(block => { if (block.type === 'text') { return { type: 'text', text: block.text }; } if (block.type === 'tool_use') { return { type: 'tool_use', id: block.id, name: block.name, input: block.input }; } return block; }); } /** * 反标准化内容块 */ denormalizeContent(content) { return content.map(block => { if (block.type === 'text') { return { type: 'text', text: block.text }; } if (block.type === 'tool_use') { return { type: 'tool_use', id: block.id, name: block.name, input: block.input }; } return block; }); } /** * 映射stop reason到统一格式 */ mapStopReason(stopReason) { const mapping = { 'end_turn': 'end_turn', 'max_tokens': 'max_tokens', 'tool_use': 'tool_use', 'stop_sequence': 'stop_sequence' }; return mapping[stopReason] || stopReason; } /** * 🔧 Convert Anthropic messages to CodeWhisperer format */ convertAnthropicMessagesToCodeWhisperer(messages) { if (!Array.isArray(messages)) { throw new Error('Messages must be an array - violates zero fallback principle'); } return messages.map(msg => { if (!msg || typeof msg !== 'object') { throw new Error('Invalid message object - violates zero fallback principle'); } const cwMsg = { role: msg.role, content: msg.content }; return cwMsg; }); } /** * 🔧 Convert Anthropic tools to CodeWhisperer format */ convertAnthropicToolsToCodeWhisperer(tools) { if (!Array.isArray(tools)) { throw new Error('Tools must be an array - violates zero fallback principle'); } return tools.map(tool => { if (!tool || typeof tool !== 'object') { throw new Error('Invalid tool object - violates zero fallback principle'); } if (!tool.name) { throw new Error('Tool missing name - violates zero fallback principle'); } return { name: tool.name, description: tool.description || '', input_schema: tool.input_schema || {} }; }); } /** * 🎯 Map CodeWhisperer stop_reason to Anthropic stop_reason */ mapCodeWhispererStopReasonToAnthropic(stopReason, hasToolCalls) { // 🔧 Critical Fix: 如果有工具调用,强制返回tool_use if (hasToolCalls && (stopReason === 'end_turn' || stopReason === 'stop')) { return 'tool_use'; } const mapping = { 'stop': 'end_turn', 'end_turn': 'end_turn', 'max_tokens': 'max_tokens', 'tool_use': 'tool_use', 'stop_sequence': 'stop_sequence' }; return mapping[stopReason] || 'end_turn'; } /** * 🔧 Check if content has tool calls */ hasToolCallsInContent(content) { return content.some((block) => block.type === 'tool_use'); } /** * 提取文本内容 */ extractTextContent(content) { const textBlocks = content.filter(block => block.type === 'text'); if (textBlocks.length === 0) return null; return textBlocks.map(block => block.text).join(''); } /** * 提取工具调用 */ extractToolCalls(content) { const toolCalls = content.filter(block => block.type === 'tool_use'); if (toolCalls.length === 0) return undefined; return toolCalls.map(tool => ({ id: tool.id, type: 'function', function: { name: tool.name, arguments: JSON.stringify(tool.input) } })); } /** * 从统一格式构建内容 */ buildContentFromUnified(message) { const content = []; if (message.content) { content.push({ type: 'text', text: message.content }); } if (message.tool_calls) { for (const toolCall of message.tool_calls) { content.push({ type: 'tool_use', id: toolCall.id, name: toolCall.function.name, input: JSON.parse(toolCall.function.arguments) }); } } return content; } } exports.CodeWhispererTransformer = CodeWhispererTransformer; /** * 创建CodeWhisperer转换器实例 */ function createCodeWhispererTransformer() { return new CodeWhispererTransformer(); } //# sourceMappingURL=codewhisperer.js.map