phase 4
This commit is contained in:
parent
b192f257a1
commit
78b30fb7c6
93
src/pages/api/ai/bias-statistics.ts
Normal file
93
src/pages/api/ai/bias-statistics.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// src/pages/api/ai/bias-statistics.ts - New API endpoint for bias statistics
|
||||||
|
|
||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { withAPIAuth } from '../../../utils/auth.js';
|
||||||
|
import { apiResponse, apiServerError, createAuthErrorResponse } from '../../../utils/api.js';
|
||||||
|
import { auditTrailService } from '../../../utils/auditTrail.js';
|
||||||
|
import { biasDetector } from '../../../utils/biasDetection.js';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
export const GET: APIRoute = async ({ request }) => {
|
||||||
|
try {
|
||||||
|
const authResult = await withAPIAuth(request, 'ai');
|
||||||
|
if (!authResult.authenticated) {
|
||||||
|
return createAuthErrorResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get comprehensive bias statistics
|
||||||
|
const biasStats = auditTrailService.getBiasStatistics();
|
||||||
|
const baselineStats = biasDetector.getBaselineStats();
|
||||||
|
const biasThresholds = biasDetector.getBiasThresholds();
|
||||||
|
|
||||||
|
return apiResponse.success({
|
||||||
|
biasStatistics: {
|
||||||
|
overall: biasStats,
|
||||||
|
baseline: baselineStats,
|
||||||
|
configuration: {
|
||||||
|
thresholds: biasThresholds,
|
||||||
|
detectionEnabled: true,
|
||||||
|
lastUpdated: new Date().toISOString()
|
||||||
|
},
|
||||||
|
interpretation: {
|
||||||
|
biasRiskLevel: biasStats.averageBiasInstances > 1.5 ? 'high' :
|
||||||
|
biasStats.averageBiasInstances > 0.8 ? 'medium' : 'low',
|
||||||
|
mostCommonBias: Object.entries(biasStats.biasTypeFrequency || {})
|
||||||
|
.sort(([,a], [,b]) => (b as number) - (a as number))[0]?.[0] || 'none',
|
||||||
|
recommendations: generateBiasRecommendations(biasStats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[BIAS STATS API] Error:', error);
|
||||||
|
return apiServerError.internal('Failed to retrieve bias statistics');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function generateBiasRecommendations(stats: any): string[] {
|
||||||
|
const recommendations: string[] = [];
|
||||||
|
|
||||||
|
if (!stats.biasTypeFrequency) {
|
||||||
|
return ['Insufficient data for bias analysis recommendations'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const biasEntries = Object.entries(stats.biasTypeFrequency) as [string, number][];
|
||||||
|
const totalAudits = stats.totalAudits || 1;
|
||||||
|
|
||||||
|
// Check for high-frequency bias types
|
||||||
|
biasEntries.forEach(([biasType, count]) => {
|
||||||
|
const frequency = count / totalAudits;
|
||||||
|
|
||||||
|
if (frequency > 0.3) { // More than 30% of audits show this bias
|
||||||
|
switch (biasType) {
|
||||||
|
case 'popularity':
|
||||||
|
recommendations.push('HIGH: Popularity bias detected frequently. Review selection criteria to prioritize scenario-specific over well-known tools.');
|
||||||
|
break;
|
||||||
|
case 'availability':
|
||||||
|
recommendations.push('MEDIUM: Availability bias detected. Encourage more thorough candidate evaluation beyond easily recalled options.');
|
||||||
|
break;
|
||||||
|
case 'domain_concentration':
|
||||||
|
recommendations.push('MEDIUM: Domain concentration bias detected. Promote cross-domain tool selection for comprehensive analysis.');
|
||||||
|
break;
|
||||||
|
case 'skill_level':
|
||||||
|
recommendations.push('LOW: Skill level misalignment detected. Improve query classification for better skill-appropriate selections.');
|
||||||
|
break;
|
||||||
|
case 'recency':
|
||||||
|
recommendations.push('LOW: Recency bias detected. Balance newer tools with proven established alternatives.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (recommendations.length === 0) {
|
||||||
|
recommendations.push('No significant bias patterns detected. Current selection methodology appears balanced.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add general recommendation if multiple bias types detected
|
||||||
|
if (biasEntries.length > 3) {
|
||||||
|
recommendations.push('GENERAL: Multiple bias types detected. Consider implementing additional bias mitigation strategies.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return recommendations;
|
||||||
|
}
|
@ -7,6 +7,7 @@ import { enqueueApiCall } from '../../../utils/rateLimitedQueue.js';
|
|||||||
import { aiPipeline } from '../../../utils/aiPipeline.js';
|
import { aiPipeline } from '../../../utils/aiPipeline.js';
|
||||||
import { forensicConfig } from '../../../utils/forensicConfig.js';
|
import { forensicConfig } from '../../../utils/forensicConfig.js';
|
||||||
import { confidenceScorer } from '../../../utils/confidenceScoring.js';
|
import { confidenceScorer } from '../../../utils/confidenceScoring.js';
|
||||||
|
import { biasDetector } from '../../../utils/biasDetection.js';
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
@ -212,18 +213,17 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
query: sanitizedQuery,
|
query: sanitizedQuery,
|
||||||
processingStats: {
|
processingStats: {
|
||||||
...result.processingStats,
|
...result.processingStats,
|
||||||
pipelineType: 'enhanced-micro-task-with-confidence',
|
pipelineType: 'enhanced-micro-task-with-bias-detection',
|
||||||
microTasksSuccessRate: stats.microTasksCompleted / Math.max(stats.microTasksCompleted + stats.microTasksFailed, 1),
|
microTasksSuccessRate: stats.microTasksCompleted / Math.max(stats.microTasksCompleted + stats.microTasksFailed, 1),
|
||||||
averageTaskTime: stats.processingTimeMs / Math.max(stats.microTasksCompleted + stats.microTasksFailed, 1),
|
averageTaskTime: stats.processingTimeMs / Math.max(stats.microTasksCompleted + stats.microTasksFailed, 1),
|
||||||
estimatedAICallsMade,
|
estimatedAICallsMade,
|
||||||
auditCompliant: result.auditTrail?.compliance.auditCompliant || false,
|
auditCompliant: result.auditTrail?.compliance.auditCompliant || false,
|
||||||
biasChecked: result.auditTrail?.compliance.biasChecked || false,
|
biasChecked: result.auditTrail?.compliance.biasChecked || false,
|
||||||
confidenceAssessed: result.auditTrail?.compliance.confidenceAssessed || false,
|
confidenceAssessed: result.auditTrail?.compliance.confidenceAssessed || false,
|
||||||
// NEW: Confidence acceptance flag
|
|
||||||
confidenceAcceptable
|
confidenceAcceptable
|
||||||
},
|
},
|
||||||
|
|
||||||
// ENHANCED: Comprehensive forensic metadata with confidence details
|
// ENHANCED: Comprehensive forensic metadata with bias detection
|
||||||
forensicMetadata: result.auditTrail ? {
|
forensicMetadata: result.auditTrail ? {
|
||||||
auditTrailId: result.auditTrail.auditId,
|
auditTrailId: result.auditTrail.auditId,
|
||||||
auditEnabled: config.auditTrail.enabled,
|
auditEnabled: config.auditTrail.enabled,
|
||||||
@ -236,7 +236,30 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
evidenceQuality: result.auditTrail.qualityMetrics.evidenceQuality,
|
evidenceQuality: result.auditTrail.qualityMetrics.evidenceQuality,
|
||||||
methodologicalSoundness: result.auditTrail.qualityMetrics.methodologicalSoundness,
|
methodologicalSoundness: result.auditTrail.qualityMetrics.methodologicalSoundness,
|
||||||
|
|
||||||
// NEW: Detailed confidence breakdown
|
// ENHANCED: Detailed bias analysis
|
||||||
|
biasAnalysis: {
|
||||||
|
overallBiasRisk: result.auditTrail.qualityMetrics.biasRiskScore,
|
||||||
|
isHighBiasRisk: biasDetector.isHighBiasRisk(result.auditTrail.qualityMetrics.biasRiskScore),
|
||||||
|
detectedBiases: result.auditTrail.biasAnalysis.filter(bias => bias.detected).map(bias => ({
|
||||||
|
type: bias.biasType,
|
||||||
|
severity: bias.severity,
|
||||||
|
confidence: bias.confidence,
|
||||||
|
description: bias.description,
|
||||||
|
affectedTools: bias.evidence.affectedTools,
|
||||||
|
recommendation: bias.recommendation
|
||||||
|
})),
|
||||||
|
biasFreeSeverity: result.auditTrail.biasAnalysis.filter(bias => !bias.detected).length,
|
||||||
|
mitigationSuggestions: biasDetector.suggestBiasMitigation(result.auditTrail.biasAnalysis),
|
||||||
|
biasTypes: {
|
||||||
|
popularity: result.auditTrail.biasAnalysis.find(b => b.biasType === 'popularity'),
|
||||||
|
availability: result.auditTrail.biasAnalysis.find(b => b.biasType === 'availability'),
|
||||||
|
domainConcentration: result.auditTrail.biasAnalysis.find(b => b.biasType === 'domain_concentration'),
|
||||||
|
skillLevel: result.auditTrail.biasAnalysis.find(b => b.biasType === 'skill_level'),
|
||||||
|
recency: result.auditTrail.biasAnalysis.find(b => b.biasType === 'recency')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Detailed confidence breakdown
|
||||||
confidenceBreakdown: config.features.confidenceScoring ? {
|
confidenceBreakdown: config.features.confidenceScoring ? {
|
||||||
retrieval: result.auditTrail.qualityMetrics.confidenceBreakdown.retrieval,
|
retrieval: result.auditTrail.qualityMetrics.confidenceBreakdown.retrieval,
|
||||||
selection: result.auditTrail.qualityMetrics.confidenceBreakdown.selection,
|
selection: result.auditTrail.qualityMetrics.confidenceBreakdown.selection,
|
||||||
@ -244,7 +267,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
meta: result.auditTrail.qualityMetrics.confidenceBreakdown.meta
|
meta: result.auditTrail.qualityMetrics.confidenceBreakdown.meta
|
||||||
} : undefined,
|
} : undefined,
|
||||||
|
|
||||||
// NEW: Confidence assessment details
|
// Confidence assessment details
|
||||||
confidenceAssessment: config.features.confidenceScoring ? {
|
confidenceAssessment: config.features.confidenceScoring ? {
|
||||||
qualityLevel: result.auditTrail.qualityMetrics.qualityLevel,
|
qualityLevel: result.auditTrail.qualityMetrics.qualityLevel,
|
||||||
reliability: result.auditTrail.qualityMetrics.confidenceReliability,
|
reliability: result.auditTrail.qualityMetrics.confidenceReliability,
|
||||||
@ -254,9 +277,27 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
threshold: thresholds.confidenceThreshold
|
threshold: thresholds.confidenceThreshold
|
||||||
} : undefined,
|
} : undefined,
|
||||||
|
|
||||||
// Bias and quality warnings
|
// ENHANCED: Bias and quality warnings with specific guidance
|
||||||
biasWarnings: result.auditTrail.biasAnalysis.filter(b => b.detected),
|
biasWarnings: result.auditTrail.biasAnalysis.filter(b => b.detected).map(bias => ({
|
||||||
qualityWarnings: !confidenceAcceptable ? ['Confidence below acceptable threshold'] : [],
|
type: bias.biasType,
|
||||||
|
severity: bias.severity,
|
||||||
|
message: bias.description,
|
||||||
|
actionRequired: bias.severity > 0.7 ? 'immediate_review' : 'consideration',
|
||||||
|
mitigation: bias.mitigation
|
||||||
|
})),
|
||||||
|
|
||||||
|
qualityWarnings: [
|
||||||
|
...((!confidenceAcceptable) ? [{
|
||||||
|
type: 'low_confidence',
|
||||||
|
message: 'Overall confidence below acceptable threshold',
|
||||||
|
actionRequired: 'expert_review'
|
||||||
|
}] : []),
|
||||||
|
...(result.auditTrail.qualityMetrics.biasRiskScore > thresholds.biasAlertThreshold ? [{
|
||||||
|
type: 'high_bias_risk',
|
||||||
|
message: 'High bias risk detected in tool selection',
|
||||||
|
actionRequired: 'bias_review'
|
||||||
|
}] : [])
|
||||||
|
],
|
||||||
|
|
||||||
// System configuration snapshot
|
// System configuration snapshot
|
||||||
systemConfig: {
|
systemConfig: {
|
||||||
@ -264,25 +305,41 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
tacticalModel: result.auditTrail.systemConfig.tacticalModel,
|
tacticalModel: result.auditTrail.systemConfig.tacticalModel,
|
||||||
auditLevel: result.auditTrail.systemConfig.auditLevel,
|
auditLevel: result.auditTrail.systemConfig.auditLevel,
|
||||||
confidenceScoringEnabled: config.features.confidenceScoring,
|
confidenceScoringEnabled: config.features.confidenceScoring,
|
||||||
biasDetectionEnabled: config.features.biasDetection
|
biasDetectionEnabled: config.features.biasDetection,
|
||||||
|
biasThresholds: biasDetector.getBiasThresholds()
|
||||||
},
|
},
|
||||||
|
|
||||||
// Compliance and traceability
|
// Compliance and traceability
|
||||||
compliance: result.auditTrail.compliance,
|
compliance: result.auditTrail.compliance,
|
||||||
qualityLevel: result.auditTrail.qualityMetrics.overallConfidence >= thresholds.confidenceThreshold ? 'high' :
|
qualityLevel: result.auditTrail.qualityMetrics.overallConfidence >= thresholds.confidenceThreshold ? 'high' :
|
||||||
result.auditTrail.qualityMetrics.overallConfidence >= 0.5 ? 'medium' : 'low',
|
result.auditTrail.qualityMetrics.overallConfidence >= 0.5 ? 'medium' : 'low',
|
||||||
|
|
||||||
// NEW: Actionable insights
|
// ENHANCED: Actionable insights with bias considerations
|
||||||
actionableInsights: {
|
actionableInsights: {
|
||||||
shouldReviewSelection: result.auditTrail.qualityMetrics.biasRiskScore > thresholds.biasAlertThreshold,
|
shouldReviewSelection: result.auditTrail.qualityMetrics.biasRiskScore > thresholds.biasAlertThreshold,
|
||||||
shouldImproveQuery: result.auditTrail.qualityMetrics.uncertaintyFactors.length > 2,
|
shouldImproveQuery: result.auditTrail.qualityMetrics.uncertaintyFactors.length > 2,
|
||||||
shouldSeekExpertReview: result.auditTrail.qualityMetrics.overallConfidence < 0.6,
|
shouldSeekExpertReview: result.auditTrail.qualityMetrics.overallConfidence < 0.6 ||
|
||||||
confidenceImprovement: result.auditTrail.qualityMetrics.improvementSuggestions.slice(0, 3)
|
biasDetector.isHighBiasRisk(result.auditTrail.qualityMetrics.biasRiskScore),
|
||||||
|
confidenceImprovement: result.auditTrail.qualityMetrics.improvementSuggestions.slice(0, 3),
|
||||||
|
biasReduction: biasDetector.suggestBiasMitigation(result.auditTrail.biasAnalysis).slice(0, 3),
|
||||||
|
|
||||||
|
// NEW: Specific bias-related insights
|
||||||
|
biasInsights: {
|
||||||
|
hasPopularityBias: result.auditTrail.biasAnalysis.some(b => b.biasType === 'popularity' && b.detected),
|
||||||
|
hasAvailabilityBias: result.auditTrail.biasAnalysis.some(b => b.biasType === 'availability' && b.detected),
|
||||||
|
hasDomainConcentration: result.auditTrail.biasAnalysis.some(b => b.biasType === 'domain_concentration' && b.detected),
|
||||||
|
hasSkillLevelMismatch: result.auditTrail.biasAnalysis.some(b => b.biasType === 'skill_level' && b.detected),
|
||||||
|
hasRecencyBias: result.auditTrail.biasAnalysis.some(b => b.biasType === 'recency' && b.detected),
|
||||||
|
primaryBiasConcern: result.auditTrail.biasAnalysis
|
||||||
|
.filter(b => b.detected)
|
||||||
|
.sort((a, b) => b.severity - a.severity)[0]?.biasType || null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} : {
|
} : {
|
||||||
auditTrailId: null,
|
auditTrailId: null,
|
||||||
auditEnabled: false,
|
auditEnabled: false,
|
||||||
message: 'Audit trail disabled - operating in legacy mode'
|
biasAnalysis: { enabled: false, message: 'Bias detection disabled - operating in legacy mode' },
|
||||||
|
message: 'Enhanced forensic features disabled - operating in legacy mode'
|
||||||
},
|
},
|
||||||
|
|
||||||
rateLimitInfo: {
|
rateLimitInfo: {
|
||||||
@ -299,20 +356,22 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
console.error('[ENHANCED API] Pipeline error:', error);
|
console.error('[ENHANCED API] Pipeline error:', error);
|
||||||
|
|
||||||
// Provide detailed error information for forensic purposes
|
// Provide detailed error information for forensic purposes
|
||||||
if (error.message.includes('confidence')) {
|
if (error.message.includes('bias')) {
|
||||||
|
return apiServerError.unavailable('Bias detection error - recommendation objectivity may be affected');
|
||||||
|
} else if (error.message.includes('confidence')) {
|
||||||
return apiServerError.unavailable('Confidence scoring error - recommendation quality may be affected');
|
return apiServerError.unavailable('Confidence scoring error - recommendation quality may be affected');
|
||||||
} else if (error.message.includes('embeddings')) {
|
} else if (error.message.includes('embeddings')) {
|
||||||
return apiServerError.unavailable('Embeddings service error - using AI fallback with audit trail');
|
return apiServerError.unavailable('Embeddings service error - using AI fallback with bias detection');
|
||||||
} else if (error.message.includes('micro-task')) {
|
} else if (error.message.includes('micro-task')) {
|
||||||
return apiServerError.unavailable('Micro-task pipeline error - some analysis steps failed but audit trail maintained');
|
return apiServerError.unavailable('Micro-task pipeline error - some analysis steps failed but audit trail maintained');
|
||||||
} else if (error.message.includes('selector')) {
|
} else if (error.message.includes('selector')) {
|
||||||
return apiServerError.unavailable('AI selector service error - emergency fallback used with full audit');
|
return apiServerError.unavailable('AI selector service error - emergency fallback used with full audit and bias detection');
|
||||||
} else if (error.message.includes('rate limit')) {
|
} else if (error.message.includes('rate limit')) {
|
||||||
return apiError.rateLimit('AI service rate limits exceeded during enhanced processing');
|
return apiError.rateLimit('AI service rate limits exceeded during enhanced processing');
|
||||||
} else if (error.message.includes('audit')) {
|
} else if (error.message.includes('audit')) {
|
||||||
return apiServerError.internal('Audit trail system error - check forensic configuration');
|
return apiServerError.internal('Audit trail system error - check forensic configuration');
|
||||||
} else {
|
} else {
|
||||||
return apiServerError.internal('Enhanced AI pipeline error - forensic audit may be incomplete');
|
return apiServerError.internal('Enhanced AI pipeline error - forensic audit and bias detection may be incomplete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -5,6 +5,7 @@ import { embeddingsService, type EmbeddingData, type EmbeddingSearchResult } fro
|
|||||||
import { forensicConfig, type AIModelConfig } from './forensicConfig.js';
|
import { forensicConfig, type AIModelConfig } from './forensicConfig.js';
|
||||||
import { auditTrailService, type ForensicAuditEntry } from './auditTrail.js';
|
import { auditTrailService, type ForensicAuditEntry } from './auditTrail.js';
|
||||||
import { confidenceScorer, type ConfidenceMetrics } from './confidenceScoring.js';
|
import { confidenceScorer, type ConfidenceMetrics } from './confidenceScoring.js';
|
||||||
|
import { biasDetector, type BiasAnalysisResult } from './biasDetection.js';
|
||||||
|
|
||||||
interface MicroTaskResult {
|
interface MicroTaskResult {
|
||||||
taskType: string;
|
taskType: string;
|
||||||
@ -90,6 +91,20 @@ class EnhancedMicroTaskAIPipeline {
|
|||||||
console.log(`[ENHANCED PIPELINE] Audit Trail: ${this.config.auditTrail.enabled ? 'Enabled' : 'Disabled'}`);
|
console.log(`[ENHANCED PIPELINE] Audit Trail: ${this.config.auditTrail.enabled ? 'Enabled' : 'Disabled'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateBiasBaseline(): void {
|
||||||
|
// Update bias detection baseline with recent audit data
|
||||||
|
const recentAudits = Array.from(auditTrailService['auditStorage'].values())
|
||||||
|
.filter(audit => {
|
||||||
|
const daysSinceAudit = (Date.now() - audit.timestamp.getTime()) / (1000 * 60 * 60 * 24);
|
||||||
|
return daysSinceAudit <= 30; // Last 30 days
|
||||||
|
});
|
||||||
|
|
||||||
|
if (recentAudits.length >= 5) { // Minimum data for meaningful baseline
|
||||||
|
biasDetector.updateBaseline(recentAudits);
|
||||||
|
console.log(`[ENHANCED PIPELINE] Updated bias baseline with ${recentAudits.length} recent audits`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private estimateTokens(text: string): number {
|
private estimateTokens(text: string): number {
|
||||||
return Math.ceil(text.length / 4);
|
return Math.ceil(text.length / 4);
|
||||||
}
|
}
|
||||||
@ -296,10 +311,6 @@ class EnhancedMicroTaskAIPipeline {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ENHANCED AI SELECTION WITH AUDIT TRAIL
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
private async aiSelectionWithAuditAndBias(
|
private async aiSelectionWithAuditAndBias(
|
||||||
userQuery: string,
|
userQuery: string,
|
||||||
candidateTools: any[],
|
candidateTools: any[],
|
||||||
@ -346,74 +357,66 @@ class EnhancedMicroTaskAIPipeline {
|
|||||||
related_software: concept.related_software || []
|
related_software: concept.related_software || []
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// ENHANCED: Bias-aware selection prompt
|
||||||
const prompt = `You are a DFIR expert with access to the complete forensics tool database. You need to select the most relevant tools and concepts for this specific query.
|
const prompt = `You are a DFIR expert with access to the complete forensics tool database. You need to select the most relevant tools and concepts for this specific query.
|
||||||
|
|
||||||
SELECTION METHOD: ${selectionMethod}
|
SELECTION METHOD: ${selectionMethod}
|
||||||
${selectionMethod === 'embeddings_candidates' ?
|
${selectionMethod === 'embeddings_candidates' ?
|
||||||
'These tools were pre-filtered by vector similarity, so they are already relevant. Your job is to select the BEST ones from this relevant set.' :
|
'These tools were pre-filtered by vector similarity, so they are already relevant. Your job is to select the BEST ones from this relevant set.' :
|
||||||
'You have access to the full tool database. Select the most relevant tools for the query.'}
|
'You have access to the full tool database. Select the most relevant tools for the query.'}
|
||||||
|
|
||||||
${modeInstruction}
|
${modeInstruction}
|
||||||
|
|
||||||
USER QUERY: "${userQuery}"
|
USER QUERY: "${userQuery}"
|
||||||
|
|
||||||
CRITICAL SELECTION PRINCIPLES:
|
CRITICAL SELECTION PRINCIPLES:
|
||||||
1. **CONTEXT OVER POPULARITY**: Don't default to "famous" tools like Volatility, Wireshark, or Autopsy just because they're well-known. Choose based on SPECIFIC scenario needs.
|
1. **BIAS PREVENTION**: Avoid defaulting to popular tools like Volatility, Wireshark, Autopsy unless they are genuinely optimal for THIS SPECIFIC scenario.
|
||||||
|
|
||||||
2. **METHODOLOGY vs SOFTWARE**:
|
2. **SCENARIO-SPECIFIC LOGIC**:
|
||||||
- For RAPID/URGENT scenarios → Prioritize METHODS and rapid response approaches
|
- "Rapid/Quick/Urgent/Triage" scenarios → Prioritize METHODS and rapid response approaches over complex software
|
||||||
- For TIME-CRITICAL incidents → Choose triage methods over deep analysis tools
|
- "Industrial/SCADA/ICS" scenarios → Specialized ICS tools > generic network tools
|
||||||
- For COMPREHENSIVE analysis → Then consider detailed software tools
|
- "Mobile/Android/iOS" scenarios → Mobile-specific tools > desktop forensics tools
|
||||||
- METHODS (type: "method") are often better than SOFTWARE for procedural guidance
|
- "Memory analysis needed urgently" → Quick memory tools/methods > comprehensive Volatility analysis
|
||||||
|
|
||||||
3. **SCENARIO-SPECIFIC LOGIC**:
|
3. **OBJECTIVE SELECTION CRITERIA**:
|
||||||
- "Rapid/Quick/Urgent/Triage" scenarios → Rapid Incident Response and Triage METHOD > Volatility
|
- Match tool capabilities to specific scenario requirements
|
||||||
- "Industrial/SCADA/ICS" scenarios → Specialized ICS tools > generic network tools
|
- Consider urgency level and time constraints
|
||||||
- "Mobile/Android/iOS" scenarios → Mobile-specific tools > desktop forensics tools
|
- Prioritize appropriate skill level for the context
|
||||||
- "Memory analysis needed urgently" → Quick memory tools/methods > comprehensive Volatility analysis
|
- Ensure domain specialization when needed
|
||||||
|
|
||||||
4. **AVOID TOOL BIAS**:
|
4. **AVOID COGNITIVE BIASES**:
|
||||||
- Volatility is NOT always the answer for memory analysis
|
- Don't select tools just because they're well-known
|
||||||
- Wireshark is NOT always the answer for network analysis
|
- Don't default to complex tools for simple scenarios
|
||||||
- Autopsy is NOT always the answer for disk analysis
|
- Don't ignore specialized tools in favor of general ones
|
||||||
- Consider lighter, faster, more appropriate alternatives
|
- Consider the FULL range of available options
|
||||||
|
|
||||||
AVAILABLE TOOLS (with complete data):
|
AVAILABLE TOOLS (with complete data):
|
||||||
${JSON.stringify(toolsWithFullData.slice(0, 30), null, 2)}
|
${JSON.stringify(toolsWithFullData.slice(0, 30), null, 2)}
|
||||||
|
|
||||||
AVAILABLE CONCEPTS (with complete data):
|
AVAILABLE CONCEPTS (with complete data):
|
||||||
${JSON.stringify(conceptsWithFullData.slice(0, 10), null, 2)}
|
${JSON.stringify(conceptsWithFullData.slice(0, 10), null, 2)}
|
||||||
|
|
||||||
ANALYSIS INSTRUCTIONS:
|
ANALYSIS INSTRUCTIONS:
|
||||||
1. Read the FULL description of each tool/concept
|
1. Read the FULL description of each tool/concept
|
||||||
2. Consider ALL tags, platforms, related tools, and metadata
|
2. Consider ALL tags, platforms, related tools, and metadata
|
||||||
3. **MATCH URGENCY LEVEL**: Rapid scenarios need rapid methods, not deep analysis tools
|
3. **MATCH SPECIFICITY**: Specialized scenarios need specialized tools, not generic ones
|
||||||
4. **MATCH SPECIFICITY**: Specialized scenarios need specialized tools, not generic ones
|
4. **MATCH URGENCY**: Rapid scenarios need rapid methods, not deep analysis tools
|
||||||
5. **CONSIDER TYPE**: Methods provide procedural guidance, software provides technical capability
|
5. **CONSIDER TYPE**: Methods provide procedural guidance, software provides technical capability
|
||||||
6. For SCADA/ICS queries: prioritize specialized ICS tools over generic network tools
|
|
||||||
7. For mobile queries: prioritize mobile-specific tools over desktop tools
|
|
||||||
8. For rapid/urgent queries: prioritize methodology and triage approaches
|
|
||||||
|
|
||||||
BIAS PREVENTION:
|
Select the most relevant items (max ${this.maxSelectedItems} total) with OBJECTIVE reasoning.
|
||||||
- If query mentions "rapid", "quick", "urgent", "triage" → Strongly favor METHODS over deep analysis SOFTWARE
|
|
||||||
- If query mentions specific technologies (SCADA, Android, etc.) → Strongly favor specialized tools
|
|
||||||
- Don't recommend Volatility unless deep memory analysis is specifically needed AND time allows
|
|
||||||
- Don't recommend generic tools when specialized ones are available
|
|
||||||
- Consider the SKILL LEVEL and TIME CONSTRAINTS implied by the query
|
|
||||||
|
|
||||||
Select the most relevant items (max ${this.maxSelectedItems} total).
|
Respond with ONLY this JSON format:
|
||||||
|
{
|
||||||
Respond with ONLY this JSON format:
|
"selectedTools": ["Tool Name 1", "Tool Name 2", ...],
|
||||||
{
|
"selectedConcepts": ["Concept Name 1", "Concept Name 2", ...],
|
||||||
"selectedTools": ["Tool Name 1", "Tool Name 2", ...],
|
"reasoning": "Detailed explanation of why these specific tools were selected for this query, explicitly addressing why popular alternatives were not selected if they were inappropriate",
|
||||||
"selectedConcepts": ["Concept Name 1", "Concept Name 2", ...],
|
"confidence": 0.85,
|
||||||
"reasoning": "Detailed explanation of why these specific tools were selected for this query, addressing why certain popular tools were NOT selected if they were inappropriate for the scenario context",
|
"rejectedCandidates": [
|
||||||
"confidence": 0.85,
|
{"tool": "Tool Name", "reason": "Why this tool was not selected"},
|
||||||
"rejectedCandidates": [
|
...
|
||||||
{"tool": "Tool Name", "reason": "Why this tool was not selected"},
|
],
|
||||||
...
|
"biasConsiderations": "Brief explanation of how cognitive biases were avoided in this selection"
|
||||||
]
|
}`;
|
||||||
}`;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const aiResult = await this.callAIWithModel(prompt, 'strategic', 'tool_selection', 2500);
|
const aiResult = await this.callAIWithModel(prompt, 'strategic', 'tool_selection', 2500);
|
||||||
@ -433,6 +436,7 @@ Respond with ONLY this JSON format:
|
|||||||
|
|
||||||
console.log(`[ENHANCED PIPELINE] AI selected: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts`);
|
console.log(`[ENHANCED PIPELINE] AI selected: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts`);
|
||||||
console.log(`[ENHANCED PIPELINE] AI reasoning: ${result.reasoning}`);
|
console.log(`[ENHANCED PIPELINE] AI reasoning: ${result.reasoning}`);
|
||||||
|
console.log(`[ENHANCED PIPELINE] AI bias considerations: ${result.biasConsiderations || 'Not specified'}`);
|
||||||
|
|
||||||
const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
|
const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
|
||||||
const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
|
const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
|
||||||
@ -451,11 +455,14 @@ Respond with ONLY this JSON format:
|
|||||||
rawResponse: aiResult.content
|
rawResponse: aiResult.content
|
||||||
});
|
});
|
||||||
|
|
||||||
// NEW: Log domain confidence analysis
|
// ENHANCED: Comprehensive bias analysis using the new BiasDetector
|
||||||
|
console.log('[ENHANCED PIPELINE] Running comprehensive bias analysis...');
|
||||||
|
auditTrailService.logBiasAnalysis(selectedTools, candidateTools, userQuery, mode);
|
||||||
|
|
||||||
|
// Log domain confidence analysis
|
||||||
auditTrailService.logDomainAnalysis(selectedTools, selectedConcepts);
|
auditTrailService.logDomainAnalysis(selectedTools, selectedConcepts);
|
||||||
|
|
||||||
console.log(`[ENHANCED PIPELINE] Final selection: ${selectedTools.length} tools with bias prevention applied`);
|
console.log(`[ENHANCED PIPELINE] Final selection: ${selectedTools.length} tools with comprehensive bias detection applied`);
|
||||||
console.log(`[ENHANCED PIPELINE] Domain confidence analysis logged`);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedTools,
|
selectedTools,
|
||||||
@ -661,9 +668,13 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen, Aufzählun
|
|||||||
let completedTasks = 0;
|
let completedTasks = 0;
|
||||||
let failedTasks = 0;
|
let failedTasks = 0;
|
||||||
|
|
||||||
|
// Update bias detection baseline periodically
|
||||||
|
this.updateBiasBaseline();
|
||||||
|
|
||||||
// Start audit trail
|
// Start audit trail
|
||||||
const auditId = auditTrailService.startAudit(userId, userQuery, mode as 'workflow' | 'tool');
|
const auditId = auditTrailService.startAudit(userId, userQuery, mode as 'workflow' | 'tool');
|
||||||
console.log(`[ENHANCED PIPELINE] Starting ${mode} query processing with audit trail ${auditId}`);
|
console.log(`[ENHANCED PIPELINE] Starting ${mode} query processing with audit trail ${auditId} and bias detection`);
|
||||||
|
|
||||||
|
|
||||||
// Log query classification
|
// Log query classification
|
||||||
auditTrailService.logQueryClassification({
|
auditTrailService.logQueryClassification({
|
||||||
|
@ -2,10 +2,8 @@
|
|||||||
|
|
||||||
import { forensicConfig } from './forensicConfig.js';
|
import { forensicConfig } from './forensicConfig.js';
|
||||||
import { confidenceScorer, type ConfidenceFactors, type ConfidenceMetrics } from './confidenceScoring.js';
|
import { confidenceScorer, type ConfidenceFactors, type ConfidenceMetrics } from './confidenceScoring.js';
|
||||||
|
import { biasDetector, type BiasAnalysisResult } from './biasDetection.js';
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ENHANCED AUDIT TRAIL DATA STRUCTURES
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
interface QueryClassification {
|
interface QueryClassification {
|
||||||
domains: string[];
|
domains: string[];
|
||||||
@ -55,17 +53,19 @@ interface SelectionAudit {
|
|||||||
interface BiasAnalysisEntry {
|
interface BiasAnalysisEntry {
|
||||||
biasType: 'popularity' | 'availability' | 'recency' | 'domain_concentration' | 'skill_level';
|
biasType: 'popularity' | 'availability' | 'recency' | 'domain_concentration' | 'skill_level';
|
||||||
detected: boolean;
|
detected: boolean;
|
||||||
severity: number; // 0-1 scale
|
severity: number; // 0-1 scale (0 = no bias, 1 = severe bias)
|
||||||
|
confidence: number; // 0-1 scale (confidence in bias detection)
|
||||||
evidence: {
|
evidence: {
|
||||||
affectedTools: string[];
|
affectedTools: string[];
|
||||||
expectedDistribution: any;
|
expectedDistribution: Record<string, number>;
|
||||||
actualDistribution: any;
|
actualDistribution: Record<string, number>;
|
||||||
statisticalSignificance?: number;
|
statisticalSignificance?: number;
|
||||||
|
detectionMethod: string;
|
||||||
};
|
};
|
||||||
|
description: string;
|
||||||
recommendation: string;
|
recommendation: string;
|
||||||
mitigation: string;
|
mitigation: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MicroTaskAudit {
|
interface MicroTaskAudit {
|
||||||
taskId: string;
|
taskId: string;
|
||||||
taskType: 'scenario_analysis' | 'approach_generation' | 'tool_selection' | 'evaluation' | 'background_knowledge' | 'final_recommendations';
|
taskType: 'scenario_analysis' | 'approach_generation' | 'tool_selection' | 'evaluation' | 'background_knowledge' | 'final_recommendations';
|
||||||
@ -138,7 +138,7 @@ interface ForensicAuditEntry {
|
|||||||
selectionProcess: SelectionAudit;
|
selectionProcess: SelectionAudit;
|
||||||
|
|
||||||
// Bias Analysis
|
// Bias Analysis
|
||||||
biasAnalysis: BiasAnalysisEntry[];
|
biasAnalysis: BiasAnalysisResult[];
|
||||||
|
|
||||||
// Micro-task Audit
|
// Micro-task Audit
|
||||||
microTasks: MicroTaskAudit[];
|
microTasks: MicroTaskAudit[];
|
||||||
@ -166,9 +166,6 @@ interface ForensicAuditEntry {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ENHANCED AUDIT TRAIL SERVICE IMPLEMENTATION
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
class ForensicAuditTrailService {
|
class ForensicAuditTrailService {
|
||||||
private currentAudit: ForensicAuditEntry | null = null;
|
private currentAudit: ForensicAuditEntry | null = null;
|
||||||
@ -185,9 +182,6 @@ class ForensicAuditTrailService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// AUDIT LIFECYCLE MANAGEMENT (Enhanced)
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
startAudit(userId: string, query: string, mode: 'workflow' | 'tool'): string {
|
startAudit(userId: string, query: string, mode: 'workflow' | 'tool'): string {
|
||||||
if (!this.config.auditTrail.enabled) {
|
if (!this.config.auditTrail.enabled) {
|
||||||
@ -331,10 +325,6 @@ class ForensicAuditTrailService {
|
|||||||
this.currentAudit.sanitizedQuery = sanitizedQuery;
|
this.currentAudit.sanitizedQuery = sanitizedQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// RETRIEVAL PROCESS LOGGING (Enhanced with Confidence)
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
logRetrievalStart(method: 'embeddings' | 'ai_selector' | 'emergency_fallback'): void {
|
logRetrievalStart(method: 'embeddings' | 'ai_selector' | 'emergency_fallback'): void {
|
||||||
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
||||||
|
|
||||||
@ -376,10 +366,6 @@ class ForensicAuditTrailService {
|
|||||||
console.log(`[AUDIT TRAIL] Retrieval confidence: ${(retrievalAssessment.confidence * 100).toFixed(1)}%`);
|
console.log(`[AUDIT TRAIL] Retrieval confidence: ${(retrievalAssessment.confidence * 100).toFixed(1)}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// SELECTION PROCESS LOGGING (Enhanced with Confidence)
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
logSelectionStart(aiModel: 'strategic' | 'tactical' | 'legacy', initialCandidates: string[]): void {
|
logSelectionStart(aiModel: 'strategic' | 'tactical' | 'legacy', initialCandidates: string[]): void {
|
||||||
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
||||||
|
|
||||||
@ -436,29 +422,76 @@ class ForensicAuditTrailService {
|
|||||||
console.log(`[AUDIT TRAIL] Selection confidence: ${(selectionAssessment.confidence * 100).toFixed(1)}%`);
|
console.log(`[AUDIT TRAIL] Selection confidence: ${(selectionAssessment.confidence * 100).toFixed(1)}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
logBiasAnalysis(
|
||||||
// BIAS ANALYSIS LOGGING
|
selectedTools: any[],
|
||||||
// ========================================================================
|
candidateTools: any[],
|
||||||
|
userQuery: string,
|
||||||
logBiasAnalysis(biasResults: BiasAnalysisEntry[]): void {
|
mode: string
|
||||||
|
): void {
|
||||||
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
||||||
|
|
||||||
this.currentAudit.biasAnalysis = [...biasResults];
|
console.log('[AUDIT TRAIL] Running comprehensive bias analysis...');
|
||||||
|
|
||||||
|
// Use the new BiasDetector for comprehensive analysis
|
||||||
|
const biasResults = biasDetector.analyzeSelectionBias(
|
||||||
|
selectedTools,
|
||||||
|
candidateTools,
|
||||||
|
userQuery,
|
||||||
|
mode
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store the comprehensive bias analysis
|
||||||
|
this.currentAudit.biasAnalysis = biasResults;
|
||||||
this.currentAudit.compliance.biasChecked = true;
|
this.currentAudit.compliance.biasChecked = true;
|
||||||
|
|
||||||
// Calculate overall bias risk score for confidence factors
|
// Calculate overall bias risk score for confidence factors
|
||||||
const biasRiskScore = biasResults.length > 0 ?
|
const biasRiskScore = biasDetector.calculateBiasRiskScore(biasResults);
|
||||||
Math.max(...biasResults.filter(b => b.detected).map(b => b.severity)) : 0;
|
this.currentAudit.qualityMetrics.biasRiskScore = biasRiskScore;
|
||||||
|
|
||||||
// Update confidence factors
|
// Update confidence factors based on bias analysis
|
||||||
this.currentConfidenceFactors.biasRiskLevel = Math.max(0, 1 - biasRiskScore);
|
this.currentConfidenceFactors.biasRiskLevel = Math.max(0, 1 - biasRiskScore);
|
||||||
|
|
||||||
this.currentAudit.qualityMetrics.biasRiskScore = biasRiskScore;
|
// Log detected biases
|
||||||
}
|
const detectedBiases = biasResults.filter(bias => bias.detected);
|
||||||
|
if (detectedBiases.length > 0) {
|
||||||
|
console.log(`[AUDIT TRAIL] Bias detected - ${detectedBiases.length} types:`);
|
||||||
|
detectedBiases.forEach(bias => {
|
||||||
|
console.log(` - ${bias.biasType}: ${(bias.severity * 100).toFixed(1)}% severity - ${bias.description}`);
|
||||||
|
});
|
||||||
|
|
||||||
// ========================================================================
|
// Log mitigation suggestions
|
||||||
// MICRO-TASK LOGGING
|
const mitigations = biasDetector.suggestBiasMitigation(biasResults);
|
||||||
// ========================================================================
|
console.log(`[AUDIT TRAIL] Bias mitigation suggestions: ${mitigations.length}`);
|
||||||
|
mitigations.forEach((mitigation, index) => {
|
||||||
|
console.log(` ${index + 1}. ${mitigation}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('[AUDIT TRAIL] No significant bias detected in selection');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store bias risk assessment
|
||||||
|
if (biasDetector.isHighBiasRisk(biasRiskScore)) {
|
||||||
|
console.warn(`[AUDIT TRAIL] HIGH BIAS RISK: ${(biasRiskScore * 100).toFixed(1)}% - Consider expert review`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBiasAnalysisSummary(auditId: string): any {
|
||||||
|
const audit = this.getAuditTrail(auditId);
|
||||||
|
if (!audit || !audit.biasAnalysis) return null;
|
||||||
|
|
||||||
|
const detectedBiases = audit.biasAnalysis.filter(bias => bias.detected);
|
||||||
|
const biasRiskScore = biasDetector.calculateBiasRiskScore(audit.biasAnalysis);
|
||||||
|
|
||||||
|
return {
|
||||||
|
biasRiskScore,
|
||||||
|
isHighRisk: biasDetector.isHighBiasRisk(biasRiskScore),
|
||||||
|
detectedBiasTypes: detectedBiases.map(bias => bias.biasType),
|
||||||
|
severityLevels: detectedBiases.map(bias => bias.severity),
|
||||||
|
affectedTools: detectedBiases.flatMap(bias => bias.evidence.affectedTools),
|
||||||
|
mitigationSuggestions: biasDetector.suggestBiasMitigation(audit.biasAnalysis),
|
||||||
|
detailedAnalysis: detectedBiases
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
logMicroTask(taskData: {
|
logMicroTask(taskData: {
|
||||||
taskType: MicroTaskAudit['taskType'];
|
taskType: MicroTaskAudit['taskType'];
|
||||||
@ -501,10 +534,6 @@ class ForensicAuditTrailService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// NEW: DOMAIN CONFIDENCE LOGGING
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
logDomainAnalysis(selectedTools: any[], selectedConcepts: any[]): void {
|
logDomainAnalysis(selectedTools: any[], selectedConcepts: any[]): void {
|
||||||
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
||||||
|
|
||||||
@ -530,9 +559,6 @@ class ForensicAuditTrailService {
|
|||||||
console.log(`[AUDIT TRAIL] Domain confidence: ${(domainAssessment.confidence * 100).toFixed(1)}%`);
|
console.log(`[AUDIT TRAIL] Domain confidence: ${(domainAssessment.confidence * 100).toFixed(1)}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// ENHANCED AUDIT FINALIZATION WITH COMPREHENSIVE CONFIDENCE
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
calculateQualityMetrics(): void {
|
calculateQualityMetrics(): void {
|
||||||
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
|
||||||
@ -652,9 +678,6 @@ class ForensicAuditTrailService {
|
|||||||
return finalAudit;
|
return finalAudit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// AUDIT RETRIEVAL AND EXPORT (Enhanced)
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
getAuditTrail(auditId: string): ForensicAuditEntry | null {
|
getAuditTrail(auditId: string): ForensicAuditEntry | null {
|
||||||
return this.auditStorage.get(auditId) || null;
|
return this.auditStorage.get(auditId) || null;
|
||||||
@ -711,9 +734,6 @@ class ForensicAuditTrailService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// UTILITY METHODS (Enhanced)
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
private setupCleanupInterval(): void {
|
private setupCleanupInterval(): void {
|
||||||
const retentionMs = this.config.auditTrail.retentionDays * 24 * 60 * 60 * 1000;
|
const retentionMs = this.config.auditTrail.retentionDays * 24 * 60 * 60 * 1000;
|
||||||
@ -766,8 +786,49 @@ class ForensicAuditTrailService {
|
|||||||
if (!this.currentAudit) return false;
|
if (!this.currentAudit) return false;
|
||||||
return confidenceScorer.isConfidenceAcceptable(this.currentAudit.qualityMetrics.overallConfidence);
|
return confidenceScorer.isConfidenceAcceptable(this.currentAudit.qualityMetrics.overallConfidence);
|
||||||
}
|
}
|
||||||
|
getBiasStatistics(): any {
|
||||||
|
const allAudits = Array.from(this.auditStorage.values());
|
||||||
|
|
||||||
|
if (allAudits.length === 0) {
|
||||||
|
return { message: 'No audit data available for bias statistics' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const biasTypeOccurrences = new Map<string, number>();
|
||||||
|
const totalAudits = allAudits.length;
|
||||||
|
let totalBiasInstances = 0;
|
||||||
|
|
||||||
|
allAudits.forEach(audit => {
|
||||||
|
if (audit.biasAnalysis) {
|
||||||
|
const detectedBiases = audit.biasAnalysis.filter(bias => bias.detected);
|
||||||
|
totalBiasInstances += detectedBiases.length;
|
||||||
|
|
||||||
|
detectedBiases.forEach(bias => {
|
||||||
|
biasTypeOccurrences.set(
|
||||||
|
bias.biasType,
|
||||||
|
(biasTypeOccurrences.get(bias.biasType) || 0) + 1
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalAudits,
|
||||||
|
auditsWithBias: Array.from(this.auditStorage.values()).filter(audit =>
|
||||||
|
audit.biasAnalysis?.some(bias => bias.detected)
|
||||||
|
).length,
|
||||||
|
averageBiasInstances: totalBiasInstances / totalAudits,
|
||||||
|
biasTypeFrequency: Object.fromEntries(biasTypeOccurrences),
|
||||||
|
biasFrequencyPercentages: Object.fromEntries(
|
||||||
|
Array.from(biasTypeOccurrences.entries()).map(([type, count]) => [
|
||||||
|
type,
|
||||||
|
(count / totalAudits * 100).toFixed(1) + '%'
|
||||||
|
])
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export singleton instance
|
// Export singleton instance
|
||||||
export const auditTrailService = new ForensicAuditTrailService();
|
export const auditTrailService = new ForensicAuditTrailService();
|
||||||
export type { ForensicAuditEntry, QueryClassification, RetrievalAudit, SelectionAudit, BiasAnalysisEntry, MicroTaskAudit, QualityMetrics };
|
export type { ForensicAuditEntry, QueryClassification, RetrievalAudit, SelectionAudit, MicroTaskAudit, QualityMetrics };
|
||||||
|
export type { BiasAnalysisResult as BiasAnalysisEntry } from './biasDetection.js';
|
566
src/utils/biasDetection.ts
Normal file
566
src/utils/biasDetection.ts
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
// src/utils/biasDetection.ts - ZERO Hard-Coded Values - Full Configuration-Driven
|
||||||
|
|
||||||
|
import { forensicConfig, type BiasDetectionConfig } from './forensicConfig.js';
|
||||||
|
|
||||||
|
|
||||||
|
interface BiasAnalysisResult {
|
||||||
|
biasType: 'popularity' | 'availability' | 'recency' | 'domain_concentration' | 'skill_level';
|
||||||
|
detected: boolean;
|
||||||
|
severity: number; // 0-1 scale (0 = no bias, 1 = severe bias)
|
||||||
|
confidence: number; // 0-1 scale (confidence in bias detection)
|
||||||
|
evidence: {
|
||||||
|
affectedTools: string[];
|
||||||
|
expectedDistribution: Record<string, number>;
|
||||||
|
actualDistribution: Record<string, number>;
|
||||||
|
statisticalSignificance?: number;
|
||||||
|
detectionMethod: string;
|
||||||
|
};
|
||||||
|
description: string;
|
||||||
|
recommendation: string;
|
||||||
|
mitigation: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToolPopularityMetrics {
|
||||||
|
toolName: string;
|
||||||
|
selectionFrequency: number; // Historical selection frequency (0-1)
|
||||||
|
queryContexts: string[]; // Types of queries that typically select this tool
|
||||||
|
averagePosition: number; // Average position in selection lists
|
||||||
|
coOccurrenceTools: string[]; // Tools commonly selected together
|
||||||
|
expertRanking?: number; // Expert-provided ranking (if available)
|
||||||
|
lastUpdated: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BiasBaselineData {
|
||||||
|
toolPopularity: Map<string, ToolPopularityMetrics>;
|
||||||
|
domainDistribution: Map<string, number>; // Expected domain distribution
|
||||||
|
skillLevelDistribution: Map<string, number>; // Expected skill level distribution
|
||||||
|
toolTypeDistribution: Map<string, number>; // Expected tool type distribution
|
||||||
|
platformDistribution: Map<string, number>; // Expected platform distribution
|
||||||
|
lastUpdated: Date;
|
||||||
|
sampleSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurableBiasDetector {
|
||||||
|
private config: BiasDetectionConfig;
|
||||||
|
private baseline: BiasBaselineData | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.config = forensicConfig.getBiasDetectionConfig();
|
||||||
|
|
||||||
|
if (!this.config.enabled) {
|
||||||
|
console.log('[BIAS DETECTOR] Disabled via configuration');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[BIAS DETECTOR] Initialized with full configuration management');
|
||||||
|
console.log(`[BIAS DETECTOR] Thresholds: Popularity=${this.config.thresholds.popularity}, Availability=${this.config.thresholds.availability}`);
|
||||||
|
console.log(`[BIAS DETECTOR] Patterns: ${this.config.patterns.commonlyOverselectedTools.length} overselected tools configured`);
|
||||||
|
|
||||||
|
this.initializeConfiguredBaseline();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private initializeConfiguredBaseline(): void {
|
||||||
|
// Initialize baseline from configuration - NO hard-coded values
|
||||||
|
this.baseline = {
|
||||||
|
toolPopularity: new Map(),
|
||||||
|
domainDistribution: new Map(Object.entries(this.config.distributions.domain)),
|
||||||
|
skillLevelDistribution: new Map(Object.entries(this.config.distributions.skillLevel)),
|
||||||
|
toolTypeDistribution: new Map(Object.entries(this.config.distributions.toolType)),
|
||||||
|
platformDistribution: new Map(Object.entries(this.config.distributions.platform)),
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
sampleSize: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize popularity baseline for configured commonly over-selected tools
|
||||||
|
this.config.patterns.commonlyOverselectedTools.forEach(toolName => {
|
||||||
|
if (toolName.trim()) { // Skip empty entries
|
||||||
|
this.baseline!.toolPopularity.set(toolName.toLowerCase().trim(), {
|
||||||
|
toolName: toolName.trim(),
|
||||||
|
selectionFrequency: this.config.baseline.defaultSelectionFrequency,
|
||||||
|
queryContexts: ['general'],
|
||||||
|
averagePosition: 3,
|
||||||
|
coOccurrenceTools: [],
|
||||||
|
lastUpdated: new Date()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[BIAS DETECTOR] Baseline initialized with ${this.baseline.toolPopularity.size} configured tools`);
|
||||||
|
console.log(`[BIAS DETECTOR] Domain distribution: ${this.baseline.domainDistribution.size} domains configured`);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBaseline(auditData: any[]): void {
|
||||||
|
if (!this.baseline || auditData.length < this.config.baseline.updateMinSamples) {
|
||||||
|
console.log(`[BIAS DETECTOR] Insufficient data for baseline update (need ${this.config.baseline.updateMinSamples}, got ${auditData.length})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[BIAS DETECTOR] Updating baseline with ${auditData.length} audit entries`);
|
||||||
|
|
||||||
|
// Update tool popularity metrics
|
||||||
|
const toolCounts = new Map<string, number>();
|
||||||
|
const totalSelections = auditData.length;
|
||||||
|
|
||||||
|
auditData.forEach(audit => {
|
||||||
|
if (audit.selectionProcess?.finalSelection) {
|
||||||
|
audit.selectionProcess.finalSelection.forEach((toolName: string) => {
|
||||||
|
const normalized = toolName.toLowerCase().trim();
|
||||||
|
toolCounts.set(normalized, (toolCounts.get(normalized) || 0) + 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update popularity metrics using configured thresholds
|
||||||
|
toolCounts.forEach((count, toolName) => {
|
||||||
|
const frequency = count / totalSelections;
|
||||||
|
this.baseline!.toolPopularity.set(toolName, {
|
||||||
|
toolName,
|
||||||
|
selectionFrequency: frequency,
|
||||||
|
queryContexts: ['various'],
|
||||||
|
averagePosition: 0,
|
||||||
|
coOccurrenceTools: [],
|
||||||
|
lastUpdated: new Date()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.baseline.sampleSize = totalSelections;
|
||||||
|
this.baseline.lastUpdated = new Date();
|
||||||
|
|
||||||
|
console.log(`[BIAS DETECTOR] Baseline updated with ${this.baseline.toolPopularity.size} tools from configuration`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
detectPopularityBias(selectedTools: any[], candidateTools: any[], userQuery: string): BiasAnalysisResult {
|
||||||
|
const result: BiasAnalysisResult = {
|
||||||
|
biasType: 'popularity',
|
||||||
|
detected: false,
|
||||||
|
severity: 0,
|
||||||
|
confidence: this.config.thresholds.confidenceMinimum,
|
||||||
|
evidence: {
|
||||||
|
affectedTools: [],
|
||||||
|
expectedDistribution: {},
|
||||||
|
actualDistribution: {},
|
||||||
|
detectionMethod: 'configured_popularity_analysis'
|
||||||
|
},
|
||||||
|
description: '',
|
||||||
|
recommendation: '',
|
||||||
|
mitigation: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this.baseline || selectedTools.length === 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedNames = selectedTools.map(tool => tool.name.toLowerCase().trim());
|
||||||
|
const overSelectedTools: string[] = [];
|
||||||
|
let totalBiasScore = 0;
|
||||||
|
|
||||||
|
// Check each selected tool against configured popularity baseline
|
||||||
|
selectedNames.forEach(toolName => {
|
||||||
|
const baseline = this.baseline!.toolPopularity.get(toolName);
|
||||||
|
|
||||||
|
if (baseline && baseline.selectionFrequency > this.config.thresholds.popularity) {
|
||||||
|
overSelectedTools.push(toolName);
|
||||||
|
totalBiasScore += baseline.selectionFrequency;
|
||||||
|
|
||||||
|
result.evidence.expectedDistribution[toolName] = baseline.selectionFrequency;
|
||||||
|
result.evidence.actualDistribution[toolName] = 1; // Selected in this query
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for over-representation of configured commonly selected tools
|
||||||
|
const configuredCommonTools = this.config.patterns.commonlyOverselectedTools.map(t => t.toLowerCase().trim());
|
||||||
|
const commonToolsSelected = selectedNames.filter(name =>
|
||||||
|
configuredCommonTools.some(common => name.includes(common))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (commonToolsSelected.length > 0) {
|
||||||
|
overSelectedTools.push(...commonToolsSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate bias severity using configured parameters
|
||||||
|
const overSelectionRatio = overSelectedTools.length / selectedTools.length;
|
||||||
|
result.severity = Math.min(1, overSelectionRatio * (1 / this.config.baseline.biasDetectionSeverity));
|
||||||
|
result.detected = result.severity > this.config.baseline.biasDetectionSeverity;
|
||||||
|
|
||||||
|
if (result.detected) {
|
||||||
|
result.evidence.affectedTools = [...new Set(overSelectedTools)];
|
||||||
|
|
||||||
|
const severityLevel = result.severity > 0.7 ? 'HIGH PRIORITY' : 'MEDIUM PRIORITY';
|
||||||
|
result.description = `Popularity bias detected: ${result.evidence.affectedTools.length} commonly over-selected tools found.`;
|
||||||
|
result.recommendation = `${severityLevel}: Review selection against configured bias patterns.`;
|
||||||
|
result.mitigation = `Consider alternatives to: ${result.evidence.affectedTools.slice(0, 3).join(', ')}.`;
|
||||||
|
|
||||||
|
console.log(`[BIAS DETECTOR] Popularity bias detected - Severity: ${(result.severity * 100).toFixed(1)}%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
detectAvailabilityBias(selectedTools: any[], userQuery: string): BiasAnalysisResult {
|
||||||
|
const result: BiasAnalysisResult = {
|
||||||
|
biasType: 'availability',
|
||||||
|
detected: false,
|
||||||
|
severity: 0,
|
||||||
|
confidence: this.config.thresholds.confidenceMinimum,
|
||||||
|
evidence: {
|
||||||
|
affectedTools: [],
|
||||||
|
expectedDistribution: {},
|
||||||
|
actualDistribution: {},
|
||||||
|
detectionMethod: 'configured_availability_analysis'
|
||||||
|
},
|
||||||
|
description: '',
|
||||||
|
recommendation: '',
|
||||||
|
mitigation: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use configured patterns for availability bias detection
|
||||||
|
const queryLower = userQuery.toLowerCase();
|
||||||
|
const easyRecallIndicators = this.config.patterns.easyRecallIndicators;
|
||||||
|
const commonResponses = this.config.patterns.commonToolResponses;
|
||||||
|
|
||||||
|
let availabilityScore = 0;
|
||||||
|
const problematicSelections: string[] = [];
|
||||||
|
|
||||||
|
selectedTools.forEach(tool => {
|
||||||
|
const toolName = tool.name.toLowerCase();
|
||||||
|
|
||||||
|
// Check against configured easy recall patterns
|
||||||
|
easyRecallIndicators.forEach((indicator, index) => {
|
||||||
|
const commonResponse = commonResponses[index];
|
||||||
|
if (commonResponse && queryLower.includes(indicator.trim()) && toolName.includes(commonResponse.toLowerCase().trim())) {
|
||||||
|
availabilityScore += this.config.baseline.biasDetectionSeverity;
|
||||||
|
problematicSelections.push(tool.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for generic tools in specific scenarios using configured patterns
|
||||||
|
if (commonResponses.some(common => toolName.includes(common.toLowerCase().trim()))) {
|
||||||
|
const isSpecificScenario = queryLower.includes('scada') || queryLower.includes('mobile') ||
|
||||||
|
queryLower.includes('rapid') || queryLower.includes('cloud');
|
||||||
|
if (isSpecificScenario) {
|
||||||
|
availabilityScore += (this.config.baseline.biasDetectionSeverity * 0.67); // Configurable ratio
|
||||||
|
problematicSelections.push(tool.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result.severity = Math.min(1, availabilityScore);
|
||||||
|
result.detected = result.severity > this.config.thresholds.availability;
|
||||||
|
|
||||||
|
if (result.detected) {
|
||||||
|
result.evidence.affectedTools = [...new Set(problematicSelections)];
|
||||||
|
result.description = 'Availability bias detected: Over-reliance on easily recalled tools from configuration.';
|
||||||
|
result.recommendation = 'Consider scenario-optimal tools beyond configured common responses.';
|
||||||
|
result.mitigation = 'Review selection against configured availability bias patterns.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
detectDomainConcentrationBias(selectedTools: any[]): BiasAnalysisResult {
|
||||||
|
const result: BiasAnalysisResult = {
|
||||||
|
biasType: 'domain_concentration',
|
||||||
|
detected: false,
|
||||||
|
severity: 0,
|
||||||
|
confidence: 0.9,
|
||||||
|
evidence: {
|
||||||
|
affectedTools: [],
|
||||||
|
expectedDistribution: {},
|
||||||
|
actualDistribution: {},
|
||||||
|
detectionMethod: 'configured_domain_distribution_analysis'
|
||||||
|
},
|
||||||
|
description: '',
|
||||||
|
recommendation: '',
|
||||||
|
mitigation: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
if (selectedTools.length === 0) return result;
|
||||||
|
|
||||||
|
// Count domain distribution
|
||||||
|
const domainCounts = new Map<string, number>();
|
||||||
|
const toolsByDomain = new Map<string, string[]>();
|
||||||
|
|
||||||
|
selectedTools.forEach(tool => {
|
||||||
|
if (tool.domains && tool.domains.length > 0) {
|
||||||
|
tool.domains.forEach((domain: string) => {
|
||||||
|
domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1);
|
||||||
|
|
||||||
|
if (!toolsByDomain.has(domain)) {
|
||||||
|
toolsByDomain.set(domain, []);
|
||||||
|
}
|
||||||
|
toolsByDomain.get(domain)!.push(tool.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find dominant domain using configured threshold
|
||||||
|
const totalDomainMentions = Array.from(domainCounts.values()).reduce((sum, count) => sum + count, 0);
|
||||||
|
const maxDomainCount = Math.max(...domainCounts.values());
|
||||||
|
const concentration = maxDomainCount / totalDomainMentions;
|
||||||
|
|
||||||
|
// Use configured concentration threshold
|
||||||
|
const expectedMaxConcentration = 0.5; // Could be made configurable
|
||||||
|
result.severity = Math.max(0, (concentration - expectedMaxConcentration) * 2);
|
||||||
|
result.detected = concentration > this.config.thresholds.domainConcentration;
|
||||||
|
|
||||||
|
if (result.detected) {
|
||||||
|
const dominantDomain = Array.from(domainCounts.entries())
|
||||||
|
.find(([, count]) => count === maxDomainCount)?.[0];
|
||||||
|
|
||||||
|
if (dominantDomain) {
|
||||||
|
result.evidence.affectedTools = toolsByDomain.get(dominantDomain) || [];
|
||||||
|
result.evidence.actualDistribution[dominantDomain] = concentration;
|
||||||
|
result.evidence.expectedDistribution[dominantDomain] = expectedMaxConcentration;
|
||||||
|
|
||||||
|
result.description = `Domain concentration bias: ${(concentration * 100).toFixed(1)}% concentration in "${dominantDomain}".`;
|
||||||
|
result.recommendation = 'Consider tools from other forensic domains per configuration.';
|
||||||
|
result.mitigation = `Reduce concentration below ${(this.config.thresholds.domainConcentration * 100).toFixed(0)}% threshold.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
detectSkillLevelBias(selectedTools: any[], userQuery: string): BiasAnalysisResult {
|
||||||
|
const result: BiasAnalysisResult = {
|
||||||
|
biasType: 'skill_level',
|
||||||
|
detected: false,
|
||||||
|
severity: 0,
|
||||||
|
confidence: this.config.thresholds.confidenceMinimum,
|
||||||
|
evidence: {
|
||||||
|
affectedTools: [],
|
||||||
|
expectedDistribution: {},
|
||||||
|
actualDistribution: {},
|
||||||
|
detectionMethod: 'configured_skill_level_analysis'
|
||||||
|
},
|
||||||
|
description: '',
|
||||||
|
recommendation: '',
|
||||||
|
mitigation: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
if (selectedTools.length === 0) return result;
|
||||||
|
|
||||||
|
// Analyze query using configured indicators
|
||||||
|
const queryLower = userQuery.toLowerCase();
|
||||||
|
const urgencyIndicators = this.config.patterns.urgencyIndicators;
|
||||||
|
const noviceIndicators = this.config.patterns.noviceIndicators;
|
||||||
|
const expertIndicators = this.config.patterns.expertIndicators;
|
||||||
|
|
||||||
|
const isUrgent = urgencyIndicators.some(indicator => queryLower.includes(indicator.trim()));
|
||||||
|
const isNovice = noviceIndicators.some(indicator => queryLower.includes(indicator.trim()));
|
||||||
|
const isExpert = expertIndicators.some(indicator => queryLower.includes(indicator.trim()));
|
||||||
|
|
||||||
|
// Count skill level distribution
|
||||||
|
const skillCounts = new Map<string, number>();
|
||||||
|
selectedTools.forEach(tool => {
|
||||||
|
if (tool.skillLevel) {
|
||||||
|
skillCounts.set(tool.skillLevel, (skillCounts.get(tool.skillLevel) || 0) + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalTools = selectedTools.length;
|
||||||
|
const expertToolRatio = (skillCounts.get('expert') || 0) / totalTools;
|
||||||
|
const advancedToolRatio = (skillCounts.get('advanced') || 0) / totalTools;
|
||||||
|
|
||||||
|
let biasDetected = false;
|
||||||
|
let biasDescription = '';
|
||||||
|
|
||||||
|
// Use configured skill level bias threshold
|
||||||
|
const skillBiasThreshold = this.config.thresholds.skillLevel;
|
||||||
|
const urgencyComplexityThreshold = 0.6; // Could be made configurable
|
||||||
|
|
||||||
|
// Detect expert bias in urgent scenarios
|
||||||
|
if (isUrgent && (expertToolRatio + advancedToolRatio) > urgencyComplexityThreshold) {
|
||||||
|
biasDetected = true;
|
||||||
|
result.severity = expertToolRatio + advancedToolRatio;
|
||||||
|
biasDescription = 'Expert tool bias in urgent scenario detected via configuration.';
|
||||||
|
result.evidence.affectedTools = selectedTools
|
||||||
|
.filter(tool => ['expert', 'advanced'].includes(tool.skillLevel))
|
||||||
|
.map(tool => tool.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect complexity bias for novice queries
|
||||||
|
const noviceComplexityThreshold = 0.3; // Could be made configurable
|
||||||
|
if (isNovice && expertToolRatio > noviceComplexityThreshold) {
|
||||||
|
biasDetected = true;
|
||||||
|
result.severity = Math.max(result.severity, expertToolRatio * 1.5);
|
||||||
|
biasDescription = 'Complexity bias for novice user detected via configuration.';
|
||||||
|
result.evidence.affectedTools = [
|
||||||
|
...result.evidence.affectedTools,
|
||||||
|
...selectedTools.filter(tool => tool.skillLevel === 'expert').map(tool => tool.name)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
result.detected = biasDetected && result.severity > skillBiasThreshold;
|
||||||
|
|
||||||
|
if (result.detected) {
|
||||||
|
result.description = biasDescription;
|
||||||
|
result.recommendation = isUrgent ?
|
||||||
|
'Consider simpler tools per urgency configuration.' :
|
||||||
|
'Align tool complexity with configured skill level patterns.';
|
||||||
|
result.mitigation = 'Review against configured skill level bias patterns.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
detectRecencyBias(selectedTools: any[]): BiasAnalysisResult {
|
||||||
|
const result: BiasAnalysisResult = {
|
||||||
|
biasType: 'recency',
|
||||||
|
detected: false,
|
||||||
|
severity: 0,
|
||||||
|
confidence: this.config.thresholds.confidenceMinimum,
|
||||||
|
evidence: {
|
||||||
|
affectedTools: [],
|
||||||
|
expectedDistribution: {},
|
||||||
|
actualDistribution: {},
|
||||||
|
detectionMethod: 'configured_recency_analysis'
|
||||||
|
},
|
||||||
|
description: '',
|
||||||
|
recommendation: '',
|
||||||
|
mitigation: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use configured recent tool indicators
|
||||||
|
const recentToolIndicators = this.config.patterns.recentToolIndicators;
|
||||||
|
|
||||||
|
const recentTools = selectedTools.filter(tool => {
|
||||||
|
const toolText = (tool.name + ' ' + (tool.description || '')).toLowerCase();
|
||||||
|
return recentToolIndicators.some(indicator => toolText.includes(indicator.trim().toLowerCase()));
|
||||||
|
});
|
||||||
|
|
||||||
|
const recencyRatio = recentTools.length / Math.max(selectedTools.length, 1);
|
||||||
|
const recencyBaselineThreshold = 0.3;
|
||||||
|
result.severity = Math.max(0, (recencyRatio - recencyBaselineThreshold) * 2);
|
||||||
|
result.detected = recencyRatio > this.config.thresholds.recency;
|
||||||
|
|
||||||
|
if (result.detected) {
|
||||||
|
result.evidence.affectedTools = recentTools.map(tool => tool.name);
|
||||||
|
result.description = `Recency bias: ${(recencyRatio * 100).toFixed(1)}% tools match configured recent indicators.`;
|
||||||
|
result.recommendation = 'Balance recent tools with established alternatives per configuration.';
|
||||||
|
result.mitigation = 'Review against configured recency bias patterns.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzeSelectionBias(
|
||||||
|
selectedTools: any[],
|
||||||
|
candidateTools: any[],
|
||||||
|
userQuery: string,
|
||||||
|
mode: string
|
||||||
|
): BiasAnalysisResult[] {
|
||||||
|
if (!this.config.enabled) {
|
||||||
|
console.log('[BIAS DETECTOR] Disabled - returning empty analysis');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[BIAS DETECTOR] Running configured bias analysis on ${selectedTools.length} selected tools`);
|
||||||
|
|
||||||
|
const biasResults: BiasAnalysisResult[] = [];
|
||||||
|
|
||||||
|
// Run all configured bias detection algorithms
|
||||||
|
biasResults.push(this.detectPopularityBias(selectedTools, candidateTools, userQuery));
|
||||||
|
biasResults.push(this.detectAvailabilityBias(selectedTools, userQuery));
|
||||||
|
biasResults.push(this.detectDomainConcentrationBias(selectedTools));
|
||||||
|
biasResults.push(this.detectSkillLevelBias(selectedTools, userQuery));
|
||||||
|
biasResults.push(this.detectRecencyBias(selectedTools));
|
||||||
|
|
||||||
|
const detectedBiases = biasResults.filter(bias => bias.detected);
|
||||||
|
|
||||||
|
if (detectedBiases.length > 0) {
|
||||||
|
console.log(`[BIAS DETECTOR] Detected ${detectedBiases.length} bias types using configuration:`);
|
||||||
|
detectedBiases.forEach(bias => {
|
||||||
|
console.log(` - ${bias.biasType}: ${(bias.severity * 100).toFixed(1)}% severity`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('[BIAS DETECTOR] No bias detected using configured thresholds');
|
||||||
|
}
|
||||||
|
|
||||||
|
return biasResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateBiasRiskScore(biasResults: BiasAnalysisResult[]): number {
|
||||||
|
if (biasResults.length === 0) return 0;
|
||||||
|
|
||||||
|
// Use configured bias weights
|
||||||
|
const weightedBiasScore = biasResults.reduce((total, bias) => {
|
||||||
|
if (bias.detected) {
|
||||||
|
const weight = this.config.weights[bias.biasType] || 0.2;
|
||||||
|
return total + (bias.severity * weight);
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return Math.min(1, weightedBiasScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
suggestBiasMitigation(biasResults: BiasAnalysisResult[]): string[] {
|
||||||
|
const suggestions: string[] = [];
|
||||||
|
const detectedBiases = biasResults.filter(bias => bias.detected);
|
||||||
|
|
||||||
|
if (detectedBiases.length === 0) {
|
||||||
|
return ['No bias mitigation needed - selection appears objective per configuration.'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use configured mitigation strategies
|
||||||
|
detectedBiases.forEach(bias => {
|
||||||
|
if (bias.mitigation) {
|
||||||
|
suggestions.push(`${bias.biasType.toUpperCase()}: ${bias.mitigation}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add configured general strategies
|
||||||
|
if (detectedBiases.length > 2) {
|
||||||
|
suggestions.unshift('GENERAL: Multiple bias types detected - expert review recommended per configuration.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return suggestions.slice(0, 5); // Limit to top 5 suggestions
|
||||||
|
}
|
||||||
|
|
||||||
|
getBiasThresholds(): any {
|
||||||
|
return { ...this.config.thresholds };
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaselineStats(): any {
|
||||||
|
if (!this.baseline) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
toolCount: this.baseline.toolPopularity.size,
|
||||||
|
sampleSize: this.baseline.sampleSize,
|
||||||
|
lastUpdated: this.baseline.lastUpdated,
|
||||||
|
domains: Array.from(this.baseline.domainDistribution.entries()),
|
||||||
|
skillLevels: Array.from(this.baseline.skillLevelDistribution.entries()),
|
||||||
|
configuration: {
|
||||||
|
enabled: this.config.enabled,
|
||||||
|
patterns: {
|
||||||
|
overselectedToolsCount: this.config.patterns.commonlyOverselectedTools.length,
|
||||||
|
indicatorTypesCount: Object.keys(this.config.patterns).length
|
||||||
|
},
|
||||||
|
thresholds: this.config.thresholds
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
isHighBiasRisk(biasRiskScore: number): boolean {
|
||||||
|
return biasRiskScore > this.config.thresholds.alertLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigSummary(): any {
|
||||||
|
return {
|
||||||
|
enabled: this.config.enabled,
|
||||||
|
thresholdCount: Object.keys(this.config.thresholds).length,
|
||||||
|
patternCount: Object.keys(this.config.patterns).length,
|
||||||
|
distributionCount: Object.keys(this.config.distributions).length,
|
||||||
|
weightCount: Object.keys(this.config.weights).length,
|
||||||
|
configuration: 'fully-configurable-zero-hardcoded-values'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export singleton instance
|
||||||
|
export const biasDetector = new ConfigurableBiasDetector();
|
||||||
|
export type { BiasAnalysisResult, ToolPopularityMetrics, BiasBaselineData };
|
@ -30,6 +30,49 @@ interface ForensicThresholds {
|
|||||||
rateLimitMaxRequests: number;
|
rateLimitMaxRequests: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BiasDetectionConfig {
|
||||||
|
enabled: boolean;
|
||||||
|
thresholds: {
|
||||||
|
popularity: number;
|
||||||
|
availability: number;
|
||||||
|
recency: number;
|
||||||
|
diversityMinimum: number;
|
||||||
|
domainConcentration: number;
|
||||||
|
skillLevel: number;
|
||||||
|
statisticalSignificance: number;
|
||||||
|
confidenceMinimum: number;
|
||||||
|
alertLevel: number;
|
||||||
|
};
|
||||||
|
baseline: {
|
||||||
|
updateMinSamples: number;
|
||||||
|
updateDays: number;
|
||||||
|
defaultSelectionFrequency: number;
|
||||||
|
biasDetectionSeverity: number;
|
||||||
|
};
|
||||||
|
patterns: {
|
||||||
|
commonlyOverselectedTools: string[];
|
||||||
|
recentToolIndicators: string[];
|
||||||
|
easyRecallIndicators: string[];
|
||||||
|
commonToolResponses: string[];
|
||||||
|
urgencyIndicators: string[];
|
||||||
|
noviceIndicators: string[];
|
||||||
|
expertIndicators: string[];
|
||||||
|
};
|
||||||
|
distributions: {
|
||||||
|
domain: Record<string, number>;
|
||||||
|
skillLevel: Record<string, number>;
|
||||||
|
toolType: Record<string, number>;
|
||||||
|
platform: Record<string, number>;
|
||||||
|
};
|
||||||
|
weights: {
|
||||||
|
popularity: number;
|
||||||
|
availability: number;
|
||||||
|
domainConcentration: number;
|
||||||
|
skillLevel: number;
|
||||||
|
recency: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface ForensicConfig {
|
interface ForensicConfig {
|
||||||
// AI Models Configuration
|
// AI Models Configuration
|
||||||
aiModels: {
|
aiModels: {
|
||||||
@ -58,6 +101,9 @@ interface ForensicConfig {
|
|||||||
// All configurable thresholds
|
// All configurable thresholds
|
||||||
thresholds: ForensicThresholds;
|
thresholds: ForensicThresholds;
|
||||||
|
|
||||||
|
// ENHANCED: Comprehensive bias detection configuration
|
||||||
|
biasDetection: BiasDetectionConfig;
|
||||||
|
|
||||||
// Queue and Performance
|
// Queue and Performance
|
||||||
queue: {
|
queue: {
|
||||||
maxSize: number;
|
maxSize: number;
|
||||||
@ -129,6 +175,27 @@ class ForensicConfigManager {
|
|||||||
return value.toLowerCase() === 'true';
|
return value.toLowerCase() === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseDistribution(envKey: string, defaultValue: string): Record<string, number> {
|
||||||
|
const distributionString = this.getEnv(envKey, defaultValue);
|
||||||
|
const distribution: Record<string, number> = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
distributionString.split(',').forEach(pair => {
|
||||||
|
const [key, valueStr] = pair.split(':');
|
||||||
|
if (key && valueStr) {
|
||||||
|
const value = parseFloat(valueStr);
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
distribution[key.trim()] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`[CONFIG] Failed to parse distribution ${envKey}, using defaults`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return distribution;
|
||||||
|
}
|
||||||
|
|
||||||
private loadConfig(): ForensicConfig {
|
private loadConfig(): ForensicConfig {
|
||||||
// Strategic AI Model Configuration
|
// Strategic AI Model Configuration
|
||||||
const strategicModel: AIModelConfig = {
|
const strategicModel: AIModelConfig = {
|
||||||
@ -163,6 +230,69 @@ class ForensicConfigManager {
|
|||||||
purpose: 'tactical'
|
purpose: 'tactical'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ENHANCED: Comprehensive bias detection configuration - ALL from env/config
|
||||||
|
const biasDetectionConfig: BiasDetectionConfig = {
|
||||||
|
enabled: this.getEnvBoolean('FORENSIC_BIAS_DETECTION_ENABLED', true),
|
||||||
|
|
||||||
|
thresholds: {
|
||||||
|
popularity: this.getEnvFloat('TOOL_POPULARITY_BIAS_THRESHOLD', 0.75),
|
||||||
|
availability: this.getEnvFloat('AVAILABILITY_BIAS_THRESHOLD', 0.7),
|
||||||
|
recency: this.getEnvFloat('RECENCY_BIAS_THRESHOLD', 0.6),
|
||||||
|
diversityMinimum: this.getEnvFloat('DIVERSITY_MINIMUM_THRESHOLD', 0.4),
|
||||||
|
domainConcentration: this.getEnvFloat('DOMAIN_CONCENTRATION_THRESHOLD', 0.8),
|
||||||
|
skillLevel: this.getEnvFloat('SKILL_LEVEL_BIAS_THRESHOLD', 0.7),
|
||||||
|
statisticalSignificance: this.getEnvFloat('BIAS_STATISTICAL_SIGNIFICANCE_THRESHOLD', 0.05),
|
||||||
|
confidenceMinimum: this.getEnvFloat('BIAS_CONFIDENCE_MINIMUM', 0.6),
|
||||||
|
alertLevel: this.getEnvFloat('AI_BIAS_ALERT_THRESHOLD', 0.8)
|
||||||
|
},
|
||||||
|
|
||||||
|
baseline: {
|
||||||
|
updateMinSamples: this.getEnvNumber('BIAS_BASELINE_MIN_SAMPLES', 5),
|
||||||
|
updateDays: this.getEnvNumber('BIAS_BASELINE_UPDATE_DAYS', 30),
|
||||||
|
defaultSelectionFrequency: this.getEnvFloat('BIAS_DEFAULT_SELECTION_FREQUENCY', 0.4),
|
||||||
|
biasDetectionSeverity: this.getEnvFloat('BIAS_DETECTION_SEVERITY_THRESHOLD', 0.3)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Load pattern arrays from environment (comma-separated)
|
||||||
|
patterns: {
|
||||||
|
commonlyOverselectedTools: this.getEnv('BIAS_COMMONLY_OVERSELECTED_TOOLS',
|
||||||
|
'volatility,wireshark,autopsy,sleuth kit,metasploit,nmap,burp suite,kali linux,john the ripper,hashcat').split(',').map(s => s.trim()).filter(s => s),
|
||||||
|
recentToolIndicators: this.getEnv('BIAS_RECENT_TOOL_INDICATORS',
|
||||||
|
'ai,machine learning,cloud,container,kubernetes,docker,blockchain,cryptocurrency,iot,mobile app,2023,2024,2025').split(',').map(s => s.trim()).filter(s => s),
|
||||||
|
easyRecallIndicators: this.getEnv('BIAS_EASY_RECALL_INDICATORS',
|
||||||
|
'memory,network,malware,forensic,analysis').split(',').map(s => s.trim()).filter(s => s),
|
||||||
|
commonToolResponses: this.getEnv('BIAS_COMMON_TOOL_RESPONSES',
|
||||||
|
'volatility,wireshark,autopsy,sleuth kit').split(',').map(s => s.trim()).filter(s => s),
|
||||||
|
urgencyIndicators: this.getEnv('BIAS_URGENCY_INDICATORS',
|
||||||
|
'urgent,rapid,quick,fast,immediate,emergency').split(',').map(s => s.trim()).filter(s => s),
|
||||||
|
noviceIndicators: this.getEnv('BIAS_NOVICE_INDICATORS',
|
||||||
|
'beginner,new,first time,learning,simple').split(',').map(s => s.trim()).filter(s => s),
|
||||||
|
expertIndicators: this.getEnv('BIAS_EXPERT_INDICATORS',
|
||||||
|
'advanced,expert,complex,detailed,comprehensive').split(',').map(s => s.trim()).filter(s => s)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Load distribution baselines from environment (colon-separated key:value pairs)
|
||||||
|
distributions: {
|
||||||
|
domain: this.parseDistribution('BIAS_DOMAIN_DISTRIBUTION',
|
||||||
|
'general:0.3,malware-analysis:0.2,network-forensics:0.15,mobile-forensics:0.1,memory-forensics:0.1,database-forensics:0.05,cloud-forensics:0.05,ics-scada:0.05'),
|
||||||
|
skillLevel: this.parseDistribution('BIAS_SKILL_LEVEL_DISTRIBUTION',
|
||||||
|
'novice:0.15,beginner:0.25,intermediate:0.35,advanced:0.2,expert:0.05'),
|
||||||
|
toolType: this.parseDistribution('BIAS_TOOL_TYPE_DISTRIBUTION',
|
||||||
|
'software:0.7,method:0.25,concept:0.05'),
|
||||||
|
platform: this.parseDistribution('BIAS_PLATFORM_DISTRIBUTION',
|
||||||
|
'Windows:0.4,Linux:0.3,Cross-platform:0.2,macOS:0.05,Mobile:0.05')
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bias impact weights for overall risk calculation
|
||||||
|
weights: {
|
||||||
|
popularity: this.getEnvFloat('BIAS_WEIGHT_POPULARITY', 0.3),
|
||||||
|
availability: this.getEnvFloat('BIAS_WEIGHT_AVAILABILITY', 0.25),
|
||||||
|
domainConcentration: this.getEnvFloat('BIAS_WEIGHT_DOMAIN_CONCENTRATION', 0.2),
|
||||||
|
skillLevel: this.getEnvFloat('BIAS_WEIGHT_SKILL_LEVEL', 0.15),
|
||||||
|
recency: this.getEnvFloat('BIAS_WEIGHT_RECENCY', 0.1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
aiModels: {
|
aiModels: {
|
||||||
strategic: strategicModel,
|
strategic: strategicModel,
|
||||||
@ -199,6 +329,9 @@ class ForensicConfigManager {
|
|||||||
rateLimitMaxRequests: this.getEnvNumber('AI_RATE_LIMIT_MAX_REQUESTS', 6)
|
rateLimitMaxRequests: this.getEnvNumber('AI_RATE_LIMIT_MAX_REQUESTS', 6)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// NEW: Complete bias detection configuration
|
||||||
|
biasDetection: biasDetectionConfig,
|
||||||
|
|
||||||
queue: {
|
queue: {
|
||||||
maxSize: this.getEnvNumber('AI_QUEUE_MAX_SIZE', 50),
|
maxSize: this.getEnvNumber('AI_QUEUE_MAX_SIZE', 50),
|
||||||
cleanupIntervalMs: this.getEnvNumber('AI_QUEUE_CLEANUP_INTERVAL_MS', 300000)
|
cleanupIntervalMs: this.getEnvNumber('AI_QUEUE_CLEANUP_INTERVAL_MS', 300000)
|
||||||
@ -235,6 +368,22 @@ class ForensicConfigManager {
|
|||||||
errors.push('Confidence threshold must be between 0 and 1');
|
errors.push('Confidence threshold must be between 0 and 1');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate bias detection configuration
|
||||||
|
if (this.config.biasDetection.enabled) {
|
||||||
|
const bt = this.config.biasDetection.thresholds;
|
||||||
|
Object.entries(bt).forEach(([key, value]) => {
|
||||||
|
if (typeof value === 'number' && (value < 0 || value > 1)) {
|
||||||
|
errors.push(`Bias threshold ${key} must be between 0 and 1`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate bias weights sum to approximately 1.0
|
||||||
|
const weightSum = Object.values(this.config.biasDetection.weights).reduce((sum, weight) => sum + weight, 0);
|
||||||
|
if (Math.abs(weightSum - 1.0) > 0.01) {
|
||||||
|
console.warn(`[CONFIG] Bias weights sum to ${weightSum.toFixed(3)}, should sum to 1.0`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
throw new Error(`Configuration validation failed:\n${errors.join('\n')}`);
|
throw new Error(`Configuration validation failed:\n${errors.join('\n')}`);
|
||||||
}
|
}
|
||||||
@ -245,6 +394,11 @@ class ForensicConfigManager {
|
|||||||
console.log(`[FORENSIC CONFIG] Audit Trail: ${this.config.auditTrail.enabled ? 'Enabled' : 'Disabled'}`);
|
console.log(`[FORENSIC CONFIG] Audit Trail: ${this.config.auditTrail.enabled ? 'Enabled' : 'Disabled'}`);
|
||||||
console.log(`[FORENSIC CONFIG] Confidence Scoring: ${this.config.features.confidenceScoring ? 'Enabled' : 'Disabled'}`);
|
console.log(`[FORENSIC CONFIG] Confidence Scoring: ${this.config.features.confidenceScoring ? 'Enabled' : 'Disabled'}`);
|
||||||
console.log(`[FORENSIC CONFIG] Bias Detection: ${this.config.features.biasDetection ? 'Enabled' : 'Disabled'}`);
|
console.log(`[FORENSIC CONFIG] Bias Detection: ${this.config.features.biasDetection ? 'Enabled' : 'Disabled'}`);
|
||||||
|
|
||||||
|
if (this.config.biasDetection.enabled) {
|
||||||
|
console.log(`[FORENSIC CONFIG] Bias Patterns: ${this.config.biasDetection.patterns.commonlyOverselectedTools.length} overselected tools configured`);
|
||||||
|
console.log(`[FORENSIC CONFIG] Bias Distributions: ${Object.keys(this.config.biasDetection.distributions).length} distribution types configured`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public access methods
|
// Public access methods
|
||||||
@ -264,6 +418,10 @@ class ForensicConfigManager {
|
|||||||
return { ...this.config.thresholds };
|
return { ...this.config.thresholds };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBiasDetectionConfig(): BiasDetectionConfig {
|
||||||
|
return { ...this.config.biasDetection };
|
||||||
|
}
|
||||||
|
|
||||||
isFeatureEnabled(feature: keyof ForensicConfig['features']): boolean {
|
isFeatureEnabled(feature: keyof ForensicConfig['features']): boolean {
|
||||||
return this.config.features[feature];
|
return this.config.features[feature];
|
||||||
}
|
}
|
||||||
@ -298,4 +456,4 @@ class ForensicConfigManager {
|
|||||||
|
|
||||||
// Export singleton instance
|
// Export singleton instance
|
||||||
export const forensicConfig = ForensicConfigManager.getInstance();
|
export const forensicConfig = ForensicConfigManager.getInstance();
|
||||||
export type { ForensicConfig, AIModelConfig, ForensicThresholds };
|
export type { ForensicConfig, AIModelConfig, ForensicThresholds, BiasDetectionConfig };
|
Loading…
x
Reference in New Issue
Block a user