first iteration - buggy
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// src/utils/auditService.ts
|
||||
// src/utils/auditService.ts - Refactored
|
||||
import 'dotenv/config';
|
||||
|
||||
function env(key: string, fallback: string | undefined = undefined): string | undefined {
|
||||
@@ -11,7 +11,6 @@ function env(key: string, fallback: string | undefined = undefined): string | un
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// CONSOLIDATED AUDIT INTERFACES - Single source of truth
|
||||
export interface AuditEntry {
|
||||
timestamp: number;
|
||||
phase: string;
|
||||
@@ -30,63 +29,9 @@ interface AuditConfig {
|
||||
maxEntries: number;
|
||||
}
|
||||
|
||||
interface CompressedAuditEntry {
|
||||
timestamp: number;
|
||||
phase: string;
|
||||
action: string;
|
||||
inputSummary: string;
|
||||
outputSummary: string;
|
||||
confidence: number;
|
||||
processingTimeMs: number;
|
||||
metadata: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ProcessedAuditTrail {
|
||||
totalTime: number;
|
||||
avgConfidence: number;
|
||||
stepCount: number;
|
||||
highConfidenceSteps: number;
|
||||
lowConfidenceSteps: number;
|
||||
phases: Array<{
|
||||
name: string;
|
||||
icon: string;
|
||||
displayName: string;
|
||||
avgConfidence: number;
|
||||
totalTime: number;
|
||||
entries: CompressedAuditEntry[];
|
||||
}>;
|
||||
summary: {
|
||||
analysisQuality: 'excellent' | 'good' | 'fair' | 'poor';
|
||||
keyInsights: string[];
|
||||
potentialIssues: string[];
|
||||
};
|
||||
}
|
||||
|
||||
class AuditService {
|
||||
private config: AuditConfig;
|
||||
private activeAuditTrail: AuditEntry[] = [];
|
||||
|
||||
private readonly phaseConfig = {
|
||||
'initialization': { icon: '🚀', displayName: 'Initialisierung' },
|
||||
'retrieval': { icon: '🔍', displayName: 'Datensuche' },
|
||||
'selection': { icon: '🎯', displayName: 'Tool-Auswahl' },
|
||||
'micro-task': { icon: '⚡', displayName: 'Detail-Analyse' },
|
||||
'validation': { icon: '✓', displayName: 'Validierung' },
|
||||
'completion': { icon: '✅', displayName: 'Finalisierung' }
|
||||
};
|
||||
|
||||
private readonly actionTranslations = {
|
||||
'pipeline-start': 'Analyse gestartet',
|
||||
'embeddings-search': 'Ähnliche Tools gesucht',
|
||||
'ai-tool-selection': 'Tools automatisch ausgewählt',
|
||||
'ai-analysis': 'KI-Analyse durchgeführt',
|
||||
'phase-tool-selection': 'Phasen-Tools evaluiert',
|
||||
'tool-evaluation': 'Tool-Bewertung erstellt',
|
||||
'background-knowledge-selection': 'Hintergrundwissen ausgewählt',
|
||||
'confidence-scoring': 'Vertrauenswertung berechnet',
|
||||
'phase-completion': 'Phasenergänzung durchgeführt',
|
||||
'pipeline-end': 'Analyse abgeschlossen'
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.config = this.loadConfig();
|
||||
@@ -110,7 +55,6 @@ class AuditService {
|
||||
};
|
||||
}
|
||||
|
||||
// CONSOLIDATED AUDIT ENTRY CREATION - Single method for all audit operations
|
||||
addEntry(
|
||||
phase: string,
|
||||
action: string,
|
||||
@@ -134,15 +78,19 @@ class AuditService {
|
||||
};
|
||||
|
||||
this.activeAuditTrail.push(entry);
|
||||
|
||||
// Enforce max entries limit
|
||||
if (this.activeAuditTrail.length > this.config.maxEntries) {
|
||||
this.activeAuditTrail.shift();
|
||||
}
|
||||
|
||||
console.log(`[AUDIT-SERVICE] ${phase}/${action}: ${confidence}% confidence, ${entry.processingTimeMs}ms`);
|
||||
}
|
||||
|
||||
// GET CURRENT AUDIT TRAIL - For integration with AI pipeline
|
||||
getCurrentAuditTrail(): AuditEntry[] {
|
||||
return [...this.activeAuditTrail];
|
||||
}
|
||||
|
||||
// CLEAR AUDIT TRAIL - Start fresh for new analysis
|
||||
clearAuditTrail(): void {
|
||||
if (this.activeAuditTrail.length > 0) {
|
||||
console.log(`[AUDIT-SERVICE] Cleared ${this.activeAuditTrail.length} audit entries`);
|
||||
@@ -150,7 +98,6 @@ class AuditService {
|
||||
}
|
||||
}
|
||||
|
||||
// FINALIZE AUDIT TRAIL - Complete analysis and return final trail
|
||||
finalizeAuditTrail(): AuditEntry[] {
|
||||
const finalTrail = [...this.activeAuditTrail];
|
||||
console.log(`[AUDIT-SERVICE] Finalized audit trail with ${finalTrail.length} entries`);
|
||||
@@ -158,102 +105,6 @@ class AuditService {
|
||||
return finalTrail;
|
||||
}
|
||||
|
||||
processAuditTrail(rawAuditTrail: AuditEntry[]): ProcessedAuditTrail | null {
|
||||
if (!this.config.enabled) {
|
||||
console.log('[AUDIT-SERVICE] Processing disabled');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!rawAuditTrail || !Array.isArray(rawAuditTrail) || rawAuditTrail.length === 0) {
|
||||
console.log('[AUDIT-SERVICE] No audit trail data to process');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`[AUDIT-SERVICE] Processing ${rawAuditTrail.length} audit entries`);
|
||||
|
||||
const totalTime = rawAuditTrail.reduce((sum, entry) => sum + (entry.processingTimeMs || 0), 0);
|
||||
const validConfidenceEntries = rawAuditTrail.filter(entry => typeof entry.confidence === 'number');
|
||||
const avgConfidence = validConfidenceEntries.length > 0
|
||||
? Math.round(validConfidenceEntries.reduce((sum, entry) => sum + entry.confidence, 0) / validConfidenceEntries.length)
|
||||
: 0;
|
||||
|
||||
const highConfidenceSteps = rawAuditTrail.filter(entry => (entry.confidence || 0) >= 80).length;
|
||||
const lowConfidenceSteps = rawAuditTrail.filter(entry => (entry.confidence || 0) < 60).length;
|
||||
|
||||
const groupedEntries = rawAuditTrail.reduce((groups, entry) => {
|
||||
const phase = entry.phase || 'unknown';
|
||||
if (!groups[phase]) groups[phase] = [];
|
||||
groups[phase].push(entry);
|
||||
return groups;
|
||||
}, {} as Record<string, AuditEntry[]>);
|
||||
|
||||
const phases = Object.entries(groupedEntries).map(([phase, entries]) => {
|
||||
const phaseConfig = this.phaseConfig[phase] || { icon: '📋', displayName: phase };
|
||||
const validEntries = entries.filter(entry => entry && typeof entry === 'object');
|
||||
|
||||
const phaseAvgConfidence = validEntries.length > 0
|
||||
? Math.round(validEntries.reduce((sum, entry) => sum + (entry.confidence || 0), 0) / validEntries.length)
|
||||
: 0;
|
||||
|
||||
const phaseTotalTime = validEntries.reduce((sum, entry) => sum + (entry.processingTimeMs || 0), 0);
|
||||
|
||||
return {
|
||||
name: phase,
|
||||
icon: phaseConfig.icon,
|
||||
displayName: phaseConfig.displayName,
|
||||
avgConfidence: phaseAvgConfidence,
|
||||
totalTime: phaseTotalTime,
|
||||
entries: validEntries
|
||||
.map(e => this.compressEntry(e))
|
||||
.filter((e): e is CompressedAuditEntry => e !== null)
|
||||
};
|
||||
}).filter(phase => phase.entries.length > 0);
|
||||
|
||||
const summary = this.generateSummary(rawAuditTrail, avgConfidence, lowConfidenceSteps);
|
||||
|
||||
const result: ProcessedAuditTrail = {
|
||||
totalTime,
|
||||
avgConfidence,
|
||||
stepCount: rawAuditTrail.length,
|
||||
highConfidenceSteps,
|
||||
lowConfidenceSteps,
|
||||
phases,
|
||||
summary
|
||||
};
|
||||
|
||||
console.log(`[AUDIT-SERVICE] Successfully processed audit trail: ${result.phases.length} phases, ${result.avgConfidence}% avg confidence`);
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
console.error('[AUDIT-SERVICE] Error processing audit trail:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private compressEntry(entry: AuditEntry): CompressedAuditEntry | null {
|
||||
if (!entry || typeof entry !== 'object') {
|
||||
console.warn('[AUDIT-SERVICE] Invalid audit entry skipped');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return {
|
||||
timestamp: entry.timestamp || Date.now(),
|
||||
phase: entry.phase || 'unknown',
|
||||
action: entry.action || 'unknown',
|
||||
inputSummary: this.summarizeData(entry.input),
|
||||
outputSummary: this.summarizeData(entry.output),
|
||||
confidence: entry.confidence || 0,
|
||||
processingTimeMs: entry.processingTimeMs || 0,
|
||||
metadata: entry.metadata || {}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[AUDIT-SERVICE] Error compressing entry:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private compressData(data: any): any {
|
||||
if (this.config.detailLevel === 'verbose') {
|
||||
return data;
|
||||
@@ -264,30 +115,6 @@ class AuditService {
|
||||
}
|
||||
}
|
||||
|
||||
private summarizeData(data: any): string {
|
||||
if (data === null || data === undefined) return 'null';
|
||||
if (typeof data === 'string') {
|
||||
return data.length > 100 ? data.slice(0, 100) + '...' : data;
|
||||
}
|
||||
if (typeof data === 'number' || typeof data === 'boolean') {
|
||||
return data.toString();
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
if (data.length === 0) return '[]';
|
||||
if (data.length <= 3) return JSON.stringify(data);
|
||||
return `[${data.slice(0, 3).map(i => typeof i === 'string' ? i : JSON.stringify(i)).join(', ')}, ...+${data.length - 3}]`;
|
||||
}
|
||||
if (typeof data === 'object') {
|
||||
const keys = Object.keys(data);
|
||||
if (keys.length === 0) return '{}';
|
||||
if (keys.length <= 3) {
|
||||
return '{' + keys.map(k => `${k}: ${typeof data[k] === 'string' ? data[k].slice(0, 20) + (data[k].length > 20 ? '...' : '') : JSON.stringify(data[k])}`).join(', ') + '}';
|
||||
}
|
||||
return `{${keys.slice(0, 3).join(', ')}, ...+${keys.length - 3} keys}`;
|
||||
}
|
||||
return String(data);
|
||||
}
|
||||
|
||||
private summarizeForStorage(data: any): any {
|
||||
if (typeof data === 'string' && data.length > 500) {
|
||||
return data.slice(0, 500) + '...[truncated]';
|
||||
@@ -308,71 +135,6 @@ class AuditService {
|
||||
return data;
|
||||
}
|
||||
|
||||
private generateSummary(entries: AuditEntry[], avgConfidence: number, lowConfidenceSteps: number): {
|
||||
analysisQuality: 'excellent' | 'good' | 'fair' | 'poor';
|
||||
keyInsights: string[];
|
||||
potentialIssues: string[];
|
||||
} {
|
||||
let analysisQuality: 'excellent' | 'good' | 'fair' | 'poor';
|
||||
if (avgConfidence >= 85 && lowConfidenceSteps === 0) {
|
||||
analysisQuality = 'excellent';
|
||||
} else if (avgConfidence >= 70 && lowConfidenceSteps <= 1) {
|
||||
analysisQuality = 'good';
|
||||
} else if (avgConfidence >= 60 && lowConfidenceSteps <= 3) {
|
||||
analysisQuality = 'fair';
|
||||
} else {
|
||||
analysisQuality = 'poor';
|
||||
}
|
||||
|
||||
const keyInsights: string[] = [];
|
||||
const embeddingsUsed = entries.some(e => e.action === 'embeddings-search');
|
||||
if (embeddingsUsed) {
|
||||
keyInsights.push('Semantische Suche wurde erfolgreich eingesetzt');
|
||||
}
|
||||
|
||||
const toolSelectionEntries = entries.filter(e => e.action === 'ai-tool-selection');
|
||||
if (toolSelectionEntries.length > 0) {
|
||||
const avgSelectionConfidence = toolSelectionEntries.reduce((sum, e) => sum + e.confidence, 0) / toolSelectionEntries.length;
|
||||
if (avgSelectionConfidence >= 80) {
|
||||
keyInsights.push('Hohe Konfidenz bei der Tool-Auswahl');
|
||||
}
|
||||
}
|
||||
|
||||
const potentialIssues: string[] = [];
|
||||
if (lowConfidenceSteps > 2) {
|
||||
potentialIssues.push(`${lowConfidenceSteps} Analyseschritte mit niedriger Konfidenz`);
|
||||
}
|
||||
|
||||
const longSteps = entries.filter(e => e.processingTimeMs > 5000);
|
||||
if (longSteps.length > 0) {
|
||||
potentialIssues.push(`${longSteps.length} Schritte benötigten mehr als 5 Sekunden`);
|
||||
}
|
||||
|
||||
return {
|
||||
analysisQuality,
|
||||
keyInsights,
|
||||
potentialIssues
|
||||
};
|
||||
}
|
||||
|
||||
getActionDisplayName(action: string): string {
|
||||
return this.actionTranslations[action] || action;
|
||||
}
|
||||
|
||||
formatDuration(ms: number): string {
|
||||
if (ms < 1000) return '< 1s';
|
||||
if (ms < 60000) return `${Math.ceil(ms / 1000)}s`;
|
||||
const minutes = Math.floor(ms / 60000);
|
||||
const seconds = Math.ceil((ms % 60000) / 1000);
|
||||
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
||||
}
|
||||
|
||||
getConfidenceColor(confidence: number): string {
|
||||
if (confidence >= 80) return 'var(--color-accent)';
|
||||
if (confidence >= 60) return 'var(--color-warning)';
|
||||
return 'var(--color-error)';
|
||||
}
|
||||
|
||||
isEnabled(): boolean {
|
||||
return this.config.enabled;
|
||||
}
|
||||
@@ -380,7 +142,122 @@ class AuditService {
|
||||
getConfig(): AuditConfig {
|
||||
return { ...this.config };
|
||||
}
|
||||
|
||||
// Statistics and analysis methods
|
||||
getAuditStatistics(auditTrail: AuditEntry[]): {
|
||||
totalTime: number;
|
||||
avgConfidence: number;
|
||||
stepCount: number;
|
||||
highConfidenceSteps: number;
|
||||
lowConfidenceSteps: number;
|
||||
phaseBreakdown: Record<string, { count: number; avgConfidence: number; totalTime: number }>;
|
||||
} {
|
||||
if (!auditTrail || auditTrail.length === 0) {
|
||||
return {
|
||||
totalTime: 0,
|
||||
avgConfidence: 0,
|
||||
stepCount: 0,
|
||||
highConfidenceSteps: 0,
|
||||
lowConfidenceSteps: 0,
|
||||
phaseBreakdown: {}
|
||||
};
|
||||
}
|
||||
|
||||
const totalTime = auditTrail.reduce((sum, entry) => sum + (entry.processingTimeMs || 0), 0);
|
||||
const validConfidenceEntries = auditTrail.filter(entry => typeof entry.confidence === 'number');
|
||||
const avgConfidence = validConfidenceEntries.length > 0
|
||||
? Math.round(validConfidenceEntries.reduce((sum, entry) => sum + entry.confidence, 0) / validConfidenceEntries.length)
|
||||
: 0;
|
||||
|
||||
const highConfidenceSteps = auditTrail.filter(entry => (entry.confidence || 0) >= 80).length;
|
||||
const lowConfidenceSteps = auditTrail.filter(entry => (entry.confidence || 0) < 60).length;
|
||||
|
||||
// Phase breakdown
|
||||
const phaseBreakdown: Record<string, { count: number; avgConfidence: number; totalTime: number }> = {};
|
||||
|
||||
auditTrail.forEach(entry => {
|
||||
const phase = entry.phase || 'unknown';
|
||||
if (!phaseBreakdown[phase]) {
|
||||
phaseBreakdown[phase] = { count: 0, avgConfidence: 0, totalTime: 0 };
|
||||
}
|
||||
|
||||
phaseBreakdown[phase].count++;
|
||||
phaseBreakdown[phase].totalTime += entry.processingTimeMs || 0;
|
||||
});
|
||||
|
||||
// Calculate average confidence per phase
|
||||
Object.keys(phaseBreakdown).forEach(phase => {
|
||||
const phaseEntries = auditTrail.filter(entry => entry.phase === phase);
|
||||
const validEntries = phaseEntries.filter(entry => typeof entry.confidence === 'number');
|
||||
|
||||
if (validEntries.length > 0) {
|
||||
phaseBreakdown[phase].avgConfidence = Math.round(
|
||||
validEntries.reduce((sum, entry) => sum + entry.confidence, 0) / validEntries.length
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
totalTime,
|
||||
avgConfidence,
|
||||
stepCount: auditTrail.length,
|
||||
highConfidenceSteps,
|
||||
lowConfidenceSteps,
|
||||
phaseBreakdown
|
||||
};
|
||||
}
|
||||
|
||||
validateAuditTrail(auditTrail: AuditEntry[]): {
|
||||
isValid: boolean;
|
||||
issues: string[];
|
||||
warnings: string[];
|
||||
} {
|
||||
const issues: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
if (!Array.isArray(auditTrail)) {
|
||||
issues.push('Audit trail is not an array');
|
||||
return { isValid: false, issues, warnings };
|
||||
}
|
||||
|
||||
if (auditTrail.length === 0) {
|
||||
warnings.push('Audit trail is empty');
|
||||
}
|
||||
|
||||
auditTrail.forEach((entry, index) => {
|
||||
if (!entry || typeof entry !== 'object') {
|
||||
issues.push(`Entry ${index} is not a valid object`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Required fields validation
|
||||
const requiredFields = ['timestamp', 'phase', 'action'];
|
||||
requiredFields.forEach(field => {
|
||||
if (!(field in entry)) {
|
||||
issues.push(`Entry ${index} missing required field: ${field}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Data type validation
|
||||
if (typeof entry.confidence !== 'number' || entry.confidence < 0 || entry.confidence > 100) {
|
||||
warnings.push(`Entry ${index} has invalid confidence value: ${entry.confidence}`);
|
||||
}
|
||||
|
||||
if (typeof entry.processingTimeMs !== 'number' || entry.processingTimeMs < 0) {
|
||||
warnings.push(`Entry ${index} has invalid processing time: ${entry.processingTimeMs}`);
|
||||
}
|
||||
|
||||
if (typeof entry.timestamp !== 'number' || entry.timestamp <= 0) {
|
||||
issues.push(`Entry ${index} has invalid timestamp: ${entry.timestamp}`);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
isValid: issues.length === 0,
|
||||
issues,
|
||||
warnings
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const auditService = new AuditService();
|
||||
export type { CompressedAuditEntry };
|
||||
export const auditService = new AuditService();
|
||||
Reference in New Issue
Block a user