@@ -28,6 +28,10 @@ export default class BaseAutoGPTServerAPI {
28
28
private wsConnecting : Promise < void > | null = null ;
29
29
private wsMessageHandlers : Record < string , Set < ( data : any ) => void > > = { } ;
30
30
private supabaseClient : SupabaseClient | null = null ;
31
+ heartbeatInterval : number | null = null ;
32
+ readonly HEARTBEAT_INTERVAL = 30000 ; // 30 seconds
33
+ readonly HEARTBEAT_TIMEOUT = 10000 ; // 10 seconds
34
+ heartbeatTimeoutId : number | null = null ;
31
35
32
36
constructor (
33
37
baseUrl : string = process . env . NEXT_PUBLIC_AGPT_SERVER_URL ||
@@ -324,34 +328,84 @@ export default class BaseAutoGPTServerAPI {
324
328
}
325
329
}
326
330
331
+ startHeartbeat ( ) {
332
+ this . stopHeartbeat ( ) ;
333
+ this . heartbeatInterval = window . setInterval ( ( ) => {
334
+ if ( this . webSocket ?. readyState === WebSocket . OPEN ) {
335
+ this . webSocket . send (
336
+ JSON . stringify ( {
337
+ method : "heartbeat" ,
338
+ data : "ping" ,
339
+ success : true ,
340
+ } ) ,
341
+ ) ;
342
+
343
+ this . heartbeatTimeoutId = window . setTimeout ( ( ) => {
344
+ console . log ( "Heartbeat timeout - reconnecting" ) ;
345
+ this . webSocket ?. close ( ) ;
346
+ this . connectWebSocket ( ) ;
347
+ } , this . HEARTBEAT_TIMEOUT ) ;
348
+ }
349
+ } , this . HEARTBEAT_INTERVAL ) ;
350
+ }
351
+
352
+ stopHeartbeat ( ) {
353
+ if ( this . heartbeatInterval ) {
354
+ clearInterval ( this . heartbeatInterval ) ;
355
+ this . heartbeatInterval = null ;
356
+ }
357
+ if ( this . heartbeatTimeoutId ) {
358
+ clearTimeout ( this . heartbeatTimeoutId ) ;
359
+ this . heartbeatTimeoutId = null ;
360
+ }
361
+ }
362
+
363
+ handleHeartbeatResponse ( ) {
364
+ if ( this . heartbeatTimeoutId ) {
365
+ clearTimeout ( this . heartbeatTimeoutId ) ;
366
+ this . heartbeatTimeoutId = null ;
367
+ }
368
+ }
369
+
327
370
async connectWebSocket ( ) : Promise < void > {
328
371
this . wsConnecting ??= new Promise ( async ( resolve , reject ) => {
329
372
try {
330
373
const token =
331
374
( await this . supabaseClient ?. auth . getSession ( ) ) ?. data . session
332
375
?. access_token || "" ;
333
-
334
376
const wsUrlWithToken = `${ this . wsUrl } ?token=${ token } ` ;
335
377
this . webSocket = new WebSocket ( wsUrlWithToken ) ;
336
378
337
379
this . webSocket . onopen = ( ) => {
338
- console . debug ( "WebSocket connection established" ) ;
380
+ console . log ( "WebSocket connection established" ) ;
381
+ this . startHeartbeat ( ) ; // Start heartbeat when connection opens
339
382
resolve ( ) ;
340
383
} ;
341
384
342
385
this . webSocket . onclose = ( event ) => {
343
- console . debug ( "WebSocket connection closed" , event ) ;
386
+ console . log ( "WebSocket connection closed" , event ) ;
387
+ this . stopHeartbeat ( ) ; // Stop heartbeat when connection closes
344
388
this . webSocket = null ;
389
+ // Attempt to reconnect after a delay
390
+ setTimeout ( ( ) => this . connectWebSocket ( ) , 1000 ) ;
345
391
} ;
346
392
347
393
this . webSocket . onerror = ( error ) => {
348
394
console . error ( "WebSocket error:" , error ) ;
395
+ this . stopHeartbeat ( ) ; // Stop heartbeat on error
349
396
reject ( error ) ;
350
397
} ;
351
398
352
399
this . webSocket . onmessage = ( event ) => {
353
400
const message : WebsocketMessage = JSON . parse ( event . data ) ;
354
- if ( message . method == "execution_event" ) {
401
+
402
+ // Handle heartbeat response
403
+ if ( message . method === "heartbeat" && message . data === "pong" ) {
404
+ this . handleHeartbeatResponse ( ) ;
405
+ return ;
406
+ }
407
+
408
+ if ( message . method === "execution_event" ) {
355
409
message . data = parseNodeExecutionResultTimestamps ( message . data ) ;
356
410
}
357
411
this . wsMessageHandlers [ message . method ] ?. forEach ( ( handler ) =>
@@ -367,6 +421,7 @@ export default class BaseAutoGPTServerAPI {
367
421
}
368
422
369
423
disconnectWebSocket ( ) {
424
+ this . stopHeartbeat ( ) ; // Stop heartbeat when disconnecting
370
425
if ( this . webSocket && this . webSocket . readyState === WebSocket . OPEN ) {
371
426
this . webSocket . close ( ) ;
372
427
}
@@ -423,6 +478,7 @@ type GraphCreateRequestBody =
423
478
type WebsocketMessageTypeMap = {
424
479
subscribe : { graph_id : string } ;
425
480
execution_event : NodeExecutionResult ;
481
+ heartbeat : "ping" | "pong" ;
426
482
} ;
427
483
428
484
type WebsocketMessage = {
0 commit comments