Hwandji's picture
feat: initial HuggingFace Space deployment
4343907
raw
history blame
14.1 kB
/**
* SAAP Agent Store - Pinia State Management
* Centralized state management for SAAP agents and system
*/
import { defineStore } from 'pinia'
import { ref, reactive, computed } from 'vue'
import { useApi, type SaapAgent, type ChatMessage, type SystemStatus } from '@/composables/useApi'
import { useWebSocket } from '@/composables/useWebSocket'
interface AgentOperation {
agentId: string
operation: 'starting' | 'stopping' | 'chatting'
timestamp: string
}
interface NotificationMessage {
id: string
type: 'success' | 'error' | 'info' | 'warning'
title: string
message: string
timestamp: string
duration?: number
}
export const useAgentStore = defineStore('agents', () => {
// API and WebSocket composables
const api = useApi()
const ws = useWebSocket()
// =====================================================
// STATE
// =====================================================
const agents = ref<SaapAgent[]>([])
const selectedAgent = ref<SaapAgent | null>(null)
const systemStatus = ref<SystemStatus | null>(null)
const chatHistory = ref<ChatMessage[]>([])
const activeOperations = ref<AgentOperation[]>([])
const notifications = ref<NotificationMessage[]>([])
// Loading states
const loading = reactive({
agents: false,
systemStatus: false,
operations: false
})
// Connection status
const connectionStatus = reactive({
api: false,
websocket: false,
lastCheck: null as string | null
})
// =====================================================
// COMPUTED
// =====================================================
const activeAgents = computed(() =>
agents.value.filter(agent => agent.status === 'active')
)
const inactiveAgents = computed(() =>
agents.value.filter(agent => agent.status === 'inactive')
)
const agentsByType = computed(() => {
const grouped: Record<string, SaapAgent[]> = {}
agents.value.forEach(agent => {
if (!grouped[agent.type]) {
grouped[agent.type] = []
}
grouped[agent.type].push(agent)
})
return grouped
})
const totalMessages = computed(() =>
agents.value.reduce((total, agent) =>
total + (agent.metrics?.messages_processed || 0), 0
)
)
const averageResponseTime = computed(() => {
const responseTimes = agents.value
.map(agent => agent.metrics?.average_response_time)
.filter((time): time is number => time !== undefined && time > 0)
if (responseTimes.length === 0) return 0
return responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length
})
const isOperationActive = computed(() => (agentId: string, operation: string) => {
return activeOperations.value.some(
op => op.agentId === agentId && op.operation === operation
)
})
// =====================================================
// AGENT MANAGEMENT ACTIONS
// =====================================================
const fetchAgents = async (): Promise<boolean> => {
try {
loading.agents = true
const result = await api.getAgents()
if (result) {
agents.value = result
showNotification({
type: 'success',
title: 'Agents Loaded',
message: `Loaded ${result.length} agents successfully`,
})
return true
}
return false
} catch (error) {
showNotification({
type: 'error',
title: 'Load Failed',
message: 'Failed to load agents from server',
})
return false
} finally {
loading.agents = false
}
}
const selectAgent = async (agentId: string): Promise<boolean> => {
const agent = agents.value.find(a => a.id === agentId)
if (agent) {
selectedAgent.value = agent
return true
}
// Try to fetch from API if not found locally
const result = await api.getAgent(agentId)
if (result) {
selectedAgent.value = result
return true
}
return false
}
const updateAgentInStore = (updatedAgent: SaapAgent) => {
const index = agents.value.findIndex(a => a.id === updatedAgent.id)
if (index !== -1) {
agents.value[index] = updatedAgent
// Update selected agent if it's the same
if (selectedAgent.value?.id === updatedAgent.id) {
selectedAgent.value = updatedAgent
}
}
}
// =====================================================
// AGENT LIFECYCLE ACTIONS
// =====================================================
const startAgent = async (agentId: string): Promise<boolean> => {
try {
addOperation(agentId, 'starting')
const result = await api.startAgent(agentId)
if (result?.success) {
// Update agent status immediately
const agent = agents.value.find(a => a.id === agentId)
if (agent) {
agent.status = 'starting'
}
showNotification({
type: 'success',
title: 'Agent Starting',
message: `${result.agent?.name || agentId} is starting...`,
})
// Refresh agents after a short delay to get updated status
setTimeout(() => {
fetchAgents()
}, 2000)
return true
}
return false
} catch (error) {
showNotification({
type: 'error',
title: 'Start Failed',
message: `Failed to start agent ${agentId}`,
})
return false
} finally {
removeOperation(agentId, 'starting')
}
}
const stopAgent = async (agentId: string): Promise<boolean> => {
try {
addOperation(agentId, 'stopping')
const result = await api.stopAgent(agentId)
if (result?.success) {
// Update agent status immediately
const agent = agents.value.find(a => a.id === agentId)
if (agent) {
agent.status = 'inactive'
}
showNotification({
type: 'info',
title: 'Agent Stopped',
message: `Agent ${agentId} stopped successfully`,
})
return true
}
return false
} catch (error) {
showNotification({
type: 'error',
title: 'Stop Failed',
message: `Failed to stop agent ${agentId}`,
})
return false
} finally {
removeOperation(agentId, 'stopping')
}
}
// =====================================================
// AGENT COMMUNICATION ACTIONS
// =====================================================
const chatWithAgent = async (agentId: string, message: string): Promise<ChatMessage | null> => {
try {
addOperation(agentId, 'chatting')
const result = await api.chatWithAgent(agentId, message)
if (result) {
// Add to chat history
chatHistory.value.push(result)
// Update agent metrics
const agent = agents.value.find(a => a.id === agentId)
if (agent && agent.metrics) {
agent.metrics.messages_processed = (agent.metrics.messages_processed || 0) + 1
if (result.response_time) {
agent.metrics.average_response_time = result.response_time
}
}
showNotification({
type: 'success',
title: 'Message Sent',
message: `Received response from ${result.agent_name || agentId}`,
duration: 3000
})
return result
}
return null
} catch (error) {
showNotification({
type: 'error',
title: 'Chat Failed',
message: `Failed to chat with agent ${agentId}`,
})
return null
} finally {
removeOperation(agentId, 'chatting')
}
}
const getChatHistory = (agentId?: string): ChatMessage[] => {
if (agentId) {
return chatHistory.value.filter(msg => msg.agent_id === agentId)
}
return chatHistory.value
}
// =====================================================
// SYSTEM STATUS ACTIONS
// =====================================================
const fetchSystemStatus = async (): Promise<boolean> => {
try {
loading.systemStatus = true
const result = await api.getSystemStatus()
if (result) {
systemStatus.value = result
connectionStatus.api = true
connectionStatus.lastCheck = new Date().toISOString()
return true
}
connectionStatus.api = false
return false
} catch (error) {
connectionStatus.api = false
return false
} finally {
loading.systemStatus = false
}
}
const testConnections = async (): Promise<void> => {
// Test API connection
connectionStatus.api = await api.testConnection()
// Test WebSocket connection
if (!ws.connectionStatus.connected) {
connectionStatus.websocket = await ws.connect()
} else {
connectionStatus.websocket = true
}
connectionStatus.lastCheck = new Date().toISOString()
showNotification({
type: connectionStatus.api && connectionStatus.websocket ? 'success' : 'warning',
title: 'Connection Test',
message: `API: ${connectionStatus.api ? 'Connected' : 'Failed'}, WebSocket: ${connectionStatus.websocket ? 'Connected' : 'Failed'}`,
})
}
// =====================================================
// OPERATIONS TRACKING
// =====================================================
const addOperation = (agentId: string, operation: AgentOperation['operation']) => {
activeOperations.value.push({
agentId,
operation,
timestamp: new Date().toISOString()
})
}
const removeOperation = (agentId: string, operation: AgentOperation['operation']) => {
const index = activeOperations.value.findIndex(
op => op.agentId === agentId && op.operation === operation
)
if (index !== -1) {
activeOperations.value.splice(index, 1)
}
}
// =====================================================
// NOTIFICATIONS
// =====================================================
const showNotification = (notification: Omit<NotificationMessage, 'id' | 'timestamp'>) => {
const id = Date.now().toString() + Math.random().toString(36).substr(2, 9)
const fullNotification: NotificationMessage = {
...notification,
id,
timestamp: new Date().toISOString(),
duration: notification.duration || 5000
}
notifications.value.push(fullNotification)
// Auto-remove notification after duration
if (fullNotification.duration && fullNotification.duration > 0) {
setTimeout(() => {
removeNotification(id)
}, fullNotification.duration)
}
}
const removeNotification = (id: string) => {
const index = notifications.value.findIndex(n => n.id === id)
if (index !== -1) {
notifications.value.splice(index, 1)
}
}
const clearNotifications = () => {
notifications.value = []
}
// =====================================================
// WEBSOCKET INTEGRATION
// =====================================================
const initializeWebSocket = () => {
// Subscribe to agent updates
ws.subscribeToAgentUpdates((agentData: any) => {
console.log('πŸ€– Agent update received:', agentData)
if (agentData.agent) {
updateAgentInStore(agentData.agent)
}
})
// Subscribe to message updates
ws.subscribeToMessageUpdates((messageData: any) => {
console.log('πŸ’¬ Message update received:', messageData)
if (messageData.agent_id) {
// Add to chat history if not already present
const exists = chatHistory.value.some(
msg => msg.timestamp === messageData.timestamp &&
msg.agent_id === messageData.agent_id
)
if (!exists) {
chatHistory.value.push(messageData)
}
}
})
// Subscribe to system updates
ws.subscribeToSystemUpdates((statusData: any) => {
console.log('πŸ”§ System update received:', statusData)
if (statusData.agents) {
systemStatus.value = statusData
}
})
// Connect WebSocket
ws.connect().then(connected => {
connectionStatus.websocket = connected
if (connected) {
ws.startHeartbeat()
}
})
}
// =====================================================
// INITIALIZATION
// =====================================================
const initialize = async (): Promise<void> => {
console.log('πŸš€ Initializing SAAP Agent Store...')
// Initialize WebSocket
initializeWebSocket()
// Fetch initial data
await fetchSystemStatus()
await fetchAgents()
console.log('βœ… SAAP Agent Store initialized')
}
// =====================================================
// CLEANUP
// =====================================================
const cleanup = () => {
ws.disconnect()
ws.stopHeartbeat()
agents.value = []
selectedAgent.value = null
chatHistory.value = []
activeOperations.value = []
notifications.value = []
}
return {
// State
agents,
selectedAgent,
systemStatus,
chatHistory,
activeOperations,
notifications,
loading,
connectionStatus,
// Computed
activeAgents,
inactiveAgents,
agentsByType,
totalMessages,
averageResponseTime,
isOperationActive,
// Agent Management
fetchAgents,
selectAgent,
updateAgentInStore,
// Agent Lifecycle
startAgent,
stopAgent,
// Communication
chatWithAgent,
getChatHistory,
// System
fetchSystemStatus,
testConnections,
// Notifications
showNotification,
removeNotification,
clearNotifications,
// Lifecycle
initialize,
cleanup,
}
})
// Export types
export type { AgentOperation, NotificationMessage }