"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) { // FeedbackTracker not available, continue without it } } } 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) { // Start fresh if persistence fails } } persist() { try { require('fs').writeFileSync(this.persistPath, JSON.stringify(this.feedbackHistory.slice(-1000))); } catch (e) { // Persistence failure is non-fatal } } 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(); //# sourceMappingURL=confidence.js.map