From 144ae9b61169e53d6ad1bbc03d57646a3354b9a1 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 29 Apr 2025 08:47:23 -0400 Subject: [PATCH 1/2] Handle case where Fal Smart Turn returns a 500 error --- CHANGELOG.md | 10 +++++++--- .../audio/turn/smart_turn/http_smart_turn.py | 20 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f6af9e3f..2675da132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,9 +58,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated -- Function calls with parameters `(function_name, tool_call_id, args, llm, - context, result_callback)` are deprectated, use a single `FunctionCallParams` - parameter instead. +- Function calls with parameters + `(function_name, tool_call_id, args, llm, context, result_callback)` are + deprectated, use a single `FunctionCallParams` parameter instead. - `TransportParams.camera_*` parameters are now deprecated, use `TransportParams.video_*` instead. @@ -73,6 +73,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue with HTTP Smart Turn handling, where the service returns a 500 + error. Previously, this would cause an unhandled exception. Now, a 500 error + is treated as an incomplete response. + - Fixed a TTS services issue that could cause assistant output not to be aggregated to the context when also using `TTSSpeakFrame`s. diff --git a/src/pipecat/audio/turn/smart_turn/http_smart_turn.py b/src/pipecat/audio/turn/smart_turn/http_smart_turn.py index 4f542f81d..7ca6c5673 100644 --- a/src/pipecat/audio/turn/smart_turn/http_smart_turn.py +++ b/src/pipecat/audio/turn/smart_turn/http_smart_turn.py @@ -40,7 +40,6 @@ class HttpSmartTurnAnalyzer(BaseSmartTurn): async def _send_raw_request(self, data_bytes: bytes) -> Dict[str, Any]: headers = {"Content-Type": "application/octet-stream"} headers.update(self._headers) - logger.trace(f"Sending {len(data_bytes)} bytes as raw body to {self._url}...") try: timeout = aiohttp.ClientTimeout(total=self._params.stop_secs) @@ -66,7 +65,11 @@ class HttpSmartTurnAnalyzer(BaseSmartTurn): error_text = await response.text() logger.trace("Response Content (Error):") logger.trace(error_text) - response.raise_for_status() + if response.status == 500: + logger.warning(f"Smart turn service returned 500 error: {error_text}") + raise Exception(f"Server returned HTTP 500: {error_text}") + else: + response.raise_for_status() except asyncio.TimeoutError: logger.error(f"Request timed out after {self._params.stop_secs} seconds") @@ -76,5 +79,14 @@ class HttpSmartTurnAnalyzer(BaseSmartTurn): raise Exception("Failed to send raw request to Daily Smart Turn.") async def _predict_endpoint(self, audio_array: np.ndarray) -> Dict[str, Any]: - serialized_array = self._serialize_array(audio_array) - return await self._send_raw_request(serialized_array) + try: + serialized_array = self._serialize_array(audio_array) + return await self._send_raw_request(serialized_array) + except Exception as e: + logger.error(f"Smart turn prediction failed: {str(e)}") + # Return an incomplete prediction when a failure occurs + return { + "prediction": 0, + "probability": 0.0, + "metrics": {"inference_time": 0.0, "total_time": 0.0}, + } From ebed1fc6ea15bcfd74fbba84fed84e129d720df1 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 29 Apr 2025 09:05:14 -0400 Subject: [PATCH 2/2] Restructure _send_raw_request to raise errors early, then process the successful response --- .../audio/turn/smart_turn/http_smart_turn.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/pipecat/audio/turn/smart_turn/http_smart_turn.py b/src/pipecat/audio/turn/smart_turn/http_smart_turn.py index 7ca6c5673..5bd3743ed 100644 --- a/src/pipecat/audio/turn/smart_turn/http_smart_turn.py +++ b/src/pipecat/audio/turn/smart_turn/http_smart_turn.py @@ -40,6 +40,7 @@ class HttpSmartTurnAnalyzer(BaseSmartTurn): async def _send_raw_request(self, data_bytes: bytes) -> Dict[str, Any]: headers = {"Content-Type": "application/octet-stream"} headers.update(self._headers) + try: timeout = aiohttp.ClientTimeout(total=self._params.stop_secs) @@ -49,28 +50,31 @@ class HttpSmartTurnAnalyzer(BaseSmartTurn): logger.trace("\n--- Response ---") logger.trace(f"Status Code: {response.status}") - if response.status == 200: - try: - json_data = await response.json() - logger.trace("Response JSON:") - logger.trace(json_data) - return json_data - except aiohttp.ContentTypeError: - # Non-JSON response - text = await response.text() - logger.trace("Response Content (non-JSON):") - logger.trace(text) - raise Exception(f"Non-JSON response: {text}") - else: + # Check if successful + if response.status != 200: error_text = await response.text() logger.trace("Response Content (Error):") logger.trace(error_text) + if response.status == 500: logger.warning(f"Smart turn service returned 500 error: {error_text}") raise Exception(f"Server returned HTTP 500: {error_text}") else: response.raise_for_status() + # Process successful response + try: + json_data = await response.json() + logger.trace("Response JSON:") + logger.trace(json_data) + return json_data + except aiohttp.ContentTypeError: + # Non-JSON response + text = await response.text() + logger.trace("Response Content (non-JSON):") + logger.trace(text) + raise Exception(f"Non-JSON response: {text}") + except asyncio.TimeoutError: logger.error(f"Request timed out after {self._params.stop_secs} seconds") raise SmartTurnTimeoutException(f"Request exceeded {self._params.stop_secs} seconds.")