performance-tracker.ts•31.8 kB
/**
* Performance Tracker Service
* Comprehensive metric collection, analysis, and reporting for GEPA system
*/
import { EventEmitter } from 'events';
// Core Types and Interfaces
export interface PerformanceMetricData {
[key: string]: unknown;
}
// Utility type for ensuring objects can be converted to PerformanceMetricData
export type ToPerformanceMetricData<T> = T & PerformanceMetricData;
export interface PerformanceMetric {
id: string;
name: string;
category: string;
timestamp: number;
duration?: number;
data: PerformanceMetricData;
tags?: Record<string, string>;
}
export interface MetricCollectionConfig {
category: string;
tags?: Record<string, string>;
}
export interface StatisticalAnalysis {
mean: number;
median: number;
min: number;
max: number;
standardDeviation: number;
variance: number;
sampleSize: number;
percentiles: {
p25: number;
p50: number;
p75: number;
p90: number;
p95: number;
p99: number;
};
}
export interface TrendAnalysis {
trend: 'improving' | 'degrading' | 'stable';
slope: number;
confidence: number;
dataPoints: number;
timeWindow: number;
}
export interface PerformanceReportMetadata {
[key: string]: unknown;
}
export interface ReportData {
summary: {
totalMetrics: number;
timeRange: { start: number; end: number };
categories: string[];
};
metrics: PerformanceMetric[];
statistics?: Record<string, StatisticalAnalysis>;
trends?: Record<string, TrendAnalysis>;
insights?: string[];
}
export type FormattedReportData = ReportData | string;
export interface PerformanceReport {
format: 'json' | 'human-readable' | 'csv';
data: FormattedReportData;
generatedAt: number;
timeRange?: { start: number; end: number };
metadata?: PerformanceReportMetadata;
}
export interface MemoryMetrics {
rss: number;
heapTotal: number;
heapUsed: number;
external: number;
arrayBuffers: number;
}
export interface ExecutionMetrics {
startTime: number;
endTime?: number;
duration?: number;
category: string;
tags?: Record<string, string>;
}
export interface TokenUsageMetrics {
inputTokens: number;
outputTokens: number;
totalTokens: number;
cost: number;
model: string;
}
// Operation and Evolution Types
export interface OperationData {
[key: string]: string | number | boolean | null | undefined;
}
export interface OperationResult {
success: boolean;
data: OperationData;
}
export interface EvolutionCycleData {
id: string;
config: EvolutionCycleConfig;
startTime: number;
endTime?: number;
generations: EvolutionGenerationMetrics[];
status: 'running' | 'completed' | 'failed';
result?: EvolutionCycleResult;
}
export interface EvolutionMetrics {
evolutionId: string;
totalGenerations: number;
finalFitness: number;
convergenceAchieved: boolean;
fitnessImprovement: number;
duration: number;
}
export interface MutationEffectivenessAnalysis {
byType: Record<string, {
successRate: number;
averageFitnessChange: number;
totalOperations: number;
}>;
overall: {
successRate: number;
averageFitnessChange: number;
};
}
export interface EvolutionInsights {
evolutionId: string;
convergenceRate: number;
diversityTrend: 'increasing' | 'decreasing' | 'stable';
recommendedAdjustments: string[];
}
export interface InsightThresholds {
slowOperationThreshold?: number;
highMemoryThreshold?: number;
anomalyDetectionEnabled?: boolean;
}
// Evolution Engine Integration Types
export interface EvolutionCycleConfig {
initialPopulationSize: number;
targetFitness: number;
maxGenerations: number;
}
export interface EvolutionGenerationMetrics {
generation: number;
bestFitness: number;
averageFitness: number;
diversityScore: number;
mutationRate: number;
selectionPressure: number;
}
export interface EvolutionCycleResult {
finalFitness: number;
totalGenerations: number;
convergenceAchieved: boolean;
terminationReason: string;
}
export interface MutationResult {
success: boolean;
fitnessChange: number;
executionTime: number;
resourceUsage: {
memoryPeak: number;
cpuTime: number;
};
}
// Configuration Types
export interface PerformanceTrackerConfig {
enableRealTimeMonitoring?: boolean;
maxMetricsHistorySize?: number;
aggregationWindowSize?: number;
statisticalAnalysisEnabled?: boolean;
memoryTrackingEnabled?: boolean;
tokenUsageTrackingEnabled?: boolean;
timeProvider?: () => number;
memoryProvider?: () => NodeJS.MemoryUsage;
}
export interface ReportConfig {
timeRange?: { start: number; end: number };
categories?: string[];
includeStatistics?: boolean;
includeTrends?: boolean;
includeInsights?: boolean;
insightThresholds?: {
slowOperationThreshold?: number;
highMemoryThreshold?: number;
anomalyDetectionEnabled?: boolean;
};
}
export interface AggregationConfig {
windowSize: number;
aggregationFunction: 'average' | 'sum' | 'max' | 'min';
categories: string[];
}
// Analysis Types
export interface SuccessRateAnalysis {
successRate: number;
totalOperations: number;
successfulOperations: number;
failedOperations: number;
}
export interface AnomalyDetection {
metric: PerformanceMetric;
score: number;
threshold: number;
method: string;
}
export interface MovingAveragePoint {
timestamp: number;
value: number;
windowSize: number;
}
export interface MemoryAnalysis {
peakHeapUsed: number;
averageHeapUsed: number;
memoryGrowthRate: number;
potentialLeaks: boolean;
}
export interface LeakDetection {
leakDetected: boolean;
leakRate: number;
confidence: number;
recommendedActions: string[];
}
export interface GarbageCollectionEvent {
type: 'minor' | 'major';
duration: number;
heapBefore: number;
heapAfter: number;
}
export interface GarbageCollectionAnalysis {
totalEvents: number;
averageDuration: number;
totalMemoryReclaimed: number;
gcEfficiency: number;
}
// Active Metric Tracking
interface ActiveMetric {
id: string;
name: string;
category: string;
startTime: number;
tags?: Record<string, string>;
}
/**
* Main Performance Tracker Class
*/
export class PerformanceTracker extends EventEmitter {
private config: Required<PerformanceTrackerConfig>;
private metrics: PerformanceMetric[] = [];
private activeMetrics: Map<string, ActiveMetric> = new Map();
private operationResults: Map<string, Array<OperationResult>> = new Map();
private evolutionCycles: Map<string, EvolutionCycleData> = new Map();
private mutationResults: Map<string, MutationResult[]> = new Map();
private memorySnapshots: MemoryMetrics[] = [];
private gcEvents: GarbageCollectionEvent[] = [];
private aggregationConfig?: AggregationConfig;
private aggregatedMetrics: Map<string, PerformanceMetric[]> = new Map();
constructor(config: PerformanceTrackerConfig = {}) {
super();
this.config = {
enableRealTimeMonitoring: config.enableRealTimeMonitoring ?? true,
maxMetricsHistorySize: config.maxMetricsHistorySize ?? 10000,
aggregationWindowSize: config.aggregationWindowSize ?? 1000,
statisticalAnalysisEnabled: config.statisticalAnalysisEnabled ?? true,
memoryTrackingEnabled: config.memoryTrackingEnabled ?? true,
tokenUsageTrackingEnabled: config.tokenUsageTrackingEnabled ?? true,
timeProvider: config.timeProvider ?? (() => Date.now()),
memoryProvider: config.memoryProvider ?? (() => process.memoryUsage())
};
}
// Metric Collection Methods
startMetricCollection(name: string, config: MetricCollectionConfig): string {
const id = `metric_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const startTime = this.config.timeProvider();
const activeMetric: ActiveMetric = {
id,
name,
category: config.category,
startTime,
tags: config.tags || {}
};
this.activeMetrics.set(id, activeMetric);
return id;
}
endMetricCollection(metricId: string): PerformanceMetric {
const activeMetric = this.activeMetrics.get(metricId);
if (!activeMetric) {
throw new Error('Metric not found');
}
const endTime = this.config.timeProvider();
const duration = endTime - activeMetric.startTime;
const metric: PerformanceMetric = {
id: activeMetric.id,
name: activeMetric.name,
category: activeMetric.category,
timestamp: activeMetric.startTime,
duration,
data: {},
tags: activeMetric.tags || {}
};
this.recordMetric(metric);
this.activeMetrics.delete(metricId);
return metric;
}
recordMetric(metric: PerformanceMetric): void {
// Validate metric data
if (!metric.id || !metric.name || !metric.category) {
throw new Error('Invalid metric data');
}
if (metric.duration !== undefined && metric.duration < 0) {
throw new Error('Invalid metric data');
}
// Ensure tags is properly defined
const metricWithTags = {
...metric,
tags: metric.tags || {}
};
this.metrics.push(metricWithTags);
// Maintain history size limit
if (this.metrics.length > this.config.maxMetricsHistorySize) {
this.metrics = this.metrics.slice(-this.config.maxMetricsHistorySize);
}
// Emit for real-time monitoring
if (this.config.enableRealTimeMonitoring) {
this.emit('metric', metric);
}
}
recordTokenUsage(operationId: string, tokenMetrics: TokenUsageMetrics): void {
if (!this.config.tokenUsageTrackingEnabled) return;
const metric: PerformanceMetric = {
id: `token_${operationId}`,
name: 'token-usage',
category: 'token-usage',
timestamp: this.config.timeProvider(),
data: tokenMetrics as unknown as PerformanceMetricData
};
this.recordMetric(metric);
}
recordMemorySnapshot(label: string): void {
if (!this.config.memoryTrackingEnabled) return;
const memoryUsage = this.config.memoryProvider();
const memoryMetrics: MemoryMetrics = {
rss: memoryUsage.rss,
heapTotal: memoryUsage.heapTotal,
heapUsed: memoryUsage.heapUsed,
external: memoryUsage.external,
arrayBuffers: memoryUsage.arrayBuffers
};
this.memorySnapshots.push(memoryMetrics);
const metric: PerformanceMetric = {
id: `memory_${label}_${Date.now()}`,
name: 'memory-snapshot',
category: 'memory',
timestamp: this.config.timeProvider(),
data: memoryMetrics as unknown as PerformanceMetricData
};
this.recordMetric(metric);
}
recordOperationResult(operationName: string, success: boolean, data: OperationData): void {
if (!this.operationResults.has(operationName)) {
this.operationResults.set(operationName, []);
}
const results = this.operationResults.get(operationName);
if (results) {
results.push({ success, data });
}
}
recordGarbageCollection(event: GarbageCollectionEvent): void {
this.gcEvents.push(event);
}
// Statistical Analysis Methods
calculateStatistics(category: string): StatisticalAnalysis {
const categoryMetrics = this.getMetricsByCategory(category);
if (categoryMetrics.length === 0) {
return {
mean: 0,
median: 0,
min: 0,
max: 0,
standardDeviation: 0,
variance: 0,
sampleSize: 0,
percentiles: { p25: 0, p50: 0, p75: 0, p90: 0, p95: 0, p99: 0 }
};
}
const durations = categoryMetrics
.filter(m => m.duration !== undefined)
.map(m => m.duration!);
if (durations.length === 0) {
return {
mean: 0,
median: 0,
min: 0,
max: 0,
standardDeviation: 0,
variance: 0,
sampleSize: 0,
percentiles: { p25: 0, p50: 0, p75: 0, p90: 0, p95: 0, p99: 0 }
};
}
const sorted = durations.sort((a, b) => a - b);
const mean = durations.reduce((sum, d) => sum + d, 0) / durations.length;
const variance = durations.reduce((sum, d) => sum + Math.pow(d - mean, 2), 0) / durations.length;
const standardDeviation = Math.sqrt(variance);
return {
mean,
median: this.calculatePercentile(sorted, 50),
min: Math.min(...durations),
max: Math.max(...durations),
standardDeviation,
variance,
sampleSize: durations.length,
percentiles: {
p25: this.calculatePercentile(sorted, 25),
p50: this.calculatePercentile(sorted, 50),
p75: this.calculatePercentile(sorted, 75),
p90: this.calculatePercentile(sorted, 90),
p95: this.calculatePercentile(sorted, 95),
p99: this.calculatePercentile(sorted, 99)
}
};
}
private calculatePercentile(sortedValues: number[], percentile: number): number {
if (sortedValues.length === 0) return 0;
if (sortedValues.length === 1) return sortedValues[0] || 0;
const index = (percentile / 100) * (sortedValues.length - 1);
const lower = Math.floor(index);
const upper = Math.ceil(index);
if (lower === upper) {
return sortedValues[lower] || 0;
}
const lowerValue = sortedValues[lower] || 0;
const upperValue = sortedValues[upper] || 0;
return lowerValue + (upperValue - lowerValue) * (index - lower);
}
calculateSuccessRate(operationName: string): SuccessRateAnalysis {
const results = this.operationResults.get(operationName) || [];
const successful = results.filter(r => r.success).length;
return {
successRate: results.length > 0 ? successful / results.length : 0,
totalOperations: results.length,
successfulOperations: successful,
failedOperations: results.length - successful
};
}
analyzeTrends(category: string, options: { timeWindow: number; minimumDataPoints: number }): TrendAnalysis {
const now = this.config.timeProvider();
const windowStart = now - options.timeWindow;
const recentMetrics = this.getMetricsByCategory(category)
.filter(m => m.timestamp >= windowStart && m.duration !== undefined)
.sort((a, b) => a.timestamp - b.timestamp);
if (recentMetrics.length < options.minimumDataPoints) {
return {
trend: 'stable',
slope: 0,
confidence: 0,
dataPoints: recentMetrics.length,
timeWindow: options.timeWindow
};
}
// Simple linear regression to determine trend
const n = recentMetrics.length;
const sumX = recentMetrics.reduce((sum, _, i) => sum + i, 0);
const sumY = recentMetrics.reduce((sum, m) => sum + (m.duration || 0), 0);
const sumXY = recentMetrics.reduce((sum, m, i) => sum + (i * (m.duration || 0)), 0);
const sumXX = recentMetrics.reduce((sum, _, i) => sum + (i * i), 0);
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
// Calculate correlation coefficient for confidence
const meanX = sumX / n;
const meanY = sumY / n;
const numerator = recentMetrics.reduce((sum, m, i) => sum + (i - meanX) * ((m.duration || 0) - meanY), 0);
const denomX = Math.sqrt(recentMetrics.reduce((sum, _, i) => sum + Math.pow(i - meanX, 2), 0));
const denomY = Math.sqrt(recentMetrics.reduce((sum, m) => sum + Math.pow((m.duration || 0) - meanY, 2), 0));
const correlation = numerator / (denomX * denomY);
let trend: 'improving' | 'degrading' | 'stable';
if (Math.abs(slope) < 0.01) {
trend = 'stable';
} else if (slope < 0) {
trend = 'improving';
} else {
trend = 'degrading';
}
return {
trend,
slope,
confidence: Math.abs(correlation),
dataPoints: n,
timeWindow: options.timeWindow
};
}
detectAnomalies(category: string, options: { threshold: number; method: string }): AnomalyDetection[] {
const stats = this.calculateStatistics(category);
const metrics = this.getMetricsByCategory(category).filter(m => m.duration !== undefined);
const anomalies: AnomalyDetection[] = [];
for (const metric of metrics) {
const zscore = Math.abs((metric.duration! - stats.mean) / stats.standardDeviation);
if (zscore > options.threshold) {
anomalies.push({
metric,
score: zscore,
threshold: options.threshold,
method: options.method
});
}
}
return anomalies;
}
calculateMovingAverage(category: string, options: { windowSize: number; metric: string }): MovingAveragePoint[] {
const metrics = this.getMetricsByCategory(category)
.filter(m => m.duration !== undefined)
.sort((a, b) => a.timestamp - b.timestamp);
const movingAverages: MovingAveragePoint[] = [];
for (let i = options.windowSize - 1; i < metrics.length; i++) {
const window = metrics.slice(i - options.windowSize + 1, i + 1);
const average = window.reduce((sum, m) => sum + m.duration!, 0) / window.length;
movingAverages.push({
timestamp: metrics[i]?.timestamp || 0,
value: average,
windowSize: options.windowSize
});
}
return movingAverages;
}
// Memory Analysis Methods
analyzeMemoryUsage(): MemoryAnalysis {
if (this.memorySnapshots.length === 0) {
return {
peakHeapUsed: 0,
averageHeapUsed: 0,
memoryGrowthRate: 0,
potentialLeaks: false
};
}
const heapUsages = this.memorySnapshots.map(s => s.heapUsed);
const peakHeapUsed = Math.max(...heapUsages);
const averageHeapUsed = heapUsages.reduce((sum, h) => sum + h, 0) / heapUsages.length;
// Calculate growth rate
let memoryGrowthRate = 0;
if (this.memorySnapshots.length > 1) {
const first = this.memorySnapshots[0];
const last = this.memorySnapshots[this.memorySnapshots.length - 1];
if (first && last) {
memoryGrowthRate = (last.heapUsed - first.heapUsed) / this.memorySnapshots.length;
}
}
return {
peakHeapUsed,
averageHeapUsed,
memoryGrowthRate,
potentialLeaks: memoryGrowthRate > 1024 * 1024 // 1MB growth suggests potential leak
};
}
detectMemoryLeaks(): LeakDetection {
if (this.memorySnapshots.length < 5) {
return {
leakDetected: false,
leakRate: 0,
confidence: 0,
recommendedActions: []
};
}
// Analyze heap usage trend
const heapUsages = this.memorySnapshots.map(s => s.heapUsed);
const n = heapUsages.length;
// Linear regression on heap usage
const sumX = heapUsages.reduce((sum, _, i) => sum + i, 0);
const sumY = heapUsages.reduce((sum, h) => sum + h, 0);
const sumXY = heapUsages.reduce((sum, h, i) => sum + (i * h), 0);
const sumXX = heapUsages.reduce((sum, _, i) => sum + (i * i), 0);
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
// Calculate correlation for confidence
const meanX = sumX / n;
const meanY = sumY / n;
const numerator = heapUsages.reduce((sum, h, i) => sum + (i - meanX) * (h - meanY), 0);
const denomX = Math.sqrt(heapUsages.reduce((sum, _, i) => sum + Math.pow(i - meanX, 2), 0));
const denomY = Math.sqrt(heapUsages.reduce((sum, h) => sum + Math.pow(h - meanY, 2), 0));
const correlation = numerator / (denomX * denomY);
const leakDetected = slope > 1024 * 1024 && correlation > 0.8; // 1MB growth with high correlation
return {
leakDetected,
leakRate: slope,
confidence: Math.abs(correlation),
recommendedActions: leakDetected ? [
'Review recent code changes for memory allocation patterns',
'Check for unclosed resources or event listeners',
'Monitor object retention and garbage collection',
'Consider memory profiling tools'
] : []
};
}
analyzeGarbageCollection(): GarbageCollectionAnalysis {
if (this.gcEvents.length === 0) {
return {
totalEvents: 0,
averageDuration: 0,
totalMemoryReclaimed: 0,
gcEfficiency: 0
};
}
const totalDuration = this.gcEvents.reduce((sum, e) => sum + e.duration, 0);
const totalMemoryReclaimed = this.gcEvents.reduce((sum, e) => sum + Math.max(0, e.heapBefore - e.heapAfter), 0);
return {
totalEvents: this.gcEvents.length,
averageDuration: totalDuration / this.gcEvents.length,
totalMemoryReclaimed,
gcEfficiency: totalDuration > 0 ? totalMemoryReclaimed / totalDuration : 0
};
}
// Report Generation Methods
generateReport(format: 'json' | 'human-readable' | 'csv', config: ReportConfig = {}): PerformanceReport {
const now = this.config.timeProvider();
const timeRange = config.timeRange || { start: 0, end: now };
const filteredMetrics = this.metrics.filter(m =>
m.timestamp >= timeRange.start && m.timestamp <= timeRange.end &&
(!config.categories || config.categories.includes(m.category))
);
const reportData: ReportData = {
summary: {
totalMetrics: filteredMetrics.length,
timeRange: timeRange,
categories: Array.from(new Set(filteredMetrics.map(m => m.category)))
},
metrics: filteredMetrics
};
if (config.includeStatistics) {
reportData.statistics = {};
for (const category of reportData.summary.categories) {
reportData.statistics[category] = this.calculateStatistics(category);
}
}
if (config.includeTrends) {
reportData.trends = {};
for (const category of reportData.summary.categories) {
reportData.trends[category] = this.analyzeTrends(category, {
timeWindow: 60000,
minimumDataPoints: 5
});
}
}
if (config.includeInsights) {
reportData.insights = this.generateInsights(config.insightThresholds || {});
}
let formattedData: FormattedReportData;
switch (format) {
case 'json':
formattedData = reportData;
break;
case 'human-readable':
formattedData = this.formatHumanReadableReport(reportData);
break;
case 'csv':
formattedData = this.formatCSVReport(filteredMetrics);
break;
default:
throw new Error(`Unsupported format: ${format}`);
}
return {
format,
data: formattedData,
generatedAt: now,
timeRange,
metadata: config as unknown as PerformanceReportMetadata
};
}
private formatHumanReadableReport(data: ReportData): string {
let report = '# Performance Report\n\n';
report += `## Summary\n`;
report += `- Total Metrics: ${data.summary.totalMetrics}\n`;
report += `- Categories: ${data.summary.categories.join(', ')}\n`;
if (data.summary.timeRange) {
report += `- Time Range: ${new Date(data.summary.timeRange.start).toISOString()} to ${new Date(data.summary.timeRange.end).toISOString()}\n\n`;
}
if (data.statistics) {
report += `## Statistics\n`;
for (const [category, stats] of Object.entries(data.statistics)) {
const s = stats as StatisticalAnalysis;
report += `### ${category} metrics\n`;
report += `- Mean: ${s.mean.toFixed(2)}ms\n`;
report += `- Median: ${s.median.toFixed(2)}ms\n`;
report += `- P95: ${s.percentiles.p95.toFixed(2)}ms\n`;
report += `- Sample Size: ${s.sampleSize}\n\n`;
}
}
return report;
}
private formatCSVReport(metrics: PerformanceMetric[]): string {
const headers = ['timestamp', 'name', 'category', 'duration', 'tags'];
const rows = metrics.map(m => [
m.timestamp,
m.name,
m.category,
m.duration || '',
JSON.stringify(m.tags || {})
]);
return [headers.join(','), ...rows.map(row => row.join(','))].join('\n');
}
private generateInsights(_thresholds: InsightThresholds): string[] {
const insights: string[] = [];
// Add basic insights based on available data
if (this.metrics.length > 0) {
insights.push('Performance tracking is active and collecting metrics');
}
return insights;
}
// Real-time Monitoring Methods
subscribeToMetrics(callback: (metric: PerformanceMetric) => void): () => void {
this.on('metric', callback);
return () => this.removeListener('metric', callback);
}
configureAggregation(config: AggregationConfig): void {
this.aggregationConfig = config;
// Start aggregation process for configured categories
setTimeout(() => {
this.performAggregation();
}, config.windowSize);
}
private performAggregation(): void {
if (!this.aggregationConfig) return;
const now = this.config.timeProvider();
const windowStart = now - this.aggregationConfig.windowSize;
for (const category of this.aggregationConfig.categories) {
const recentMetrics = this.getMetricsByCategory(category)
.filter(m => m.timestamp >= windowStart);
if (recentMetrics.length > 0) {
const aggregatedMetric = this.createAggregatedMetric(recentMetrics, this.aggregationConfig.aggregationFunction);
if (!this.aggregatedMetrics.has(category)) {
this.aggregatedMetrics.set(category, []);
}
const metrics = this.aggregatedMetrics.get(category);
if (metrics) {
metrics.push(aggregatedMetric);
}
}
}
}
private createAggregatedMetric(metrics: PerformanceMetric[], aggregationFunction: string): PerformanceMetric {
const durations = metrics.filter(m => m.duration !== undefined).map(m => m.duration!);
let aggregatedValue = 0;
switch (aggregationFunction) {
case 'average':
aggregatedValue = durations.reduce((sum, d) => sum + d, 0) / durations.length;
break;
case 'sum':
aggregatedValue = durations.reduce((sum, d) => sum + d, 0);
break;
case 'max':
aggregatedValue = Math.max(...durations);
break;
case 'min':
aggregatedValue = Math.min(...durations);
break;
default:
aggregatedValue = durations.reduce((sum, d) => sum + d, 0) / durations.length;
}
return {
id: `aggregated_${Date.now()}`,
name: `${aggregationFunction}_${metrics[0]?.category || 'unknown'}`,
category: metrics[0]?.category || 'unknown',
timestamp: this.config.timeProvider(),
duration: aggregatedValue,
data: {
aggregationFunction,
sourceMetricsCount: metrics.length,
windowSize: this.aggregationConfig?.windowSize
}
};
}
getAggregatedMetrics(category: string): PerformanceMetric[] {
return this.aggregatedMetrics.get(category) || [];
}
// Evolution Engine Integration Methods
startEvolutionCycle(evolutionId: string, config: EvolutionCycleConfig): void {
this.evolutionCycles.set(evolutionId, {
id: evolutionId,
config,
startTime: this.config.timeProvider(),
generations: [],
status: 'running'
});
}
recordEvolutionGeneration(evolutionId: string, metrics: EvolutionGenerationMetrics): void {
const cycle = this.evolutionCycles.get(evolutionId);
if (cycle) {
cycle.generations.push(metrics);
}
}
endEvolutionCycle(evolutionId: string, result: EvolutionCycleResult): void {
const cycle = this.evolutionCycles.get(evolutionId);
if (cycle) {
cycle.endTime = this.config.timeProvider();
cycle.result = result;
cycle.status = 'completed';
}
}
getEvolutionMetrics(evolutionId: string): EvolutionMetrics | null {
const cycle = this.evolutionCycles.get(evolutionId);
if (!cycle || !cycle.result) return null;
const generations = cycle.generations as EvolutionGenerationMetrics[];
const firstGen = generations[0];
return {
evolutionId,
totalGenerations: cycle.result.totalGenerations,
finalFitness: cycle.result.finalFitness,
convergenceAchieved: cycle.result.convergenceAchieved,
fitnessImprovement: firstGen ? cycle.result.finalFitness - firstGen.bestFitness : 0,
duration: (cycle.endTime || 0) - cycle.startTime
};
}
recordMutationResult(mutationType: string, result: MutationResult): void {
if (!this.mutationResults.has(mutationType)) {
this.mutationResults.set(mutationType, []);
}
const results = this.mutationResults.get(mutationType);
if (results) {
results.push(result);
}
}
analyzeMutationEffectiveness(): MutationEffectivenessAnalysis {
const analysis: MutationEffectivenessAnalysis = {
byType: {},
overall: {
successRate: 0,
averageFitnessChange: 0
}
};
let totalOperations = 0;
let totalSuccessful = 0;
let totalFitnessChange = 0;
for (const [type, results] of Array.from(this.mutationResults.entries())) {
const successful = results.filter(r => r.success).length;
const avgFitnessChange = results.reduce((sum, r) => sum + r.fitnessChange, 0) / results.length;
analysis.byType[type] = {
successRate: successful / results.length,
averageFitnessChange: avgFitnessChange,
totalOperations: results.length
};
totalOperations += results.length;
totalSuccessful += successful;
totalFitnessChange += results.reduce((sum, r) => sum + r.fitnessChange, 0);
}
if (totalOperations > 0) {
analysis.overall.successRate = totalSuccessful / totalOperations;
analysis.overall.averageFitnessChange = totalFitnessChange / totalOperations;
}
return analysis;
}
getEvolutionInsights(evolutionId: string): EvolutionInsights | null {
const cycle = this.evolutionCycles.get(evolutionId);
if (!cycle) return null;
const generations = cycle.generations as EvolutionGenerationMetrics[];
if (generations.length < 2) return null;
// Analyze diversity trend
const diversityScores = generations.map(g => g.diversityScore);
const lastScore = diversityScores[diversityScores.length - 1] || 0;
const firstScore = diversityScores[0] || 0;
const diversitySlope = (lastScore - firstScore) / diversityScores.length;
let diversityTrend: 'increasing' | 'decreasing' | 'stable';
if (Math.abs(diversitySlope) < 0.01) {
diversityTrend = 'stable';
} else if (diversitySlope < 0) {
diversityTrend = 'decreasing';
} else {
diversityTrend = 'increasing';
}
// Calculate convergence rate
const fitnessValues = generations.map(g => g.bestFitness);
const lastFitness = fitnessValues[fitnessValues.length - 1] || 0;
const firstFitness = fitnessValues[0] || 0;
const convergenceRate = (lastFitness - firstFitness) / generations.length;
const recommendedAdjustments: string[] = [];
if (diversityTrend === 'decreasing') {
recommendedAdjustments.push('Consider increasing mutation rate to maintain diversity');
}
if (convergenceRate < 0.01) {
recommendedAdjustments.push('Slow convergence detected - consider adjusting selection pressure');
}
return {
evolutionId,
convergenceRate,
diversityTrend,
recommendedAdjustments
};
}
// Utility Methods
getMetricsByCategory(category: string): PerformanceMetric[] {
return this.metrics.filter(m => m.category === category);
}
getAllMetrics(): PerformanceMetric[] {
return [...this.metrics];
}
clearMetrics(): void {
this.metrics = [];
this.operationResults.clear();
this.memorySnapshots = [];
this.gcEvents = [];
}
}