Overview
The /ws/ai WebSocket channel provides real-time notifications to AI analysis systems when surgeons complete surgical simulations. This enables immediate AI processing and feedback generation without polling the REST API.
Connection
Endpoint
ws://localhost:8080/ws/ai?token=<jwt-token>
Authentication
Requires a valid JWT token with ROLE_AI (or ROLE_IA ). Surgeon accounts cannot connect to this channel.
Connection Example
JavaScript/TypeScript
Python
Java
const aiToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' ;
const ws = new WebSocket ( `ws://localhost:8080/ws/ai?token= ${ aiToken } ` );
ws . onopen = () => {
console . log ( '🤖 AI system connected' );
};
ws . onmessage = ( event ) => {
const notification = JSON . parse ( event . data );
console . log ( '🔔 Received notification:' , notification );
if ( notification . event === 'NEW_SURGERY' ) {
handleNewSurgery ( notification . surgeryId );
}
};
ws . onerror = ( error ) => {
console . error ( '❌ WebSocket error:' , error );
};
ws . onclose = ( event ) => {
console . log ( `🔌 Disconnected: ${ event . code } ` );
};
Message Protocol
Server → Client: Connection Confirmation
Immediately after successful connection, the server sends a confirmation message:
Connection status, always "connected"
{
"status" : "connected" ,
"message" : "IA conectada exitosamente"
}
Server → Client: Surgery Notification
When a surgeon completes a simulation (sends FINISH event), the AI channel receives:
Event type, always "NEW_SURGERY"
Unique identifier of the completed surgery
{
"event" : "NEW_SURGERY" ,
"surgeryId" : "550e8400-e29b-41d4-a716-446655440000"
}
Client → Server: Optional Messages
AI systems can send messages to the server (currently for logging/debugging):
// Send acknowledgment (optional)
ws . send ( JSON . stringify ({
"status" : "PROCESSING" ,
"surgeryId" : "550e8400-e29b-41d4-a716-446655440000"
}));
The backend logs client messages but does not process them. This feature is reserved for future bidirectional communication.
Complete AI Workflow
AI System Connects
Establish persistent WebSocket connection with ROLE_AI token
Receive Confirmation
Server confirms connection with connected status message
Wait for Notifications
Listen for NEW_SURGERY events (blocking/event-driven)
Fetch Trajectory
On notification, call REST API: GET /api/v1/surgeries/{id}/trajectory
Analyze Surgery
Process trajectory data:
Calculate performance metrics
Detect errors (tumor touches, hemorrhages)
Evaluate movement smoothness
Generate textual feedback
Submit Analysis
Call REST API: POST /api/v1/surgeries/{id}/analysis
Continue Listening
Return to waiting for next NEW_SURGERY notification
Full Implementation Example
JavaScript/TypeScript
Python
import axios from 'axios' ;
class AIAnalysisSystem {
constructor ( aiToken , apiBaseUrl ) {
this . token = aiToken ;
this . apiBaseUrl = apiBaseUrl ;
this . ws = null ;
}
connect () {
this . ws = new WebSocket ( `ws://localhost:8080/ws/ai?token= ${ this . token } ` );
this . ws . onopen = () => {
console . log ( '🤖 AI system connected and ready' );
};
this . ws . onmessage = async ( event ) => {
const notification = JSON . parse ( event . data );
// Handle connection confirmation
if ( notification . status === 'connected' ) {
console . log ( '✅' , notification . message );
return ;
}
// Handle new surgery notification
if ( notification . event === 'NEW_SURGERY' ) {
console . log ( `🔔 New surgery: ${ notification . surgeryId } ` );
await this . processSurgery ( notification . surgeryId );
}
};
this . ws . onerror = ( error ) => {
console . error ( '❌ WebSocket error:' , error );
};
this . ws . onclose = ( event ) => {
console . log ( '🔌 Disconnected, reconnecting in 5s...' );
setTimeout (() => this . connect (), 5000 );
};
}
async processSurgery ( surgeryId ) {
try {
// 1. Fetch trajectory data
console . log ( `📥 Fetching trajectory for ${ surgeryId } ` );
const response = await axios . get (
` ${ this . apiBaseUrl } /surgeries/ ${ surgeryId } /trajectory` ,
{
headers: { Authorization: `Bearer ${ this . token } ` }
}
);
const trajectory = response . data ;
// 2. Analyze the surgery
console . log ( `🧠 Analyzing ${ trajectory . movements . length } movements...` );
const analysis = this . analyzeSurgery ( trajectory );
// 3. Submit analysis
console . log ( `📤 Submitting analysis (score: ${ analysis . score } )` );
await axios . post (
` ${ this . apiBaseUrl } /surgeries/ ${ surgeryId } /analysis` ,
analysis ,
{
headers: { Authorization: `Bearer ${ this . token } ` }
}
);
console . log ( `✅ Analysis complete for ${ surgeryId } ` );
} catch ( error ) {
console . error ( `❌ Error processing surgery ${ surgeryId } :` , error . message );
}
}
analyzeSurgery ( trajectory ) {
// Simple scoring algorithm (replace with actual AI model)
const movements = trajectory . movements ;
const duration = new Date ( trajectory . endTime ) - new Date ( trajectory . startTime );
const durationMinutes = duration / 60000 ;
// Count events
const tumorTouches = movements . filter ( m => m . event === 'TUMOR_TOUCH' ). length ;
const hemorrhages = movements . filter ( m => m . event === 'HEMORRHAGE' ). length ;
// Calculate score (0-100)
let score = 100 ;
score -= tumorTouches * 10 ; // -10 per tumor touch
score -= hemorrhages * 20 ; // -20 per hemorrhage
score -= Math . max ( 0 , durationMinutes - 15 ) * 2 ; // -2 per minute over 15
score = Math . max ( 0 , Math . min ( 100 , score ));
// Generate feedback
const feedback = `
Surgery completed in ${ durationMinutes . toFixed ( 1 ) } minutes.
Tumor contacts: ${ tumorTouches }
Hemorrhage events: ${ hemorrhages }
${ score >= 90 ? 'Excellent performance!' : '' }
${ score >= 70 && score < 90 ? 'Good performance with room for improvement.' : '' }
${ score < 70 ? 'Practice recommended to improve precision and speed.' : '' }
${ tumorTouches > 0 ? 'Minimize tumor contact to prevent spread.' : '' }
${ hemorrhages > 0 ? 'Focus on vascular control techniques.' : '' }
` . trim ();
return { score , feedback };
}
}
// Usage
const aiSystem = new AIAnalysisSystem (
'your-ai-jwt-token' ,
'http://localhost:8080/api/v1'
);
aiSystem . connect ();
Error Handling
Authentication Errors
Invalid token or wrong role results in immediate disconnection:
ws . onclose = ( event ) => {
if ( event . code === 1008 ) {
console . error ( '❌ Authentication failed: ROLE_AI token required' );
// AI token may have expired, refresh and reconnect
}
};
Reconnection Strategy
Implement automatic reconnection with exponential backoff:
class AIWebSocketClient {
constructor ( token ) {
this . token = token ;
this . reconnectDelay = 1000 ;
this . maxReconnectDelay = 30000 ;
}
connect () {
this . ws = new WebSocket ( `ws://localhost:8080/ws/ai?token= ${ this . token } ` );
this . ws . onopen = () => {
console . log ( '✅ Connected' );
this . reconnectDelay = 1000 ; // Reset delay on success
};
this . ws . onclose = () => {
console . log ( `🔄 Reconnecting in ${ this . reconnectDelay / 1000 } s...` );
setTimeout (() => this . connect (), this . reconnectDelay );
// Exponential backoff
this . reconnectDelay = Math . min (
this . reconnectDelay * 2 ,
this . maxReconnectDelay
);
};
}
}
Backend Implementation
The AI channel handler (AIWebSocketHandler.java) performs:
Validate Role
Check that connecting user has ROLE_AI
Register Session
Add WebSocket session to active AI sessions map
Send Confirmation
Send connection confirmation message
Listen for Notifications
Static method notificarNuevaCirugia() broadcasts to all AI sessions
Handle Disconnection
Remove session from map on close
Notification Trigger
Notifications are sent from SimulationWebSocketHandler.java when a surgery finishes:
// When FINISH event is received:
if ( movement . event () == SurgeryEvent . FINISH ) {
surgery . endSurgery ();
surgeryRepository . save (surgery);
activeSessions . remove ( session . getId ());
// Send confirmation to surgeon
String response = String . format (
"{ \" status \" : \" SAVED \" , \" surgeryId \" : \" %s \" }" ,
surgery . getId ()
);
session . sendMessage ( new TextMessage (response));
// Notify all connected AI systems
AIWebSocketHandler . notificarNuevaCirugia ( surgery . getId ());
}
Multi-AI Support
The channel supports multiple concurrent AI connections:
All connected AI systems receive notifications
Each AI can process surgeries independently
Useful for:
Load balancing across AI instances
Running multiple AI models simultaneously
Development/staging environments
If multiple AI systems submit analysis for the same surgery, the last submission overwrites previous results.
Notification Latency
Notifications are sent immediately after surgery persistence:
Typical latency: < 100ms
No polling delay
Synchronous broadcast to all AI sessions
Connection Stability
Recommendations for production:
Implement heartbeat/ping mechanism
Monitor connection health
Log all notifications for audit trail
Handle temporary network interruptions gracefully
Next Steps
Surgery Endpoints Fetch trajectory data and submit AI analysis
Simulation Channel Learn how surgeons stream telemetry data