route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
421 lines • 17.4 kB
JavaScript
"use strict";
/**
* Provider Comparison Analysis Engine
* 对比分析 CodeWhisperer 与 OpenAI 响应差异
* Project owner: Jason Zhang
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComparisonAnalysisEngine = void 0;
const logger_1 = require("@/utils/logger");
class ComparisonAnalysisEngine {
config;
analysisHistory = [];
maxHistorySize = 1000;
constructor(config) {
this.config = config;
}
/**
* 对比分析两个 provider 的响应
*/
async analyzeProviderResponses(request, codewhispererResponse, openaiResponse) {
const requestId = request.metadata?.requestId || `analysis_${Date.now()}`;
logger_1.logger.info(`Starting provider comparison analysis`, {
requestId,
model: request.model,
codewhispererTokens: codewhispererResponse.metadata.tokenCount.output,
openaiTokens: openaiResponse.metadata.tokenCount.output
});
try {
// 执行各维度分析
const contentAnalysis = await this.analyzeContentDifferences(codewhispererResponse.response, openaiResponse.response);
const qualityAnalysis = await this.analyzeQualityDifferences(codewhispererResponse, openaiResponse);
const performanceAnalysis = await this.analyzePerformanceDifferences(codewhispererResponse, openaiResponse);
// 识别具体差异
const differences = await this.identifyDifferences(codewhispererResponse.response, openaiResponse.response);
// 计算综合质量分数
const qualityScore = await this.calculateQualityScore(contentAnalysis, qualityAnalysis, performanceAnalysis, differences);
// 生成修正建议
const recommendations = await this.generateRecommendations(differences, qualityScore);
const result = {
requestId,
timestamp: new Date(),
request,
responses: {
codewhisperer: codewhispererResponse,
openai: openaiResponse
},
analysis: {
contentComparison: contentAnalysis,
qualityComparison: qualityAnalysis,
performanceComparison: performanceAnalysis
},
differences,
qualityScore,
recommendations
};
// 存储到历史记录
this.addToHistory(result);
logger_1.logger.info(`Comparison analysis completed`, {
requestId,
overallScore: qualityScore.overall,
differenceCount: differences.length,
recommendation: qualityScore.recommendation
});
return result;
}
catch (error) {
logger_1.logger.error(`Comparison analysis failed`, error, requestId);
throw error;
}
}
/**
* 分析内容差异
*/
async analyzeContentDifferences(cwResponse, oaiResponse) {
const cwContent = this.extractTextContent(cwResponse);
const oaiContent = this.extractTextContent(oaiResponse);
// 计算内容相似度(简化算法)
const similarity = this.calculateContentSimilarity(cwContent, oaiContent);
// 计算长度差异
const lengthDifference = Math.abs(cwContent.length - oaiContent.length) /
Math.max(cwContent.length, oaiContent.length);
// 计算结构差异
const structuralDifference = this.calculateStructuralDifference(cwResponse, oaiResponse);
return {
similarity,
lengthDifference,
structuralDifference
};
}
/**
* 分析质量差异
*/
async analyzeQualityDifferences(cwResponse, oaiResponse) {
return {
completeness: {
codewhisperer: this.assessCompleteness(cwResponse.response),
openai: this.assessCompleteness(oaiResponse.response)
},
accuracy: {
codewhisperer: this.assessAccuracy(cwResponse.response),
openai: this.assessAccuracy(oaiResponse.response)
},
coherence: {
codewhisperer: this.assessCoherence(cwResponse.response),
openai: this.assessCoherence(oaiResponse.response)
}
};
}
/**
* 分析性能差异
*/
async analyzePerformanceDifferences(cwResponse, oaiResponse) {
const cwTokens = cwResponse.metadata.tokenCount.output;
const oaiTokens = oaiResponse.metadata.tokenCount.output;
const cwContent = this.extractTextContent(cwResponse.response);
const oaiContent = this.extractTextContent(oaiResponse.response);
return {
responseTime: {
codewhisperer: cwResponse.metadata.responseTime,
openai: oaiResponse.metadata.responseTime,
difference: cwResponse.metadata.responseTime - oaiResponse.metadata.responseTime
},
tokenEfficiency: {
codewhisperer: cwTokens / Math.max(cwContent.length, 1),
openai: oaiTokens / Math.max(oaiContent.length, 1)
}
};
}
/**
* 识别具体差异
*/
async identifyDifferences(cwResponse, oaiResponse) {
const differences = [];
// 检查内容差异
const cwContent = this.extractTextContent(cwResponse);
const oaiContent = this.extractTextContent(oaiResponse);
if (cwContent !== oaiContent) {
differences.push({
type: 'content',
severity: this.assessContentDifferenceSeverity(cwContent, oaiContent),
description: 'Response content differs between providers',
codewhispererValue: cwContent.substring(0, 200) + '...',
openaiValue: oaiContent.substring(0, 200) + '...',
impact: 'User experience may vary depending on provider',
fixable: true
});
}
// 检查结构差异
const cwStructure = this.analyzeResponseStructure(cwResponse);
const oaiStructure = this.analyzeResponseStructure(oaiResponse);
if (JSON.stringify(cwStructure) !== JSON.stringify(oaiStructure)) {
differences.push({
type: 'structure',
severity: 'major',
description: 'Response structure differs between providers',
codewhispererValue: cwStructure,
openaiValue: oaiStructure,
impact: 'Response parsing may behave differently',
fixable: true
});
}
// 检查工具调用差异
const cwTools = this.extractToolCalls(cwResponse);
const oaiTools = this.extractToolCalls(oaiResponse);
if (JSON.stringify(cwTools) !== JSON.stringify(oaiTools)) {
differences.push({
type: 'tools',
severity: 'critical',
description: 'Tool calls differ between providers',
codewhispererValue: cwTools,
openaiValue: oaiTools,
impact: 'Tool execution results will differ',
fixable: true
});
}
// 检查元数据差异
const cwUsage = cwResponse.usage;
const oaiUsage = oaiResponse.usage;
if (cwUsage && oaiUsage &&
(cwUsage.input_tokens !== oaiUsage.input_tokens ||
cwUsage.output_tokens !== oaiUsage.output_tokens)) {
differences.push({
type: 'metadata',
severity: 'minor',
description: 'Token usage differs between providers',
codewhispererValue: cwUsage,
openaiValue: oaiUsage,
impact: 'Cost calculation may differ',
fixable: false
});
}
return differences;
}
/**
* 计算综合质量分数
*/
async calculateQualityScore(contentAnalysis, qualityAnalysis, performanceAnalysis, differences) {
const completeness = (qualityAnalysis.completeness.codewhisperer +
qualityAnalysis.completeness.openai) / 2 * 100;
const accuracy = (qualityAnalysis.accuracy.codewhisperer +
qualityAnalysis.accuracy.openai) / 2 * 100;
const consistency = (1 - contentAnalysis.structuralDifference) * 100;
const performance = this.calculatePerformanceScore(performanceAnalysis);
const overall = (completeness + accuracy + consistency + performance) / 4;
const criticalDifferences = differences.filter(d => d.severity === 'critical').length;
const majorDifferences = differences.filter(d => d.severity === 'major').length;
let recommendation;
if (criticalDifferences > 0 || majorDifferences > 2) {
recommendation = 'needs_correction';
}
else if (qualityAnalysis.completeness.openai > qualityAnalysis.completeness.codewhisperer + 0.1) {
recommendation = 'use_openai';
}
else if (qualityAnalysis.completeness.codewhisperer > qualityAnalysis.completeness.openai + 0.1) {
recommendation = 'use_codewhisperer';
}
else {
recommendation = 'equivalent';
}
return {
overall,
dimensions: {
completeness,
accuracy,
consistency,
performance
},
recommendation
};
}
/**
* 生成修正建议
*/
async generateRecommendations(differences, qualityScore) {
const recommendations = [];
for (const diff of differences) {
if (!diff.fixable)
continue;
switch (diff.type) {
case 'content':
if (diff.severity === 'critical' || diff.severity === 'major') {
recommendations.push({
type: 'content',
priority: diff.severity === 'critical' ? 'high' : 'medium',
action: 'Apply content correction',
implementation: 'Use OpenAI response as reference to correct CodeWhisperer content',
expectedImpact: 'Improved response quality and consistency'
});
}
break;
case 'structure':
recommendations.push({
type: 'structure',
priority: 'high',
action: 'Normalize response structure',
implementation: 'Transform CodeWhisperer response to match OpenAI structure',
expectedImpact: 'Consistent response parsing and handling'
});
break;
case 'tools':
recommendations.push({
type: 'tools',
priority: 'high',
action: 'Fix tool call formatting',
implementation: 'Convert CodeWhisperer tool calls to match OpenAI format',
expectedImpact: 'Proper tool execution and result handling'
});
break;
}
}
return recommendations;
}
// 辅助方法
extractTextContent(response) {
if (!response.content || !Array.isArray(response.content)) {
return '';
}
return response.content
.filter(item => item.type === 'text')
.map(item => item.text || '')
.join(' ');
}
calculateContentSimilarity(content1, content2) {
// 简化的相似度计算(实际项目中可以使用更复杂的算法)
const words1 = content1.toLowerCase().split(/\s+/);
const words2 = content2.toLowerCase().split(/\s+/);
const commonWords = words1.filter(word => words2.includes(word));
const totalWords = [...new Set([...words1, ...words2])];
return commonWords.length / totalWords.length;
}
calculateStructuralDifference(response1, response2) {
const structure1 = this.analyzeResponseStructure(response1);
const structure2 = this.analyzeResponseStructure(response2);
// 简化的结构差异计算
const keys1 = Object.keys(structure1);
const keys2 = Object.keys(structure2);
const allKeys = [...new Set([...keys1, ...keys2])];
const commonKeys = keys1.filter(key => keys2.includes(key));
return 1 - (commonKeys.length / allKeys.length);
}
analyzeResponseStructure(response) {
return {
hasId: !!response.id,
hasType: !!response.type,
hasModel: !!response.model,
hasRole: !!response.role,
contentLength: response.content?.length || 0,
contentTypes: response.content?.map(item => item.type) || [],
hasUsage: !!response.usage,
hasStopReason: !!response.stop_reason
};
}
extractToolCalls(response) {
if (!response.content || !Array.isArray(response.content)) {
return [];
}
return response.content.filter(item => item.type === 'tool_use');
}
assessCompleteness(response) {
let score = 0.5; // Base score
if (response.content && response.content.length > 0)
score += 0.3;
if (response.usage)
score += 0.1;
if (response.stop_reason)
score += 0.1;
return Math.min(score, 1.0);
}
assessAccuracy(response) {
// 简化的准确性评估 - 实际项目中需要更复杂的逻辑
let score = 0.7; // Base score
const content = this.extractTextContent(response);
if (content.length > 50)
score += 0.1;
if (content.length > 200)
score += 0.1;
if (response.usage && response.usage.output_tokens > 0)
score += 0.1;
return Math.min(score, 1.0);
}
assessCoherence(response) {
// 简化的连贯性评估
const content = this.extractTextContent(response);
let score = 0.6; // Base score
// 检查内容是否有明显的结构
if (content.includes('\n') || content.includes('.'))
score += 0.2;
if (content.length > 100)
score += 0.1;
if (response.content && response.content.length > 1)
score += 0.1;
return Math.min(score, 1.0);
}
assessContentDifferenceSeverity(content1, content2) {
const similarity = this.calculateContentSimilarity(content1, content2);
if (similarity < 0.3)
return 'critical';
if (similarity < 0.6)
return 'major';
return 'minor';
}
calculatePerformanceScore(analysis) {
// 基于响应时间和token效率的性能分数
const timeScore = analysis.responseTime.codewhisperer < analysis.responseTime.openai ? 60 : 40;
const efficiencyScore = analysis.tokenEfficiency.codewhisperer < analysis.tokenEfficiency.openai ? 60 : 40;
return (timeScore + efficiencyScore) / 2;
}
addToHistory(result) {
this.analysisHistory.push(result);
// 限制历史记录大小
if (this.analysisHistory.length > this.maxHistorySize) {
this.analysisHistory = this.analysisHistory.slice(-this.maxHistorySize);
}
}
/**
* 获取分析历史
*/
getAnalysisHistory(limit) {
if (limit) {
return this.analysisHistory.slice(-limit);
}
return [...this.analysisHistory];
}
/**
* 获取质量统计
*/
getQualityStatistics() {
if (this.analysisHistory.length === 0) {
return {
averageScore: 0,
totalComparisons: 0,
recommendationDistribution: {},
commonIssues: []
};
}
const scores = this.analysisHistory.map(r => r.qualityScore.overall);
const averageScore = scores.reduce((sum, score) => sum + score, 0) / scores.length;
const recommendations = this.analysisHistory.map(r => r.qualityScore.recommendation);
const recommendationDistribution = recommendations.reduce((acc, rec) => {
acc[rec] = (acc[rec] || 0) + 1;
return acc;
}, {});
const allDifferences = this.analysisHistory.flatMap(r => r.differences);
const issueFrequency = allDifferences.reduce((acc, diff) => {
acc[diff.description] = (acc[diff.description] || 0) + 1;
return acc;
}, {});
const commonIssues = Object.entries(issueFrequency)
.sort(([, a], [, b]) => b - a)
.slice(0, 5)
.map(([issue]) => issue);
return {
averageScore,
totalComparisons: this.analysisHistory.length,
recommendationDistribution,
commonIssues
};
}
}
exports.ComparisonAnalysisEngine = ComparisonAnalysisEngine;
//# sourceMappingURL=analysis-engine.js.map