Hwandji's picture
feat: initial HuggingFace Space deployment
4343907
// 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
}
}