PRIX / lib /src /confidence.js
Rachit-Tw's picture
Upload 94 files
440cddf verified
Raw
History Blame Contribute Delete
15.6 kB
"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