11"""Chat bridge implementations for conversational agents."""
22
33import asyncio
4+ import json
45import logging
56import os
67import uuid
@@ -57,6 +58,10 @@ def __init__(
5758 self ._client : AsyncClient | None = None
5859 self ._connected_event = asyncio .Event ()
5960
61+ # Set CAS_WEBSOCKET_DISABLED when using the debugger to prevent websocket errors from
62+ # interrupting the debugging session. Events will be logged instead of being sent.
63+ self ._debug_mode = os .environ .get ("CAS_WEBSOCKET_DISABLED" ) == "true"
64+
6065 async def connect (self , timeout : float = 10.0 ) -> None :
6166 """Establish WebSocket connection to the server.
6267
@@ -89,34 +94,37 @@ async def connect(self, timeout: float = 10.0) -> None:
8994
9095 self ._connected_event .clear ()
9196
92- try :
93- # Attempt to connect with timeout
94- await asyncio .wait_for (
95- self ._client .connect (
96- url = self .websocket_url ,
97- socketio_path = self .websocket_path ,
98- headers = self .headers ,
99- auth = self .auth ,
100- transports = ["websocket" ],
101- ),
102- timeout = timeout ,
103- )
97+ if self ._debug_mode :
98+ logger .warning ("SocketIOChatBridge is in debug mode. Not connecting websocket." )
99+ else :
100+ try :
101+ # Attempt to connect with timeout
102+ await asyncio .wait_for (
103+ self ._client .connect (
104+ url = self .websocket_url ,
105+ socketio_path = self .websocket_path ,
106+ headers = self .headers ,
107+ auth = self .auth ,
108+ transports = ["websocket" ],
109+ ),
110+ timeout = timeout ,
111+ )
104112
105- await asyncio .wait_for (self ._connected_event .wait (), timeout = timeout )
113+ await asyncio .wait_for (self ._connected_event .wait (), timeout = timeout )
106114
107- except asyncio .TimeoutError as e :
108- error_message = (
109- f"Failed to connect to WebSocket server within { timeout } s timeout"
110- )
111- logger .error (error_message )
112- await self ._cleanup_client ()
113- raise RuntimeError (error_message ) from e
115+ except asyncio .TimeoutError as e :
116+ error_message = (
117+ f"Failed to connect to WebSocket server within { timeout } s timeout"
118+ )
119+ logger .error (error_message )
120+ await self ._cleanup_client ()
121+ raise RuntimeError (error_message ) from e
114122
115- except Exception as e :
116- error_message = f"Failed to connect to WebSocket server: { e } "
117- logger .error (error_message )
118- await self ._cleanup_client ()
119- raise RuntimeError (error_message ) from e
123+ except Exception as e :
124+ error_message = f"Failed to connect to WebSocket server: { e } "
125+ logger .error (error_message )
126+ await self ._cleanup_client ()
127+ raise RuntimeError (error_message ) from e
120128
121129 async def disconnect (self ) -> None :
122130 """Close the WebSocket connection gracefully.
@@ -149,7 +157,7 @@ async def emit_message_event(
149157 if self ._client is None :
150158 raise RuntimeError ("WebSocket client not connected. Call connect() first." )
151159
152- if not self ._connected_event .is_set ():
160+ if not self ._connected_event .is_set () and not self . _debug_mode :
153161 raise RuntimeError ("WebSocket client not in connected state" )
154162
155163 try :
@@ -166,7 +174,10 @@ async def emit_message_event(
166174 mode = "json" , exclude_none = True , by_alias = True
167175 )
168176
169- await self ._client .emit ("ConversationEvent" , event_data )
177+ if self ._debug_mode :
178+ logger .info (f"SocketIOChatBridge is in debug mode. Not sending event: { json .dumps (event_data )} " )
179+ else :
180+ await self ._client .emit ("ConversationEvent" , event_data )
170181
171182 # Store the current message ID, used for emitting interrupt events.
172183 self ._current_message_id = message_event .message_id
@@ -184,7 +195,7 @@ async def emit_exchange_end_event(self) -> None:
184195 if self ._client is None :
185196 raise RuntimeError ("WebSocket client not connected. Call connect() first." )
186197
187- if not self ._connected_event .is_set ():
198+ if not self ._connected_event .is_set () and not self . _debug_mode :
188199 raise RuntimeError ("WebSocket client not in connected state" )
189200
190201 try :
@@ -200,7 +211,10 @@ async def emit_exchange_end_event(self) -> None:
200211 mode = "json" , exclude_none = True , by_alias = True
201212 )
202213
203- await self ._client .emit ("ConversationEvent" , event_data )
214+ if self ._debug_mode :
215+ logger .info (f"SocketIOChatBridge is in debug mode. Not sending event: { json .dumps (event_data )} " )
216+ else :
217+ await self ._client .emit ("ConversationEvent" , event_data )
204218
205219 except Exception as e :
206220 logger .error (f"Error sending conversation event to WebSocket: { e } " )
@@ -230,7 +244,10 @@ async def emit_interrupt_event(self, runtime_result: UiPathRuntimeResult):
230244 event_data = interrupt_event .model_dump (
231245 mode = "json" , exclude_none = True , by_alias = True
232246 )
233- await self ._client .emit ("ConversationEvent" , event_data )
247+ if self ._debug_mode :
248+ logger .info (f"SocketIOChatBridge is in debug mode. Not sending event: { json .dumps (event_data )} " )
249+ else :
250+ await self ._client .emit ("ConversationEvent" , event_data )
234251 except Exception as e :
235252 logger .warning (f"Error sending interrupt event: { e } " )
236253
@@ -315,6 +332,11 @@ def get_chat_bridge(
315332 websocket_url = f"wss://{ host } ?conversationId={ context .conversation_id } "
316333 websocket_path = "autopilotforeveryone_/websocket_/socket.io"
317334
335+ if os .environ .get ("CAS_WEBSOCKET_HOST" ):
336+ websocket_url = f"ws://{ os .environ .get ('CAS_WEBSOCKET_HOST' )} ?conversationId={ context .conversation_id } "
337+ websocket_path = "/socket.io"
338+ logger .warning (f"CAS_WEBSOCKET_HOST is set. Using websocket_url '{ websocket_url } { websocket_path } '." )
339+
318340 # Build headers from context
319341 headers = {
320342 "Authorization" : f"Bearer { os .environ .get ('UIPATH_ACCESS_TOKEN' , '' )} " ,
0 commit comments