How the Vue 3 WebSocket Client Manages Real-Time Task Updates in MathModelAgent
The MathModelAgent frontend uses a custom TaskWebSocket wrapper class integrated with a Pinia store to handle WebSocket connections, automatically updating the UI through Vue 3's reactivity system when new task messages arrive.
The MathModelAgent project implements a clean, reactive pipeline for real-time task updates using Vue 3's Composition API and Pinia state management. The frontend establishes WebSocket connections through a lightweight wrapper that parses JSON payloads and feeds them into reactive store properties, enabling instant UI updates without polling. This architecture decouples connection management from UI components while maintaining type safety and predictable state flow.
The TaskWebSocket Wrapper Class
The frontend encapsulates the native WebSocket API in a minimal wrapper defined in [src/utils/websocket.ts](https://github.com/jihe520/mathmodelagent/blob/main/frontend/src/utils/websocket.ts). This TaskWebSocket class provides a typed interface for connection lifecycle management and message handling.
The wrapper maintains three core properties: a native WebSocket instance, the target URL, and an onMessage callback function. The connect() method opens the socket, attaches event listeners for incoming messages, and parses JSON payloads before forwarding them to the registered callback. For outgoing communication, the send(data) method serializes objects to JSON, while close() terminates the connection cleanly.
Pinia Store Integration
State management lives in [src/stores/task.ts](https://github.com/jihe520/mathmodelagent/blob/main/frontend/src/stores/task.ts), where the useTaskStore composable initializes and controls the WebSocket lifecycle.
Connection Management
The store exposes a connectWebSocket(taskId) method that constructs the WebSocket URL using the VITE_WS_URL environment variable, appends the specific task ID, and instantiates a TaskWebSocket with a message handler. Incoming messages are pushed into a reactive messages array that serves as the single source of truth for all real-time updates.
import { useTaskStore } from '@/stores/task'
export default {
setup() {
const taskStore = useTaskStore()
const taskId = '12345' // obtained from route or props
// Open WS connection on component mount
taskStore.connectWebSocket(taskId)
return {
chatMessages: taskStore.chatMessages,
addUserMessage: taskStore.addUserMessage,
}
},
}
Message Filtering and Computed Properties
The store defines computed properties such as chatMessages, coderMessages, and writerMessages that filter the raw messages array by msg_type and agent_type fields. Vue components subscribe to these computed properties rather than accessing the raw array directly, ensuring components receive only relevant data subsets for their specific views.
Managing the WebSocket Lifecycle
Unlike some auto-connecting implementations, the MathModelAgent store requires explicit lifecycle management. The UI component must call connectWebSocket(taskId) when mounting, typically when a user navigates to a specific task page.
import { useTaskStore } from '@/stores/task'
import { onBeforeUnmount } from 'vue'
export default {
setup() {
const taskStore = useTaskStore()
const taskId = '12345'
taskStore.connectWebSocket(taskId)
// Clean up on component unmount
onBeforeUnmount(() => {
taskStore.closeWebSocket()
})
return {
chatMessages: taskStore.chatMessages,
}
},
}
The closeWebSocket() helper method in the store terminates the connection and clears the instance, preventing memory leaks and orphaned connections when users navigate away from task pages.
Real-Time UI Updates
When the WebSocket receives a new message, the onMessage callback pushes the parsed data into the reactive messages array. Because the store uses Vue 3's reactivity system, any component subscribing to chatMessages, coderMessages, or other computed properties automatically re-renders to reflect the updated state.
Type definitions for the various message shapes are located in [src/utils/response.ts](https://github.com/jihe520/mathmodelagent/blob/main/frontend/src/utils/response.ts), ensuring TypeScript safety when handling different msg_type values across the application.
// Sending messages back to the backend if needed
taskStore.ws?.send({ type: 'ping' })
Summary
- Minimal wrapper: The
TaskWebSocketclass insrc/utils/websocket.tsabstracts native WebSocket complexity while exposingconnect(),send(), andclose()methods. - Centralized state: The Pinia store in
src/stores/task.tsmanages the connection lifecycle and maintains a reactivemessagesarray as the single source of truth. - Filtered views: Computed properties like
chatMessagesandcoderMessagesfilter raw messages by type, allowing components to subscribe only to relevant updates. - Explicit lifecycle: Components must manually initiate connections on mount and close them on unmount, preventing resource leaks and unwanted background connections.
Frequently Asked Questions
How do I connect to the WebSocket for a specific task?
Import the useTaskStore composable from @/stores/task and call connectWebSocket(taskId), passing the task identifier obtained from your route parameters or props. The method constructs the full WebSocket URL using the VITE_WS_URL environment variable and establishes the connection.
What triggers UI updates when new messages arrive?
The TaskWebSocket wrapper parses incoming JSON and pushes it to the store's reactive messages array. Vue 3's reactivity system detects this mutation and automatically updates any component subscribing to the store's computed properties (such as chatMessages or coderMessages), triggering a re-render without manual polling.
Where are message type definitions located?
Type definitions for WebSocket message payloads reside in [src/utils/response.ts](https://github.com/jihe520/mathmodelagent/blob/main/frontend/src/utils/response.ts). This file contains interfaces for different msg_type values and agent_type classifications, ensuring TypeScript type safety when processing real-time updates in the store and components.
How do I properly close the WebSocket connection?
Call closeWebSocket() on the task store instance, typically inside an onBeforeUnmount lifecycle hook in your Vue component. This method terminates the underlying WebSocket connection and cleans up the instance, preventing memory leaks and ensuring the connection doesn't persist after the user navigates away.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →