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 ._websocket_disabled = 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,39 @@ 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 ,
97+ if self ._websocket_disabled :
98+ logger .warning (
99+ "SocketIOChatBridge is in debug mode. Not connecting websocket."
103100 )
101+ else :
102+ try :
103+ # Attempt to connect with timeout
104+ await asyncio .wait_for (
105+ self ._client .connect (
106+ url = self .websocket_url ,
107+ socketio_path = self .websocket_path ,
108+ headers = self .headers ,
109+ auth = self .auth ,
110+ transports = ["websocket" ],
111+ ),
112+ timeout = timeout ,
113+ )
104114
105- await asyncio .wait_for (self ._connected_event .wait (), timeout = timeout )
115+ await asyncio .wait_for (self ._connected_event .wait (), timeout = timeout )
106116
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
117+ except asyncio .TimeoutError as e :
118+ error_message = (
119+ f"Failed to connect to WebSocket server within { timeout } s timeout"
120+ )
121+ logger .error (error_message )
122+ await self ._cleanup_client ()
123+ raise RuntimeError (error_message ) from e
114124
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
125+ except Exception as e :
126+ error_message = f"Failed to connect to WebSocket server: { e } "
127+ logger .error (error_message )
128+ await self ._cleanup_client ()
129+ raise RuntimeError (error_message ) from e
120130
121131 async def disconnect (self ) -> None :
122132 """Close the WebSocket connection gracefully.
@@ -149,7 +159,7 @@ async def emit_message_event(
149159 if self ._client is None :
150160 raise RuntimeError ("WebSocket client not connected. Call connect() first." )
151161
152- if not self ._connected_event .is_set ():
162+ if not self ._connected_event .is_set () and not self . _websocket_disabled :
153163 raise RuntimeError ("WebSocket client not in connected state" )
154164
155165 try :
@@ -166,7 +176,12 @@ async def emit_message_event(
166176 mode = "json" , exclude_none = True , by_alias = True
167177 )
168178
169- await self ._client .emit ("ConversationEvent" , event_data )
179+ if self ._websocket_disabled :
180+ logger .info (
181+ f"SocketIOChatBridge is in debug mode. Not sending event: { json .dumps (event_data )} "
182+ )
183+ else :
184+ await self ._client .emit ("ConversationEvent" , event_data )
170185
171186 # Store the current message ID, used for emitting interrupt events.
172187 self ._current_message_id = message_event .message_id
@@ -184,7 +199,7 @@ async def emit_exchange_end_event(self) -> None:
184199 if self ._client is None :
185200 raise RuntimeError ("WebSocket client not connected. Call connect() first." )
186201
187- if not self ._connected_event .is_set ():
202+ if not self ._connected_event .is_set () and not self . _websocket_disabled :
188203 raise RuntimeError ("WebSocket client not in connected state" )
189204
190205 try :
@@ -200,7 +215,12 @@ async def emit_exchange_end_event(self) -> None:
200215 mode = "json" , exclude_none = True , by_alias = True
201216 )
202217
203- await self ._client .emit ("ConversationEvent" , event_data )
218+ if self ._websocket_disabled :
219+ logger .info (
220+ f"SocketIOChatBridge is in debug mode. Not sending event: { json .dumps (event_data )} "
221+ )
222+ else :
223+ await self ._client .emit ("ConversationEvent" , event_data )
204224
205225 except Exception as e :
206226 logger .error (f"Error sending conversation event to WebSocket: { e } " )
@@ -230,7 +250,12 @@ async def emit_interrupt_event(self, runtime_result: UiPathRuntimeResult):
230250 event_data = interrupt_event .model_dump (
231251 mode = "json" , exclude_none = True , by_alias = True
232252 )
233- await self ._client .emit ("ConversationEvent" , event_data )
253+ if self ._websocket_disabled :
254+ logger .info (
255+ f"SocketIOChatBridge is in debug mode. Not sending event: { json .dumps (event_data )} "
256+ )
257+ else :
258+ await self ._client .emit ("ConversationEvent" , event_data )
234259 except Exception as e :
235260 logger .warning (f"Error sending interrupt event: { e } " )
236261
@@ -315,6 +340,13 @@ def get_chat_bridge(
315340 websocket_url = f"wss://{ host } ?conversationId={ context .conversation_id } "
316341 websocket_path = "autopilotforeveryone_/websocket_/socket.io"
317342
343+ if os .environ .get ("CAS_WEBSOCKET_HOST" ):
344+ websocket_url = f"ws://{ os .environ .get ('CAS_WEBSOCKET_HOST' )} ?conversationId={ context .conversation_id } "
345+ websocket_path = "/socket.io"
346+ logger .warning (
347+ f"CAS_WEBSOCKET_HOST is set. Using websocket_url '{ websocket_url } { websocket_path } '."
348+ )
349+
318350 # Build headers from context
319351 headers = {
320352 "Authorization" : f"Bearer { os .environ .get ('UIPATH_ACCESS_TOKEN' , '' )} " ,
0 commit comments