Hwandji's picture
feat: initial HuggingFace Space deployment
4343907
raw
history blame
6.16 kB
// useAgentStatus Composable
// Manages agent lifecycle operations and status tracking
import { ref, computed, onMounted, onUnmounted, type Ref } from 'vue'
import { useWebSocket } from './useWebSocket'
import type { Agent, AgentStatus, WebSocketEvent } from '@/types'
interface AgentStatusComposable {
// State
isStarting: Ref<boolean>
isStopping: Ref<boolean>
isRestarting: Ref<boolean>
isLoading: Ref<boolean>
error: Ref<string | null>
// Computed
canStart: Ref<boolean>
canStop: Ref<boolean>
canRestart: Ref<boolean>
// Actions
startAgent: () => Promise<void>
stopAgent: () => Promise<void>
restartAgent: () => Promise<void>
refreshStatus: () => Promise<void>
clearError: () => void
}
export function useAgentStatus(agent: Agent): AgentStatusComposable {
// State
const isStarting = ref(false)
const isStopping = ref(false)
const isRestarting = ref(false)
const error = ref<string | null>(null)
// WebSocket for real-time updates
const { connected, on, off } = useWebSocket()
// Computed
const isLoading = computed(() =>
isStarting.value || isStopping.value || isRestarting.value
)
const canStart = computed(() =>
agent.status === 'inactive' && !isLoading.value
)
const canStop = computed(() =>
['active', 'processing', 'idle'].includes(agent.status) && !isLoading.value
)
const canRestart = computed(() =>
agent.status === 'error' && !isLoading.value
)
// API client
async function apiCall(method: string, endpoint: string, data?: any) {
const response = await fetch(`/api/v1${endpoint}`, {
method,
headers: {
'Content-Type': 'application/json',
},
body: data ? JSON.stringify(data) : undefined,
})
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(errorData.message || `HTTP ${response.status}`)
}
return response.json()
}
// Actions
async function startAgent(): Promise<void> {
if (!canStart.value) return
isStarting.value = true
error.value = null
try {
await apiCall('POST', `/agents/${agent.id}/start`)
// Status will be updated via WebSocket
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to start agent'
throw err
} finally {
isStarting.value = false
}
}
async function stopAgent(): Promise<void> {
if (!canStop.value) return
isStopping.value = true
error.value = null
try {
await apiCall('POST', `/agents/${agent.id}/stop`)
// Status will be updated via WebSocket
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to stop agent'
throw err
} finally {
isStopping.value = false
}
}
async function restartAgent(): Promise<void> {
if (!canRestart.value) return
isRestarting.value = true
error.value = null
try {
await apiCall('POST', `/agents/${agent.id}/restart`)
// Status will be updated via WebSocket
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to restart agent'
throw err
} finally {
isRestarting.value = false
}
}
async function refreshStatus(): Promise<void> {
try {
const response = await apiCall('GET', `/agents/${agent.id}`)
// Update agent status through parent component or store
// This assumes the parent handles the agent object updates
Object.assign(agent, response.data)
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to refresh status'
throw err
}
}
function clearError(): void {
error.value = null
}
// WebSocket event handlers
function handleAgentStatusChanged(event: WebSocketEvent) {
if (event.data.agentId === agent.id) {
// Update agent status
Object.assign(agent, event.data)
// Clear loading states if status changed
if (event.data.status !== agent.status) {
isStarting.value = false
isStopping.value = false
isRestarting.value = false
}
}
}
// Lifecycle
onMounted(() => {
// Subscribe to WebSocket events
on('agent_status_changed', handleAgentStatusChanged)
// Initial status refresh if needed
if (connected.value) {
refreshStatus().catch(() => {
// Silently handle initial refresh errors
})
}
})
onUnmounted(() => {
// Unsubscribe from WebSocket events
off('agent_status_changed', handleAgentStatusChanged)
})
return {
// State
isStarting,
isStopping,
isRestarting,
isLoading,
error,
// Computed
canStart,
canStop,
canRestart,
// Actions
startAgent,
stopAgent,
restartAgent,
refreshStatus,
clearError
}
}
// Agent Status Store (optional Pinia store)
export interface AgentStatusState {
agents: Record<string, Agent>
loading: boolean
error: string | null
}
// Helper functions
export function getAgentStatusColor(status: AgentStatus): string {
const colorMap = {
active: 'var(--saap-success)',
inactive: 'var(--saap-neutral-400)',
processing: 'var(--saap-warning)',
error: 'var(--saap-error)',
idle: 'var(--saap-info)'
}
return colorMap[status] || colorMap.inactive
}
export function getAgentTypeIcon(type: string): string {
const iconMap = {
generalist: 'brain',
specialist: 'target',
coordinator: 'network'
}
return iconMap[type as keyof typeof iconMap] || 'help-circle'
}
export function formatAgentUptime(timestamp: string): string {
const start = new Date(timestamp)
const now = new Date()
const diff = now.getTime() - start.getTime()
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
if (days > 0) {
return `${days}d ${hours}h`
}
if (hours > 0) {
return `${hours}h ${minutes}m`
}
return `${minutes}m`
}