| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.confidenceCalibrator = exports.ConfidenceCalibrator = void 0; |
| const KNOWN_BUG_PATTERNS = [ |
| { |
| pattern: /null.*pointer|NPE|null.*check/i, |
| severity: 'critical', |
| category: 'null-pointer' |
| }, |
| { |
| pattern: /sql.*injection|SELECT.*WHERE.*\+|query.*concat/i, |
| severity: 'critical', |
| category: 'sql-injection' |
| }, |
| { |
| pattern: /password|secret|api.*key.*=.*['"]|token.*=.*['"]/i, |
| severity: 'critical', |
| category: 'secret-exposure' |
| }, |
| { |
| pattern: /eval\(|new.*Function\(|innerHTML\s*=|document\.write/i, |
| severity: 'critical', |
| category: 'xss' |
| }, |
| { |
| pattern: /race.*condition|concurrent.*modif|deadlock|mutex/i, |
| severity: 'major', |
| category: 'concurrency' |
| }, |
| { |
| pattern: /memory.*leak|dispose\(|close\(|gc\./i, |
| severity: 'major', |
| category: 'resource-leak' |
| }, |
| { |
| pattern: /async.*without.*await|unhandled.*promise|catch.*without.*throw/i, |
| severity: 'major', |
| category: 'async-issues' |
| }, |
| { |
| pattern: /try.*without.*catch|catch.*empty|swallow.*exception/i, |
| severity: 'minor', |
| category: 'error-handling' |
| }, |
| { |
| pattern: /any.*type|cast.*any|as.*any/i, |
| severity: 'info', |
| category: 'type-safety' |
| }, |
| { |
| pattern: /console\.(log|debug|warn)/i, |
| severity: 'info', |
| category: 'logging' |
| }, |
| { |
| pattern: /TODO|FIXME|HACK|XXX|BUG:/i, |
| severity: 'info', |
| category: 'tech-debt' |
| }, |
| { |
| pattern: /division.*by.*zero|divide.*0|x\s*\/\s*0/i, |
| severity: 'major', |
| category: 'arithmetic' |
| }, |
| { |
| pattern: /off.*by.*one|index.*length|length.*-\s*1/i, |
| severity: 'minor', |
| category: 'logic-error' |
| }, |
| { |
| pattern: /useEffect.*missing.*dep|dependency.*array/i, |
| severity: 'minor', |
| category: 'react-hooks' |
| }, |
| { |
| pattern: /mutation.*props|props.*change|immutable/i, |
| severity: 'minor', |
| category: 'immutability' |
| }, |
| { |
| pattern: /hardcoded|hard.*coded/i, |
| severity: 'minor', |
| category: 'hardcoding' |
| }, |
| { |
| pattern: /circular.*depend|lazy.*load|circular/i, |
| severity: 'major', |
| category: 'architecture' |
| }, |
| { |
| pattern: /sql.*string|query.*build|string.*concat.*sql/i, |
| severity: 'critical', |
| category: 'sql-injection' |
| }, |
| { |
| pattern: /crypto.*md5|sha1.*encrypt|md5.*hash/i, |
| severity: 'critical', |
| category: 'crypto' |
| }, |
| { |
| pattern: /http.*not.*https|mixed.*content|insecure.*request/i, |
| severity: 'major', |
| category: 'security' |
| }, |
| { |
| pattern: /cookie.*without.*secure|secure.*flag.*false/i, |
| severity: 'major', |
| category: 'security' |
| }, |
| { |
| pattern: /sudo|chmod\s+777|root.*login/i, |
| severity: 'critical', |
| category: 'security' |
| }, |
| { |
| pattern: /catch.*(e|err).*{[\s]*}/i, |
| severity: 'minor', |
| category: 'error-handling' |
| }, |
| { |
| pattern: /setTimeout.*0|setImmediate/i, |
| severity: 'info', |
| category: 'timing' |
| }, |
| { |
| pattern: /double.*click|race.*click|multiple.*submit/i, |
| severity: 'minor', |
| category: 'ux' |
| } |
| ]; |
| const POSITIVE_INDICATORS = [ |
| { indicator: 'suggestion', weight: 0.15 }, |
| { indicator: '```suggestion', weight: 0.2 }, |
| { indicator: 'fix:', weight: 0.1 }, |
| { indicator: 'fixed code', weight: 0.1 }, |
| { indicator: 'correct', weight: 0.05 }, |
| { indicator: 'instead of', weight: 0.05 }, |
| { indicator: 'should be', weight: 0.05 }, |
| { indicator: 'best practice', weight: 0.05 } |
| ]; |
| const NEGATIVE_INDICATORS = [ |
| { indicator: 'maybe', weight: -0.15 }, |
| { indicator: 'might', weight: -0.1 }, |
| { indicator: 'possibly', weight: -0.1 }, |
| { indicator: 'i think', weight: -0.2 }, |
| { indicator: 'unclear', weight: -0.1 }, |
| { indicator: '不确定', weight: -0.15 }, |
| { indicator: 'perhaps', weight: -0.1 }, |
| { indicator: 'could be', weight: -0.1 }, |
| { indicator: 'not sure', weight: -0.15 }, |
| { indicator: 'likely', weight: -0.05 } |
| ]; |
| class ConfidenceCalibrator { |
| historicalAccuracies = new Map(); |
| feedbackHistory = []; |
| persistPath = '/tmp/prix_feedback.json'; |
| feedbackTracker = null; |
| constructor(feedbackTracker = null) { |
| this.loadPersisted(); |
| if (feedbackTracker) { |
| this.feedbackTracker = feedbackTracker; |
| } |
| else { |
| try { |
| const { FeedbackTracker } = require('./feedback-tracker'); |
| this.feedbackTracker = new FeedbackTracker(); |
| } |
| catch (e) { |
| |
| } |
| } |
| } |
| loadPersisted() { |
| try { |
| if (require('fs').existsSync(this.persistPath)) { |
| const data = JSON.parse(require('fs').readFileSync(this.persistPath, 'utf8')); |
| if (Array.isArray(data)) { |
| this.feedbackHistory = data.map((item) => ({ |
| pattern: item.pattern || '', |
| helpful: item.helpful || false, |
| timestamp: item.timestamp || Date.now() |
| })); |
| this.rebuildHistoricalAccuracies(); |
| } |
| } |
| } |
| catch (e) { |
| |
| } |
| } |
| persist() { |
| try { |
| require('fs').writeFileSync(this.persistPath, JSON.stringify(this.feedbackHistory.slice(-1000))); |
| } |
| catch (e) { |
| |
| } |
| } |
| rebuildHistoricalAccuracies() { |
| const patternMap = new Map(); |
| for (const entry of this.feedbackHistory) { |
| const accuracies = patternMap.get(entry.pattern) || []; |
| accuracies.push(entry.helpful ? 1 : 0); |
| patternMap.set(entry.pattern, accuracies); |
| } |
| for (const [pattern, accuracies] of patternMap) { |
| this.historicalAccuracies.set(pattern, accuracies); |
| } |
| } |
| recordFeedback(pattern, helpful) { |
| this.feedbackHistory.push({ pattern, helpful, timestamp: Date.now() }); |
| const accuracies = this.historicalAccuracies.get(pattern) || []; |
| accuracies.push(helpful ? 1 : 0); |
| this.historicalAccuracies.set(pattern, accuracies); |
| this.persist(); |
| } |
| calibrate(aiReportedConfidence, comment, contextTokens, maxContextTokens, severity, teamId, repoSlug, patternType, filename) { |
| const factors = this.calculateFactors(comment, contextTokens, maxContextTokens, severity, teamId, repoSlug, patternType, filename); |
| let score; |
| if (aiReportedConfidence !== undefined) { |
| const deBiased = this.removeLLMBias(aiReportedConfidence); |
| score = this.combineWithFactors(deBiased, factors); |
| } |
| else { |
| score = this.computeFromFactorsOnly(factors); |
| } |
| score = Math.max(0, Math.min(100, score)); |
| return { |
| score: Math.round(score), |
| factors, |
| reasoning: this.generateReasoning(factors, score) |
| }; |
| } |
| calculateFactors(comment, contextTokens, maxContextTokens, severity, teamId, repoSlug, patternType, filename) { |
| const patternMatchScore = this.calculatePatternMatch(comment); |
| const contextRichness = Math.min(1, contextTokens / Math.max(1, maxContextTokens * 0.5)); |
| const historicalAccuracy = this.calculateHistoricalAccuracy(comment); |
| const severityMatchesKnownBug = severity |
| ? this.matchesKnownBugPattern(comment, severity) |
| : this.matchesAnyKnownBugPattern(comment); |
| const hasExplicitEvidence = this.hasExplicitEvidence(comment); |
| const { teamAcceptanceRate, patternTypeAccuracy } = this.calculateTeamMetrics(teamId, repoSlug, patternType, filename); |
| return { |
| patternMatchScore, |
| contextRichness, |
| historicalAccuracy, |
| severityMatchesKnownBug, |
| hasExplicitEvidence, |
| teamAcceptanceRate, |
| patternTypeAccuracy |
| }; |
| } |
| calculateTeamMetrics(teamId, repoSlug, patternType, filename) { |
| if (!this.feedbackTracker || !teamId || !repoSlug) { |
| return { teamAcceptanceRate: 0.5, patternTypeAccuracy: 0.5 }; |
| } |
| let teamAcceptanceRate = 0.5; |
| let patternTypeAccuracy = 0.5; |
| if (patternType && filename) { |
| teamAcceptanceRate = this.feedbackTracker.getPatternAcceptanceRate(patternType, teamId, repoSlug); |
| patternTypeAccuracy = this.feedbackTracker.getFilenameAcceptanceRate(filename, teamId, repoSlug); |
| } |
| return { teamAcceptanceRate, patternTypeAccuracy }; |
| } |
| calculatePatternMatch(comment) { |
| const lowerComment = comment.toLowerCase(); |
| let score = 0.5; |
| for (const { indicator, weight } of POSITIVE_INDICATORS) { |
| if (lowerComment.includes(indicator.toLowerCase())) { |
| score += weight; |
| } |
| } |
| for (const { indicator, weight } of NEGATIVE_INDICATORS) { |
| if (lowerComment.includes(indicator.toLowerCase())) { |
| score += weight; |
| } |
| } |
| if (lowerComment.includes('why this matters')) { |
| score += 0.1; |
| } |
| if (lowerComment.includes('production') || |
| lowerComment.includes('production environment')) { |
| score += 0.05; |
| } |
| return Math.max(0, Math.min(1, score)); |
| } |
| calculateHistoricalAccuracy(comment) { |
| const lowerComment = comment.toLowerCase(); |
| for (const [pattern, accuracies] of this.historicalAccuracies) { |
| if (lowerComment.includes(pattern.toLowerCase())) { |
| const avg = accuracies.reduce((a, b) => a + b, 0) / accuracies.length; |
| return avg; |
| } |
| } |
| const relevantFeedback = this.feedbackHistory.filter(f => f.pattern |
| .split(' ') |
| .some(word => word.length > 4 && lowerComment.includes(word.toLowerCase()))); |
| if (relevantFeedback.length === 0) { |
| return 0.5; |
| } |
| const accuracy = relevantFeedback.filter(f => f.helpful).length / relevantFeedback.length; |
| return accuracy; |
| } |
| matchesKnownBugPattern(comment, severity) { |
| const lowerComment = comment.toLowerCase(); |
| for (const { pattern, severity: patternSeverity } of KNOWN_BUG_PATTERNS) { |
| if (pattern.test(lowerComment) && patternSeverity === severity) { |
| return true; |
| } |
| } |
| return false; |
| } |
| matchesAnyKnownBugPattern(comment) { |
| const lowerComment = comment.toLowerCase(); |
| for (const { pattern } of KNOWN_BUG_PATTERNS) { |
| if (pattern.test(lowerComment)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| hasExplicitEvidence(comment) { |
| const explicitPhrases = [ |
| 'this will crash', |
| 'this causes', |
| 'this leads to', |
| 'results in', |
| 'fails when', |
| 'breaks when', |
| 'error when', |
| 'crash if', |
| 'exception if', |
| 'undefined behavior', |
| 'undefined is', |
| 'will throw', |
| 'will fail' |
| ]; |
| const lowerComment = comment.toLowerCase(); |
| return explicitPhrases.some(phrase => lowerComment.includes(phrase)); |
| } |
| removeLLMBias(reportedConfidence) { |
| if (reportedConfidence >= 95) { |
| return 75; |
| } |
| if (reportedConfidence >= 85) { |
| return 70; |
| } |
| if (reportedConfidence >= 70) { |
| return 62; |
| } |
| if (reportedConfidence >= 60) { |
| return 55; |
| } |
| return reportedConfidence * 0.9; |
| } |
| combineWithFactors(baseScore, factors) { |
| const weights = { |
| base: 0.25, |
| pattern: 0.2, |
| context: 0.1, |
| historical: 0.15, |
| knownBug: 0.05, |
| teamAcceptance: 0.15, |
| patternAccuracy: 0.1 |
| }; |
| let score = baseScore * weights.base; |
| score += factors.patternMatchScore * 100 * weights.pattern; |
| score += factors.contextRichness * 100 * weights.context; |
| score += factors.historicalAccuracy * 100 * weights.historical; |
| if (factors.severityMatchesKnownBug) { |
| score += 10 * weights.knownBug; |
| } |
| if (factors.hasExplicitEvidence) { |
| score += 5; |
| } |
| score += factors.teamAcceptanceRate * 100 * weights.teamAcceptance; |
| score += factors.patternTypeAccuracy * 100 * weights.patternAccuracy; |
| return score; |
| } |
| computeFromFactorsOnly(factors) { |
| const weights = { |
| pattern: 0.3, |
| context: 0.15, |
| historical: 0.2, |
| knownBug: 0.15, |
| teamAcceptance: 0.12, |
| patternAccuracy: 0.08 |
| }; |
| let score = 50; |
| score += factors.patternMatchScore * 50 * weights.pattern; |
| score += factors.contextRichness * 50 * weights.context; |
| score += factors.historicalAccuracy * 50 * weights.historical; |
| if (factors.severityMatchesKnownBug) { |
| score += 10 * weights.knownBug; |
| } |
| if (factors.hasExplicitEvidence) { |
| score += 5; |
| } |
| score += factors.teamAcceptanceRate * 50 * weights.teamAcceptance; |
| score += factors.patternTypeAccuracy * 50 * weights.patternAccuracy; |
| return score; |
| } |
| generateReasoning(factors, score) { |
| const reasons = []; |
| if (factors.patternMatchScore > 0.7) { |
| reasons.push('strong pattern match'); |
| } |
| else if (factors.patternMatchScore < 0.4) { |
| reasons.push('weak pattern match'); |
| } |
| if (factors.contextRichness > 0.8) { |
| reasons.push('rich context'); |
| } |
| else if (factors.contextRichness < 0.3) { |
| reasons.push('limited context'); |
| } |
| if (factors.historicalAccuracy > 0.7) { |
| reasons.push('historically accurate'); |
| } |
| else if (factors.historicalAccuracy < 0.4 && |
| factors.historicalAccuracy !== 0.5) { |
| reasons.push('historically unreliable'); |
| } |
| if (factors.severityMatchesKnownBug) { |
| reasons.push('matches known bug pattern'); |
| } |
| if (factors.hasExplicitEvidence) { |
| reasons.push('has explicit evidence'); |
| } |
| if (factors.teamAcceptanceRate > 0.6) { |
| reasons.push('high team acceptance'); |
| } |
| else if (factors.teamAcceptanceRate < 0.3 && |
| factors.teamAcceptanceRate !== 0.5) { |
| reasons.push('low team acceptance'); |
| } |
| if (factors.patternTypeAccuracy > 0.6) { |
| reasons.push('matches accepted patterns'); |
| } |
| return reasons.length > 0 |
| ? `Calibrated based on: ${reasons.join(', ')}` |
| : 'Calibrated using default heuristics'; |
| } |
| } |
| exports.ConfidenceCalibrator = ConfidenceCalibrator; |
| exports.confidenceCalibrator = new ConfidenceCalibrator(); |
| |