Skip to main content
Glama
metrics.tsβ€’14.8 kB
/** * Event-based Metrics Collection * * Collects and aggregates metrics from events for monitoring and performance analysis. */ import { EventEnvelope, ServiceEventMap, SessionEventMap } from './event-types.js'; export interface MetricValue { count: number; sum: number; min: number; max: number; avg: number; lastUpdated: number; } export interface ServiceMetrics { // Cipher lifecycle cipherUptime: number; serviceStartCount: number; serviceErrorCount: number; allServicesReadyCount: number; // Tool operations toolRegistrationCount: number; toolErrorCount: number; // MCP operations mcpConnectionCount: number; mcpDisconnectionCount: number; mcpErrorCount: number; // Vector store operations vectorStoreConnectionCount: number; vectorStoreDisconnectionCount: number; vectorStoreErrorCount: number; // LLM provider operations llmProviderRegistrationCount: number; llmProviderErrorCount: number; } export interface SessionMetrics { // Session lifecycle sessionCreatedCount: number; sessionActivatedCount: number; sessionExpiredCount: number; sessionDeletedCount: number; averageSessionDuration: MetricValue; // Tool execution toolExecutionCount: number; toolExecutionSuccessCount: number; toolExecutionFailureCount: number; toolExecutionDuration: MetricValue; toolExecutionsByType: Record<string, number>; // LLM interactions llmThinkingCount: number; llmResponseCount: number; llmResponseSuccessCount: number; llmResponseErrorCount: number; llmResponseDuration: MetricValue; llmResponsesByModel: Record<string, number>; // Memory operations memoryStoreCount: number; memoryRetrieveCount: number; memorySearchCount: number; memorySearchDuration: MetricValue; // Conversation operations conversationMessageCount: number; conversationClearCount: number; contextUpdateCount: number; contextTruncateCount: number; } export interface PerformanceMetrics { // Event system performance eventEmissionRate: MetricValue; // events per second eventProcessingLatency: MetricValue; // ms eventQueueSize: number; // Service performance serviceResponseTimes: Record<string, MetricValue>; errorRates: Record<string, number>; // Resource usage memoryUsage: MetricValue; cpuUsage: MetricValue; } /** * Metrics collector that processes events and maintains statistics */ export class EventMetricsCollector { private serviceMetrics: ServiceMetrics; private sessionMetrics: SessionMetrics; private performanceMetrics: PerformanceMetrics; private sessionStartTimes = new Map<string, number>(); private processingStartTime = Date.now(); constructor() { this.serviceMetrics = this.initializeServiceMetrics(); this.sessionMetrics = this.initializeSessionMetrics(); this.performanceMetrics = this.initializePerformanceMetrics(); } /** * Process a service event and update metrics */ processServiceEvent(event: EventEnvelope<ServiceEventMap[keyof ServiceEventMap]>): void { const now = Date.now(); switch (event.type) { case 'cipher:started': this.processingStartTime = event.metadata.timestamp; break; case 'cipher:serviceStarted': this.serviceMetrics.serviceStartCount++; break; case 'cipher:serviceError': this.serviceMetrics.serviceErrorCount++; break; case 'cipher:allServicesReady': this.serviceMetrics.allServicesReadyCount++; break; case 'cipher:toolRegistered': this.serviceMetrics.toolRegistrationCount++; break; case 'cipher:toolError': this.serviceMetrics.toolErrorCount++; break; case 'cipher:mcpClientConnected': this.serviceMetrics.mcpConnectionCount++; break; case 'cipher:mcpClientDisconnected': this.serviceMetrics.mcpDisconnectionCount++; break; case 'cipher:mcpClientError': this.serviceMetrics.mcpErrorCount++; break; case 'cipher:vectorStoreConnected': this.serviceMetrics.vectorStoreConnectionCount++; break; case 'cipher:vectorStoreDisconnected': this.serviceMetrics.vectorStoreDisconnectionCount++; break; case 'cipher:vectorStoreError': this.serviceMetrics.vectorStoreErrorCount++; break; case 'cipher:llmProviderRegistered': this.serviceMetrics.llmProviderRegistrationCount++; break; case 'cipher:llmProviderError': this.serviceMetrics.llmProviderErrorCount++; break; } // Update cipher uptime this.serviceMetrics.cipherUptime = now - this.processingStartTime; } /** * Process a session event and update metrics */ processSessionEvent(event: EventEnvelope<SessionEventMap[keyof SessionEventMap]>): void { switch (event.type) { case 'session:created': this.sessionMetrics.sessionCreatedCount++; if (event.metadata.sessionId) { this.sessionStartTimes.set(event.metadata.sessionId, event.metadata.timestamp); } break; case 'session:activated': this.sessionMetrics.sessionActivatedCount++; break; case 'session:expired': this.sessionMetrics.sessionExpiredCount++; this.updateSessionDuration(event); break; case 'session:deleted': this.sessionMetrics.sessionDeletedCount++; this.updateSessionDuration(event); break; case 'tool:executionStarted': { this.sessionMetrics.toolExecutionCount++; const toolData = event.data as any; if (toolData.toolType) { this.sessionMetrics.toolExecutionsByType[toolData.toolType] = (this.sessionMetrics.toolExecutionsByType[toolData.toolType] || 0) + 1; } break; } case 'tool:executionCompleted': this.sessionMetrics.toolExecutionSuccessCount++; this.updateMetricValue( this.sessionMetrics.toolExecutionDuration, (event.data as any).duration || 0 ); break; case 'tool:executionFailed': this.sessionMetrics.toolExecutionFailureCount++; this.updateMetricValue( this.sessionMetrics.toolExecutionDuration, (event.data as any).duration || 0 ); break; case 'llm:thinking': this.sessionMetrics.llmThinkingCount++; break; case 'llm:responseStarted': { this.sessionMetrics.llmResponseCount++; const responseData = event.data as any; if (responseData.model) { this.sessionMetrics.llmResponsesByModel[responseData.model] = (this.sessionMetrics.llmResponsesByModel[responseData.model] || 0) + 1; } break; } case 'llm:responseCompleted': this.sessionMetrics.llmResponseSuccessCount++; this.updateMetricValue( this.sessionMetrics.llmResponseDuration, (event.data as any).duration || 0 ); break; case 'llm:responseError': this.sessionMetrics.llmResponseErrorCount++; break; case 'memory:stored': this.sessionMetrics.memoryStoreCount++; break; case 'memory:retrieved': this.sessionMetrics.memoryRetrieveCount++; break; case 'memory:searched': this.sessionMetrics.memorySearchCount++; this.updateMetricValue( this.sessionMetrics.memorySearchDuration, (event.data as any).duration || 0 ); break; case 'conversation:messageAdded': this.sessionMetrics.conversationMessageCount++; break; case 'conversation:cleared': this.sessionMetrics.conversationClearCount++; break; case 'context:updated': this.sessionMetrics.contextUpdateCount++; break; case 'context:truncated': this.sessionMetrics.contextTruncateCount++; break; } } /** * Get current service metrics */ getServiceMetrics(): ServiceMetrics { return { ...this.serviceMetrics }; } /** * Get current session metrics */ getSessionMetrics(): SessionMetrics { return { ...this.sessionMetrics }; } /** * Get current performance metrics */ getPerformanceMetrics(): PerformanceMetrics { return { ...this.performanceMetrics }; } /** * Get comprehensive metrics summary */ getMetricsSummary(): { service: ServiceMetrics; session: SessionMetrics; performance: PerformanceMetrics; timestamp: number; } { return { service: this.getServiceMetrics(), session: this.getSessionMetrics(), performance: this.getPerformanceMetrics(), timestamp: Date.now(), }; } /** * Reset all metrics */ reset(): void { this.serviceMetrics = this.initializeServiceMetrics(); this.sessionMetrics = this.initializeSessionMetrics(); this.performanceMetrics = this.initializePerformanceMetrics(); this.sessionStartTimes.clear(); this.processingStartTime = Date.now(); } /** * Get metrics for a specific time period */ getMetricsForPeriod(startTime: number, endTime: number): any { // This would be implemented with time-series data storage // For now, return current metrics with period info return { ...this.getMetricsSummary(), period: { startTime, endTime, duration: endTime - startTime }, }; } private initializeServiceMetrics(): ServiceMetrics { return { cipherUptime: 0, serviceStartCount: 0, serviceErrorCount: 0, allServicesReadyCount: 0, toolRegistrationCount: 0, toolErrorCount: 0, mcpConnectionCount: 0, mcpDisconnectionCount: 0, mcpErrorCount: 0, vectorStoreConnectionCount: 0, vectorStoreDisconnectionCount: 0, vectorStoreErrorCount: 0, llmProviderRegistrationCount: 0, llmProviderErrorCount: 0, }; } private initializeSessionMetrics(): SessionMetrics { return { sessionCreatedCount: 0, sessionActivatedCount: 0, sessionExpiredCount: 0, sessionDeletedCount: 0, averageSessionDuration: this.createMetricValue(), toolExecutionCount: 0, toolExecutionSuccessCount: 0, toolExecutionFailureCount: 0, toolExecutionDuration: this.createMetricValue(), toolExecutionsByType: {}, llmThinkingCount: 0, llmResponseCount: 0, llmResponseSuccessCount: 0, llmResponseErrorCount: 0, llmResponseDuration: this.createMetricValue(), llmResponsesByModel: {}, memoryStoreCount: 0, memoryRetrieveCount: 0, memorySearchCount: 0, memorySearchDuration: this.createMetricValue(), conversationMessageCount: 0, conversationClearCount: 0, contextUpdateCount: 0, contextTruncateCount: 0, }; } private initializePerformanceMetrics(): PerformanceMetrics { return { eventEmissionRate: this.createMetricValue(), eventProcessingLatency: this.createMetricValue(), eventQueueSize: 0, serviceResponseTimes: {}, errorRates: {}, memoryUsage: this.createMetricValue(), cpuUsage: this.createMetricValue(), }; } private createMetricValue(): MetricValue { return { count: 0, sum: 0, min: Number.MAX_SAFE_INTEGER, max: 0, avg: 0, lastUpdated: Date.now(), }; } private updateMetricValue(metric: MetricValue, value: number): void { metric.count++; metric.sum += value; metric.min = Math.min(metric.min, value); metric.max = Math.max(metric.max, value); metric.avg = metric.sum / metric.count; metric.lastUpdated = Date.now(); } private updateSessionDuration(event: EventEnvelope): void { if (!event.metadata.sessionId) return; const startTime = this.sessionStartTimes.get(event.metadata.sessionId); if (startTime) { const duration = event.metadata.timestamp - startTime; this.updateMetricValue(this.sessionMetrics.averageSessionDuration, duration); this.sessionStartTimes.delete(event.metadata.sessionId); } } } /** * Metrics exporter for external monitoring systems */ export class MetricsExporter { private metricsCollector: EventMetricsCollector; constructor(metricsCollector: EventMetricsCollector) { this.metricsCollector = metricsCollector; } /** * Export metrics in Prometheus format */ exportPrometheus(): string { const metrics = this.metricsCollector.getMetricsSummary(); const lines: string[] = []; // Service metrics lines.push(`# HELP cipher_uptime_seconds Total uptime of cipher instance`); lines.push(`# TYPE cipher_uptime_seconds gauge`); lines.push(`cipher_uptime_seconds ${metrics.service.cipherUptime / 1000}`); lines.push(`# HELP cipher_service_starts_total Total number of service starts`); lines.push(`# TYPE cipher_service_starts_total counter`); lines.push(`cipher_service_starts_total ${metrics.service.serviceStartCount}`); lines.push(`# HELP cipher_tool_executions_total Total number of tool executions`); lines.push(`# TYPE cipher_tool_executions_total counter`); lines.push(`cipher_tool_executions_total ${metrics.session.toolExecutionCount}`); lines.push(`# HELP cipher_tool_execution_duration_ms Tool execution duration in milliseconds`); lines.push(`# TYPE cipher_tool_execution_duration_ms histogram`); lines.push( `cipher_tool_execution_duration_ms_count ${metrics.session.toolExecutionDuration.count}` ); lines.push( `cipher_tool_execution_duration_ms_sum ${metrics.session.toolExecutionDuration.sum}` ); lines.push(`# HELP cipher_llm_responses_total Total number of LLM responses`); lines.push(`# TYPE cipher_llm_responses_total counter`); lines.push(`cipher_llm_responses_total ${metrics.session.llmResponseCount}`); lines.push(`# HELP cipher_memory_operations_total Total number of memory operations`); lines.push(`# TYPE cipher_memory_operations_total counter`); lines.push( `cipher_memory_operations_total ${metrics.session.memoryStoreCount + metrics.session.memoryRetrieveCount}` ); return lines.join('\n') + '\n'; } /** * Export metrics in JSON format */ exportJSON(): string { return JSON.stringify(this.metricsCollector.getMetricsSummary(), null, 2); } /** * Export metrics for specific monitoring system */ exportForSystem(system: 'datadog' | 'newrelic' | 'cloudwatch'): any { const metrics = this.metricsCollector.getMetricsSummary(); switch (system) { case 'datadog': return this.formatForDatadog(metrics); case 'newrelic': return this.formatForNewRelic(metrics); case 'cloudwatch': return this.formatForCloudWatch(metrics); default: return metrics; } } private formatForDatadog(metrics: any): any { // DataDog specific format return { series: [ { metric: 'cipher.uptime', type: 'gauge', points: [[Date.now() / 1000, metrics.service.cipherUptime / 1000]], }, { metric: 'cipher.tool.executions', type: 'count', points: [[Date.now() / 1000, metrics.session.toolExecutionCount]], }, ], }; } private formatForNewRelic(metrics: any): any { // New Relic specific format return { metrics: [ { name: 'cipher.uptime', type: 'gauge', value: metrics.service.cipherUptime / 1000, timestamp: Date.now(), }, ], }; } private formatForCloudWatch(metrics: any): any { // CloudWatch specific format return { MetricData: [ { MetricName: 'CipherUptime', Value: metrics.service.cipherUptime / 1000, Unit: 'Seconds', Timestamp: new Date(), }, ], }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/campfirein/cipher'

If you have feedback or need assistance with the MCP directory API, please join our Discord server