Spaces:
Sleeping
Sleeping
| /** | |
| * 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 } |