Spaces:
Sleeping
Sleeping
| // useMetrics Composable | |
| // Real-time system and agent metrics management | |
| import { ref, computed, onMounted, onUnmounted, type Ref } from 'vue' | |
| import { useWebSocket } from './useWebSocket' | |
| import type { SystemMetrics, AgentMetrics, ResourceUsage } from '@/types' | |
| interface MetricsComposable { | |
| // System Metrics | |
| systemMetrics: Ref<SystemMetrics | null> | |
| systemLoading: Ref<boolean> | |
| systemError: Ref<string | null> | |
| // Agent Metrics | |
| agentMetrics: Ref<Record<string, AgentMetrics>> | |
| agentLoading: Ref<boolean> | |
| agentError: Ref<string | null> | |
| // Computed | |
| systemHealthScore: Ref<number> | |
| topPerformingAgents: Ref<AgentMetrics[]> | |
| systemAlerts: Ref<string[]> | |
| // Actions | |
| refreshSystemMetrics: () => Promise<void> | |
| refreshAgentMetrics: (agentId?: string) => Promise<void> | |
| exportMetrics: (format: 'json' | 'csv') => Promise<void> | |
| clearErrors: () => void | |
| // Utils | |
| getMetricTrend: (current: number, previous: number) => 'up' | 'down' | 'stable' | |
| formatMetricValue: (value: number, type: string) => string | |
| } | |
| export function useMetrics(): MetricsComposable { | |
| // State | |
| const systemMetrics = ref<SystemMetrics | null>(null) | |
| const systemLoading = ref(false) | |
| const systemError = ref<string | null>(null) | |
| const agentMetrics = ref<Record<string, AgentMetrics>>({}) | |
| const agentLoading = ref(false) | |
| const agentError = ref<string | null>(null) | |
| // WebSocket for real-time updates | |
| const { connected, on, off } = useWebSocket() | |
| // API client | |
| async function apiCall(endpoint: string) { | |
| const response = await fetch(`/api/v1${endpoint}`) | |
| if (!response.ok) { | |
| const errorData = await response.json().catch(() => ({})) | |
| throw new Error(errorData.message || `HTTP ${response.status}`) | |
| } | |
| return response.json() | |
| } | |
| // Computed | |
| const systemHealthScore = computed(() => { | |
| if (!systemMetrics.value) return 0 | |
| const metrics = systemMetrics.value | |
| let score = 100 | |
| // Deduct points for high resource usage | |
| if (metrics.memoryUsage.percentage > 80) score -= 20 | |
| if (metrics.cpuUsage.percentage > 80) score -= 20 | |
| // Deduct points for high error rate | |
| if (metrics.errorRate > 5) score -= 15 | |
| if (metrics.errorRate > 10) score -= 25 | |
| // Deduct points for network issues | |
| if (metrics.networkLatency > 500) score -= 10 | |
| if (metrics.networkLatency > 1000) score -= 20 | |
| // Deduct points for inactive agents | |
| const activePercentage = (metrics.activeAgents / metrics.totalAgents) * 100 | |
| if (activePercentage < 80) score -= 10 | |
| if (activePercentage < 50) score -= 20 | |
| return Math.max(0, score) | |
| }) | |
| const topPerformingAgents = computed(() => { | |
| return Object.values(agentMetrics.value) | |
| .sort((a, b) => { | |
| // Sort by success rate first, then by response time | |
| if (a.successRate !== b.successRate) { | |
| return b.successRate - a.successRate | |
| } | |
| return a.avgResponseTime - b.avgResponseTime | |
| }) | |
| .slice(0, 5) // Top 5 performers | |
| }) | |
| const systemAlerts = computed(() => { | |
| const alerts: string[] = [] | |
| if (!systemMetrics.value) return alerts | |
| const metrics = systemMetrics.value | |
| if (metrics.memoryUsage.percentage > 90) { | |
| alerts.push('Critical: Memory usage above 90%') | |
| } else if (metrics.memoryUsage.percentage > 80) { | |
| alerts.push('Warning: High memory usage') | |
| } | |
| if (metrics.cpuUsage.percentage > 90) { | |
| alerts.push('Critical: CPU usage above 90%') | |
| } else if (metrics.cpuUsage.percentage > 80) { | |
| alerts.push('Warning: High CPU usage') | |
| } | |
| if (metrics.errorRate > 10) { | |
| alerts.push('Critical: High error rate detected') | |
| } else if (metrics.errorRate > 5) { | |
| alerts.push('Warning: Elevated error rate') | |
| } | |
| if (metrics.networkLatency > 1000) { | |
| alerts.push('Critical: Network latency above 1s') | |
| } else if (metrics.networkLatency > 500) { | |
| alerts.push('Warning: High network latency') | |
| } | |
| const inactiveAgents = metrics.totalAgents - metrics.activeAgents | |
| if (inactiveAgents > metrics.totalAgents * 0.5) { | |
| alerts.push('Warning: More than 50% of agents are inactive') | |
| } | |
| return alerts | |
| }) | |
| // Actions | |
| async function refreshSystemMetrics(): Promise<void> { | |
| systemLoading.value = true | |
| systemError.value = null | |
| try { | |
| const response = await apiCall('/system/metrics') | |
| systemMetrics.value = response.data | |
| } catch (err) { | |
| systemError.value = err instanceof Error ? err.message : 'Failed to fetch system metrics' | |
| throw err | |
| } finally { | |
| systemLoading.value = false | |
| } | |
| } | |
| async function refreshAgentMetrics(agentId?: string): Promise<void> { | |
| agentLoading.value = true | |
| agentError.value = null | |
| try { | |
| const endpoint = agentId | |
| ? `/agents/${agentId}/metrics` | |
| : '/agents/metrics' | |
| const response = await apiCall(endpoint) | |
| if (agentId) { | |
| agentMetrics.value[agentId] = response.data | |
| } else { | |
| agentMetrics.value = response.data | |
| } | |
| } catch (err) { | |
| agentError.value = err instanceof Error ? err.message : 'Failed to fetch agent metrics' | |
| throw err | |
| } finally { | |
| agentLoading.value = false | |
| } | |
| } | |
| async function exportMetrics(format: 'json' | 'csv'): Promise<void> { | |
| try { | |
| const response = await fetch(`/api/v1/metrics/export?format=${format}`) | |
| if (!response.ok) { | |
| throw new Error('Failed to export metrics') | |
| } | |
| const blob = await response.blob() | |
| const url = URL.createObjectURL(blob) | |
| const link = document.createElement('a') | |
| link.href = url | |
| link.download = `saap-metrics-${new Date().toISOString().split('T')[0]}.${format}` | |
| document.body.appendChild(link) | |
| link.click() | |
| document.body.removeChild(link) | |
| URL.revokeObjectURL(url) | |
| } catch (err) { | |
| const message = err instanceof Error ? err.message : 'Failed to export metrics' | |
| systemError.value = message | |
| throw new Error(message) | |
| } | |
| } | |
| function clearErrors(): void { | |
| systemError.value = null | |
| agentError.value = null | |
| } | |
| // Utils | |
| function getMetricTrend(current: number, previous: number): 'up' | 'down' | 'stable' { | |
| const threshold = 0.05 // 5% change threshold | |
| const change = Math.abs(current - previous) / previous | |
| if (change < threshold) return 'stable' | |
| return current > previous ? 'up' : 'down' | |
| } | |
| function formatMetricValue(value: number, type: string): string { | |
| switch (type) { | |
| case 'percentage': | |
| return `${Math.round(value)}%` | |
| case 'bytes': | |
| return formatBytes(value) | |
| case 'duration': | |
| return formatDuration(value) | |
| case 'rate': | |
| return `${value.toFixed(2)}/min` | |
| case 'latency': | |
| return `${value}ms` | |
| case 'count': | |
| return value.toLocaleString() | |
| default: | |
| return value.toString() | |
| } | |
| } | |
| function formatBytes(bytes: number): string { | |
| if (bytes >= 1024 ** 3) { | |
| return `${(bytes / (1024 ** 3)).toFixed(1)}GB` | |
| } | |
| if (bytes >= 1024 ** 2) { | |
| return `${(bytes / (1024 ** 2)).toFixed(1)}MB` | |
| } | |
| if (bytes >= 1024) { | |
| return `${(bytes / 1024).toFixed(1)}KB` | |
| } | |
| return `${bytes}B` | |
| } | |
| function formatDuration(ms: number): string { | |
| if (ms < 1000) { | |
| return `${ms}ms` | |
| } | |
| if (ms < 60000) { | |
| return `${(ms / 1000).toFixed(1)}s` | |
| } | |
| return `${Math.round(ms / 60000)}m` | |
| } | |
| // WebSocket event handlers | |
| function handleMetricsUpdated(event: any) { | |
| if (event.data.type === 'system') { | |
| systemMetrics.value = event.data.metrics | |
| } else if (event.data.type === 'agent') { | |
| agentMetrics.value[event.data.agentId] = event.data.metrics | |
| } | |
| } | |
| // Lifecycle | |
| onMounted(() => { | |
| // Subscribe to WebSocket events | |
| on('metrics_updated', handleMetricsUpdated) | |
| // Initial data fetch | |
| if (connected.value) { | |
| Promise.all([ | |
| refreshSystemMetrics().catch(() => {}), | |
| refreshAgentMetrics().catch(() => {}) | |
| ]) | |
| } | |
| // Set up periodic refresh (fallback if WebSocket fails) | |
| const refreshInterval = setInterval(() => { | |
| if (!connected.value) { | |
| refreshSystemMetrics().catch(() => {}) | |
| refreshAgentMetrics().catch(() => {}) | |
| } | |
| }, 30000) // 30 seconds | |
| onUnmounted(() => { | |
| clearInterval(refreshInterval) | |
| }) | |
| }) | |
| onUnmounted(() => { | |
| // Unsubscribe from WebSocket events | |
| off('metrics_updated', handleMetricsUpdated) | |
| }) | |
| return { | |
| // System Metrics | |
| systemMetrics, | |
| systemLoading, | |
| systemError, | |
| // Agent Metrics | |
| agentMetrics, | |
| agentLoading, | |
| agentError, | |
| // Computed | |
| systemHealthScore, | |
| topPerformingAgents, | |
| systemAlerts, | |
| // Actions | |
| refreshSystemMetrics, | |
| refreshAgentMetrics, | |
| exportMetrics, | |
| clearErrors, | |
| // Utils | |
| getMetricTrend, | |
| formatMetricValue | |
| } | |
| } | |
| // Helper function to create mock metrics for development | |
| export function createMockSystemMetrics(): SystemMetrics { | |
| return { | |
| totalAgents: 8, | |
| activeAgents: 6, | |
| totalMessages: 1247, | |
| messagesPerMinute: 15.3, | |
| avgResponseTime: 1250, | |
| systemUptime: Date.now() - (3 * 24 * 60 * 60 * 1000), // 3 days ago | |
| memoryUsage: { | |
| current: 8.5 * 1024 ** 3, // 8.5 GB | |
| maximum: 16 * 1024 ** 3, // 16 GB | |
| percentage: 53, | |
| trend: 'stable' | |
| }, | |
| cpuUsage: { | |
| current: 45, | |
| maximum: 100, | |
| percentage: 45, | |
| trend: 'increasing' | |
| }, | |
| networkLatency: 125, | |
| errorRate: 2.1 | |
| } | |
| } | |
| export function createMockAgentMetrics(agentId: string): AgentMetrics { | |
| return { | |
| agentId, | |
| messagesProcessed: Math.floor(Math.random() * 500) + 50, | |
| successRate: Math.random() * 20 + 80, // 80-100% | |
| avgResponseTime: Math.random() * 2000 + 500, // 0.5-2.5s | |
| errorCount: Math.floor(Math.random() * 10), | |
| uptime: Date.now() - (Math.random() * 24 * 60 * 60 * 1000), // Up to 24h ago | |
| memoryUsage: Math.random() * 512 * 1024 * 1024, // Up to 512MB | |
| cpuUsage: Math.random() * 50, // 0-50% | |
| lastActivity: new Date(Date.now() - Math.random() * 60 * 60 * 1000).toISOString() // Up to 1h ago | |
| } | |
| } | |