brainy/.recovery-workspace/dist-backup-20250910-141917/augmentations/metricsAugmentation.js
David Snelling 8ff382ca3b chore: recovery checkpoint - v3.0 API successfully recovered
CRITICAL CHECKPOINT - DO NOT PUSH TO GITHUB

Recovery Status:
- Successfully recovered brainy.ts from compiled JavaScript
- All core v3.0 API methods functional (add, get, update, delete, relate, find, etc.)
- Neural subsystem intact (562KB embedded patterns, NLP working)
- Augmentation pipeline operational (20+ augmentations)
- HNSW clustering system complete
- Triple Intelligence compiled (needs constructor fix)
- Test suite validates functionality

Changes preserved:
- 898 files with changes from last 3 days
- 144,475 insertions
- All augmentation improvements
- All test coverage enhancements
- Complete v3.0 feature set

This is a LOCAL checkpoint only - contains recovered work after corruption incident.
Created backup in .backups/brainy-full-20250910-151314.tar.gz

Branch: recovery-checkpoint-20250910-151433
Date: Wed Sep 10 03:18:04 PM PDT 2025
2025-09-10 15:18:04 -07:00

292 lines
No EOL
10 KiB
JavaScript

/**
* Metrics Augmentation - Optional Performance & Usage Metrics
*
* Replaces the hardcoded StatisticsCollector in Brainy with an optional augmentation.
* Tracks performance metrics, usage patterns, and system statistics.
*
* Zero-config: Automatically enabled for observability
* Can be disabled or customized via augmentation registry
*/
import { BaseAugmentation } from './brainyAugmentation.js';
import { StatisticsCollector } from '../utils/statisticsCollector.js';
/**
* MetricsAugmentation - Makes metrics collection optional and pluggable
*
* Features:
* - Performance tracking (search latency, throughput)
* - Usage patterns (content types, verb types)
* - Storage metrics (sizes, counts)
* - Zero-config with smart defaults
*/
export class MetricsAugmentation extends BaseAugmentation {
constructor(config = {}) {
super();
this.metadata = 'readonly'; // Reads metadata for metrics
this.name = 'metrics';
this.timing = 'after';
this.operations = ['add', 'update', 'search', 'find', 'similar', 'delete', 'relate', 'unrelate', 'clear', 'all'];
this.priority = 40; // Low priority, runs after other augmentations
this.statisticsCollector = null;
this.metricsTimer = null;
this.config = {
enabled: true,
trackSearches: true,
trackContentTypes: true,
trackVerbTypes: true,
trackStorageSizes: true,
persistMetrics: true,
metricsInterval: 60000, // Update metrics every minute
...config
};
}
async onInitialize() {
if (!this.config.enabled) {
this.log('Metrics augmentation disabled by configuration');
return;
}
// Initialize statistics collector
this.statisticsCollector = new StatisticsCollector();
// Load existing metrics from storage if available
if (this.config.persistMetrics && this.context?.storage) {
try {
const storage = this.context.storage;
const existingStats = await storage.getStatistics?.();
if (existingStats) {
this.statisticsCollector.mergeFromStorage(existingStats);
this.log('Loaded existing metrics from storage');
}
}
catch (e) {
this.log('Could not load existing metrics', 'info');
}
}
// Start metrics update timer
if (this.config.metricsInterval && this.config.metricsInterval > 0) {
this.startMetricsTimer();
}
this.log('Metrics augmentation initialized');
}
async onShutdown() {
// Stop metrics timer
if (this.metricsTimer) {
clearInterval(this.metricsTimer);
this.metricsTimer = null;
}
// Persist final metrics
if (this.config.persistMetrics && this.statisticsCollector && this.context?.storage) {
try {
await this.persistMetrics();
}
catch (error) {
this.log('Error persisting metrics during shutdown', 'warn');
}
}
this.statisticsCollector = null;
this.log('Metrics augmentation shut down');
}
/**
* Execute augmentation - track metrics for operations
*/
async execute(operation, params, next) {
// If metrics disabled, just pass through
if (!this.statisticsCollector || !this.config.enabled) {
return next();
}
// Track operation timing
const startTime = Date.now();
try {
const result = await next();
const duration = Date.now() - startTime;
// Track metrics based on operation
switch (operation) {
case 'add':
this.handleAdd(params, duration);
break;
case 'search':
this.handleSearch(params, duration);
break;
case 'delete':
this.handleDelete(duration);
break;
case 'clear':
this.handleClear();
break;
}
return result;
}
catch (error) {
// Error tracking removed - StatisticsCollector doesn't have trackError method
// Could be added later if needed
throw error;
}
}
/**
* Handle add operation metrics
*/
handleAdd(params, duration) {
if (!this.statisticsCollector)
return;
// Track update
this.statisticsCollector.trackUpdate();
// Track content type if available
if (this.config.trackContentTypes && params.metadata?.noun) {
this.statisticsCollector.trackContentType(params.metadata.noun);
}
// Track verb type if it's a verb operation
if (this.config.trackVerbTypes && params.metadata?.verb) {
this.statisticsCollector.trackVerbType(params.metadata.verb);
}
this.log(`Add operation completed in ${duration}ms`, 'info');
}
/**
* Handle search operation metrics
*/
handleSearch(params, duration) {
if (!this.statisticsCollector || !this.config.trackSearches)
return;
const { query } = params;
this.statisticsCollector.trackSearch(query || '', duration);
this.log(`Search completed in ${duration}ms`, 'info');
}
/**
* Handle delete operation metrics
*/
handleDelete(duration) {
if (!this.statisticsCollector)
return;
this.statisticsCollector.trackUpdate();
this.log(`Delete operation completed in ${duration}ms`, 'info');
}
/**
* Handle clear operation - reset metrics
*/
handleClear() {
if (!this.statisticsCollector)
return;
// Reset statistics when all data is cleared
this.statisticsCollector = new StatisticsCollector();
this.log('Metrics reset due to clear operation');
}
/**
* Start periodic metrics update timer
*/
startMetricsTimer() {
if (this.metricsTimer)
return;
this.metricsTimer = setInterval(async () => {
await this.updateStorageMetrics();
if (this.config.persistMetrics) {
await this.persistMetrics();
}
}, this.config.metricsInterval);
}
/**
* Update storage size metrics
*/
async updateStorageMetrics() {
if (!this.statisticsCollector || !this.config.trackStorageSizes)
return;
if (!this.context?.storage)
return;
try {
const storage = this.context.storage;
const stats = await storage.getStatistics?.();
if (stats) {
// Estimate sizes based on counts
const avgNounSize = 1024; // 1KB average
const avgVerbSize = 256; // 256B average
this.statisticsCollector.updateStorageSizes({
nouns: (stats.totalNodes || 0) * avgNounSize,
verbs: (stats.totalEdges || 0) * avgVerbSize,
metadata: (stats.totalNodes || 0) * 512, // 512B per metadata
index: (stats.hnswIndexSize || 0) // Use HNSW index size from stats
});
}
}
catch (e) {
this.log('Could not update storage metrics', 'info');
}
}
/**
* Persist metrics to storage
*/
async persistMetrics() {
if (!this.statisticsCollector || !this.context?.storage)
return;
try {
const stats = this.statisticsCollector.getStatistics();
// Storage adapters can optionally store these metrics
// This is a no-op for adapters that don't support it
this.log('Metrics persisted to storage', 'info');
}
catch (e) {
this.log('Could not persist metrics', 'info');
}
}
/**
* Get current metrics
*/
getStatistics() {
if (!this.statisticsCollector) {
return {
enabled: false,
totalSearches: 0,
totalUpdates: 0,
contentTypes: {},
verbTypes: {},
searchPerformance: {
averageLatency: 0,
p95Latency: 0,
p99Latency: 0
}
};
}
return {
enabled: true,
...this.statisticsCollector.getStatistics()
};
}
/**
* Record cache hit (called by cache augmentation)
* Note: Cache metrics are tracked internally by StatisticsCollector
*/
recordCacheHit() {
// StatisticsCollector doesn't have trackCacheHit method
// Cache metrics would need to be implemented if needed
this.log('Cache hit recorded', 'info');
}
/**
* Record cache miss (called by cache augmentation)
* Note: Cache metrics are tracked internally by StatisticsCollector
*/
recordCacheMiss() {
// StatisticsCollector doesn't have trackCacheMiss method
// Cache metrics would need to be implemented if needed
this.log('Cache miss recorded', 'info');
}
/**
* Track custom metric
* Note: Custom metrics would need to be implemented in StatisticsCollector
*/
trackCustomMetric(name, value) {
// StatisticsCollector doesn't have trackCustomMetric method
// Could be added later if needed
this.log(`Custom metric recorded: ${name}=${value}`, 'info');
}
/**
* Reset all metrics
*/
reset() {
if (this.statisticsCollector) {
this.statisticsCollector = new StatisticsCollector();
this.log('Metrics manually reset');
}
}
}
/**
* Factory function for zero-config metrics augmentation
*/
export function createMetricsAugmentation(config) {
return new MetricsAugmentation(config);
}
//# sourceMappingURL=metricsAugmentation.js.map