UNPKG

route-claudecode

Version:

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

230 lines 9.96 kB
"use strict"; /** * Anthropic Output Format Processor * Converts provider responses to Anthropic API format */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AnthropicOutputProcessor = void 0; const logger_1 = require("@/utils/logger"); // import { PipelineDebugger } from '@/debug/pipeline-debugger'; // 已迁移到统一日志系统 const logger_2 = require("@/utils/logger"); const consistency_validator_1 = require("./consistency-validator"); const response_converter_1 = require("@/transformers/response-converter"); class AnthropicOutputProcessor { name = 'anthropic'; pipelineDebugger; consistencyValidator; port; constructor(port) { // 🔧 修复硬编码:必须明确指定端口,不允许fallback this.port = port; this.pipelineDebugger = new logger_2.PipelineDebugger(port); this.consistencyValidator = (0, consistency_validator_1.getConsistencyValidator)(port); } /** * Check if this processor can handle the response format */ canProcess(response, format) { return format === 'anthropic' || format === 'claude'; } /** * Process provider response to Anthropic format */ async process(response, originalRequest) { const requestId = originalRequest.metadata?.requestId || 'unknown'; try { logger_1.logger.trace(requestId, 'output', 'Processing response to Anthropic format', { responseType: typeof response, hasContent: !!response.content }); // 使用统一的转换器处理所有格式 console.log(`🔍 [DEBUG-PROCESS] Using unified response converter`); let anthropicResponse; if (this.isAnthropicFormat(response)) { console.log(`🔍 [DEBUG-PROCESS] Validating existing Anthropic format`); anthropicResponse = (0, response_converter_1.validateAndNormalizeAnthropicResponse)(response, originalRequest, requestId); } else { console.log(`🔍 [DEBUG-PROCESS] Converting from other format`); anthropicResponse = (0, response_converter_1.convertToAnthropicResponse)(response, originalRequest, requestId); } console.log(`🔍 [DEBUG-PROCESS] Final result stop_reason: "${anthropicResponse.stop_reason || 'undefined'}"`); // 🎯 最终一致性验证 - 输出前强制检查finish reason一致性 if (this.consistencyValidator.shouldValidate(anthropicResponse)) { const validationResult = this.consistencyValidator.validateAndFix(anthropicResponse, requestId); const finalResponse = validationResult.response; if (validationResult.result.fixed) { logger_1.logger.info('🔧 [OUTPUT] Final consistency fix applied', { report: this.consistencyValidator.generateValidationReport(validationResult.result), requestId }, requestId, 'output-consistency'); } // 🔧 [FIXED] 移除错误的max_tokens检查 - max_tokens是正常完成状态,不是错误 // MaxTokensErrorHandler.checkAndThrowMaxTokensError( // finalResponse, // originalRequest.metadata?.targetProvider || 'unknown', // originalRequest.metadata?.originalModel || originalRequest.model, // requestId // ); logger_1.logger.trace(requestId, 'output', 'Response processed successfully', { contentBlocks: finalResponse.content?.length || 0, usage: finalResponse.usage }); return finalResponse; } // 🔧 [FIXED] 移除错误的max_tokens检查 - max_tokens是正常完成状态,不是错误 // MaxTokensErrorHandler.checkAndThrowMaxTokensError( // anthropicResponse, // originalRequest.metadata?.targetProvider || 'unknown', // originalRequest.metadata?.originalModel || originalRequest.model, // requestId // ); logger_1.logger.trace(requestId, 'output', 'Response processed successfully', { contentBlocks: anthropicResponse.content?.length || 0, usage: anthropicResponse.usage }); return anthropicResponse; } catch (error) { logger_1.logger.error('Failed to process response to Anthropic format', error, requestId, 'output'); throw error; } } /** * Check if response is already in Anthropic format */ isAnthropicFormat(response) { return (response && typeof response === 'object' && response.role === 'assistant' && Array.isArray(response.content) && (response.type === 'message' || !response.type)); } /** * Normalize content to Anthropic format with tool error detection */ normalizeContent(content, requestId) { if (!content) return []; if (typeof content === 'string') { this.checkForToolCallsInText(content, requestId || 'unknown', 'content-normalization'); return [{ type: 'text', text: content }]; } if (Array.isArray(content)) { return content.map(block => this.normalizeContentBlock(block, requestId)); } if (typeof content === 'object') { return [this.normalizeContentBlock(content, requestId)]; } const textContent = String(content); this.checkForToolCallsInText(textContent, requestId || 'unknown', 'content-normalization'); return [{ type: 'text', text: textContent }]; } /** * Normalize a single content block with tool error detection */ normalizeContentBlock(block, requestId) { if (typeof block === 'string') { // Check for tool calls in text content this.checkForToolCallsInText(block, requestId || 'unknown', 'output-processing'); return { type: 'text', text: block }; } if (!block || typeof block !== 'object') { const textContent = String(block); // Check for tool calls in converted text this.checkForToolCallsInText(textContent, requestId || 'unknown', 'output-processing'); return { type: 'text', text: textContent }; } // Already in correct format if (block.type && (block.text || block.id || block.content)) { // Check text content in properly formatted blocks if (block.type === 'text' && block.text) { this.checkForToolCallsInText(block.text, requestId || 'unknown', 'output-processing'); } return block; } // Convert common formats if (block.content && !block.type) { this.checkForToolCallsInText(block.content, requestId || 'unknown', 'output-processing'); return { type: 'text', text: block.content }; } if (block.message && !block.type) { this.checkForToolCallsInText(block.message, requestId || 'unknown', 'output-processing'); return { type: 'text', text: block.message }; } // Default to text block const jsonText = JSON.stringify(block); this.checkForToolCallsInText(jsonText, requestId || 'unknown', 'output-processing'); return { type: 'text', text: jsonText }; } /** * Check for tool calls appearing in text areas (error condition) */ checkForToolCallsInText(text, requestId, stage) { this.pipelineDebugger.detectToolCallError(text, requestId, stage, 'anthropic', 'unknown'); } /** * Estimate input tokens */ estimateInputTokens(request) { try { let totalChars = 0; request.messages.forEach(msg => { if (typeof msg.content === 'string') { totalChars += msg.content.length; } else if (Array.isArray(msg.content)) { msg.content.forEach((block) => { if (block.text) totalChars += block.text.length; }); } }); // Add system and tools if (request.metadata?.system) { totalChars += JSON.stringify(request.metadata.system).length; } if (request.metadata?.tools) { totalChars += JSON.stringify(request.metadata.tools).length; } return Math.ceil(totalChars / 4); // ~4 chars per token } catch { return 100; // Fallback estimate } } /** * Estimate output tokens */ estimateOutputTokens(content) { try { let totalChars = 0; content.forEach(block => { if (block.text) { totalChars += block.text.length; } else if (block.input) { totalChars += JSON.stringify(block.input).length; } else if (block.content) { totalChars += JSON.stringify(block.content).length; } }); return Math.ceil(totalChars / 4); // ~4 chars per token } catch { return 50; // Fallback estimate } } /** * 计算工具调用数量 */ countToolCalls(data) { if (!data?.content || !Array.isArray(data.content)) { return 0; } return data.content.filter((block) => block.type === 'tool_use').length; } } exports.AnthropicOutputProcessor = AnthropicOutputProcessor; //# sourceMappingURL=processor.js.map