From 8569b615984dfd8eecf89300f32d4c0ffe1fde03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 15 Sep 2025 11:35:40 -0700 Subject: [PATCH] transports: on_client_disconnected only if remote client disconnects --- CHANGELOG.md | 5 +++++ src/pipecat/transports/smallwebrtc/transport.py | 6 +++++- src/pipecat/transports/websocket/fastapi.py | 11 +++++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55880578c..121558825 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed a `FastAPIWebsocketTransport` and `SmallWebRTCTransport` issue where + `on_client_disconnected` would be triggered when the bot ends the + conversation. That is, `on_client_disconnected` should only be triggered when + the remote client actually disconnects. + - Fixed an issue in `HeyGenVideoService` where the `BotStartedSpeakingFrame` was blocked from moving through the Pipeline. diff --git a/src/pipecat/transports/smallwebrtc/transport.py b/src/pipecat/transports/smallwebrtc/transport.py index 681674000..2abe2abf5 100644 --- a/src/pipecat/transports/smallwebrtc/transport.py +++ b/src/pipecat/transports/smallwebrtc/transport.py @@ -478,7 +478,11 @@ class SmallWebRTCClient: self._screen_video_track = None self._audio_output_track = None self._video_output_track = None - await self._callbacks.on_client_disconnected(self._webrtc_connection) + + # Trigger `on_client_disconnected` if the client actually disconnects, + # that is, we are not the ones disconnecting. + if not self._closing: + await self._callbacks.on_client_disconnected(self._webrtc_connection) async def _handle_app_message(self, message: Any): """Handle incoming application messages.""" diff --git a/src/pipecat/transports/websocket/fastapi.py b/src/pipecat/transports/websocket/fastapi.py index 474f70de8..cfa68f5cb 100644 --- a/src/pipecat/transports/websocket/fastapi.py +++ b/src/pipecat/transports/websocket/fastapi.py @@ -138,7 +138,6 @@ class FastAPIWebsocketClient: ): logger.warning("Closing already disconnected websocket!") self._closing = True - await self.trigger_client_disconnected() async def disconnect(self): """Disconnect the WebSocket client.""" @@ -152,8 +151,6 @@ class FastAPIWebsocketClient: await self._websocket.close() except Exception as e: logger.error(f"{self} exception while closing the websocket: {e}") - finally: - await self.trigger_client_disconnected() async def trigger_client_disconnected(self): """Trigger the client disconnected callback.""" @@ -298,7 +295,10 @@ class FastAPIWebsocketInputTransport(BaseInputTransport): except Exception as e: logger.error(f"{self} exception receiving data: {e.__class__.__name__} ({e})") - await self._client.trigger_client_disconnected() + # Trigger `on_client_disconnected` if the client actually disconnects, + # that is, we are not the ones disconnecting. + if not self._client.is_closing: + await self._client.trigger_client_disconnected() async def _monitor_websocket(self): """Wait for self._params.session_timeout seconds, if the websocket is still open, trigger timeout event.""" @@ -446,6 +446,9 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport): async def _write_frame(self, frame: Frame): """Serialize and send a frame through the WebSocket.""" + if self._client.is_closing or not self._client.is_connected: + return + if not self._params.serializer: return