improve error handling and don't swallow exceptions

This commit is contained in:
Aleix Conchillo Flaqué
2024-06-28 16:10:58 -07:00
parent abd65a93b2
commit fc0920504d
16 changed files with 38 additions and 35 deletions

View File

@@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed ### Fixed
- Fixed an issue where exceptions that occurred inside frame processors were
being swallowed and not displayed.
- Fixed an issue in `FastAPIWebsocketTransport` where it would still try to send - Fixed an issue in `FastAPIWebsocketTransport` where it would still try to send
data to the websocket after being closed. data to the websocket after being closed.

View File

@@ -82,5 +82,5 @@ class WakeCheckFilter(FrameProcessor):
await self.push_frame(frame, direction) await self.push_frame(frame, direction)
except Exception as e: except Exception as e:
error_msg = f"Error in wake word filter: {e}" error_msg = f"Error in wake word filter: {e}"
logger.error(error_msg) logger.exception(error_msg)
await self.push_error(ErrorFrame(error_msg)) await self.push_error(ErrorFrame(error_msg))

View File

@@ -147,12 +147,15 @@ class FrameProcessor:
await self.push_frame(error, FrameDirection.UPSTREAM) await self.push_frame(error, FrameDirection.UPSTREAM)
async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM): async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM):
if direction == FrameDirection.DOWNSTREAM and self._next: try:
logger.trace(f"Pushing {frame} from {self} to {self._next}") if direction == FrameDirection.DOWNSTREAM and self._next:
await self._next.process_frame(frame, direction) logger.trace(f"Pushing {frame} from {self} to {self._next}")
elif direction == FrameDirection.UPSTREAM and self._prev: await self._next.process_frame(frame, direction)
logger.trace(f"Pushing {frame} upstream from {self} to {self._prev}") elif direction == FrameDirection.UPSTREAM and self._prev:
await self._prev.process_frame(frame, direction) logger.trace(f"Pushing {frame} upstream from {self} to {self._prev}")
await self._prev.process_frame(frame, direction)
except Exception as e:
logger.exception(f"Uncaught exception in {self}: {e}")
def __str__(self): def __str__(self):
return self.name return self.name

View File

@@ -75,5 +75,6 @@ class LangchainProcessor(FrameProcessor):
except GeneratorExit: except GeneratorExit:
logger.warning(f"{self} generator was closed prematurely") logger.warning(f"{self} generator was closed prematurely")
except Exception as e: except Exception as e:
logger.error(f"{self} an unknown error occurred: {e}") logger.exception(f"{self} an unknown error occurred: {e}")
await self.push_frame(LLMFullResponseEndFrame()) finally:
await self.push_frame(LLMFullResponseEndFrame())

View File

@@ -122,7 +122,7 @@ class AnthropicLLMService(LLMService):
await self.push_frame(LLMResponseEndFrame()) await self.push_frame(LLMResponseEndFrame())
except Exception as e: except Exception as e:
logger.error(f"{self} exception: {e}") logger.exception(f"{self} exception: {e}")
finally: finally:
await self.push_frame(LLMFullResponseEndFrame()) await self.push_frame(LLMFullResponseEndFrame())

View File

@@ -39,7 +39,7 @@ class CartesiaTTSService(TTSService):
self._client = AsyncCartesia(api_key=self._api_key) self._client = AsyncCartesia(api_key=self._api_key)
self._voice = self._client.voices.get(id=voice_id) self._voice = self._client.voices.get(id=voice_id)
except Exception as e: except Exception as e:
logger.error(f"{self} initialization error: {e}") logger.exception(f"{self} initialization error: {e}")
def can_generate_metrics(self) -> bool: def can_generate_metrics(self) -> bool:
return True return True
@@ -62,4 +62,4 @@ class CartesiaTTSService(TTSService):
await self.stop_ttfb_metrics() await self.stop_ttfb_metrics()
yield AudioRawFrame(chunk["audio"], self._output_format["sample_rate"], 1) yield AudioRawFrame(chunk["audio"], self._output_format["sample_rate"], 1)
except Exception as e: except Exception as e:
logger.error(f"{self} exception: {e}") logger.exception(f"{self} exception: {e}")

View File

@@ -91,7 +91,7 @@ class DeepgramTTSService(TTSService):
frame = AudioRawFrame(audio=data, sample_rate=16000, num_channels=1) frame = AudioRawFrame(audio=data, sample_rate=16000, num_channels=1)
yield frame yield frame
except Exception as e: except Exception as e:
logger.error(f"{self} exception: {e}") logger.exception(f"{self} exception: {e}")
class DeepgramSTTService(AIService): class DeepgramSTTService(AIService):

View File

@@ -104,10 +104,10 @@ class GoogleLLMService(LLMService):
logger.debug( logger.debug(
f"LLM refused to generate content for safety reasons - {messages}.") f"LLM refused to generate content for safety reasons - {messages}.")
else: else:
logger.error(f"{self} error: {e}") logger.exception(f"{self} error: {e}")
except Exception as e: except Exception as e:
logger.error(f"{self} exception: {e}") logger.exception(f"{self} exception: {e}")
finally: finally:
await self.push_frame(LLMFullResponseEndFrame()) await self.push_frame(LLMFullResponseEndFrame())

View File

@@ -53,7 +53,7 @@ except ModuleNotFoundError as e:
raise Exception(f"Missing module: {e}") raise Exception(f"Missing module: {e}")
class OpenAIUnhandledFunctionException(BaseException): class OpenAIUnhandledFunctionException(Exception):
pass pass
@@ -109,10 +109,7 @@ class BaseOpenAILLMService(LLMService):
del message["data"] del message["data"]
del message["mime_type"] del message["mime_type"]
try: chunks = await self.get_chat_completions(context, messages)
chunks = await self.get_chat_completions(context, messages)
except Exception as e:
logger.error(f"{self} exception: {e}")
return chunks return chunks
@@ -214,7 +211,7 @@ class BaseOpenAILLMService(LLMService):
elif isinstance(result, type(None)): elif isinstance(result, type(None)):
pass pass
else: else:
raise BaseException(f"Unknown return type from function callback: {type(result)}") raise TypeError(f"Unknown return type from function callback: {type(result)}")
async def process_frame(self, frame: Frame, direction: FrameDirection): async def process_frame(self, frame: Frame, direction: FrameDirection):
await super().process_frame(frame, direction) await super().process_frame(frame, direction)
@@ -336,4 +333,4 @@ class OpenAITTSService(TTSService):
frame = AudioRawFrame(chunk, 24_000, 1) frame = AudioRawFrame(chunk, 24_000, 1)
yield frame yield frame
except BadRequestError as e: except BadRequestError as e:
logger.error(f"{self} error generating TTS: {e}") logger.exception(f"{self} error generating TTS: {e}")

View File

@@ -80,4 +80,4 @@ class PlayHTTTSService(TTSService):
frame = AudioRawFrame(chunk, 16000, 1) frame = AudioRawFrame(chunk, 16000, 1)
yield frame yield frame
except Exception as e: except Exception as e:
logger.error(f"{self} error generating TTS: {e}") logger.exception(f"{self} error generating TTS: {e}")

View File

@@ -173,5 +173,5 @@ class BaseInputTransport(FrameProcessor):
await self._internal_push_frame(frame) await self._internal_push_frame(frame)
except asyncio.CancelledError: except asyncio.CancelledError:
break break
except BaseException as e: except Exception as e:
logger.error(f"{self} error reading audio frames: {e}") logger.exception(f"{self} error reading audio frames: {e}")

View File

@@ -180,8 +180,8 @@ class BaseOutputTransport(FrameProcessor):
self._sink_queue.task_done() self._sink_queue.task_done()
except asyncio.CancelledError: except asyncio.CancelledError:
break break
except BaseException as e: except Exception as e:
logger.error(f"{self} error processing sink queue: {e}") logger.exception(f"{self} error processing sink queue: {e}")
# #
# Push frames task # Push frames task
@@ -250,7 +250,7 @@ class BaseOutputTransport(FrameProcessor):
except asyncio.CancelledError: except asyncio.CancelledError:
break break
except Exception as e: except Exception as e:
logger.error(f"{self} error writing to camera: {e}") logger.exception(f"{self} error writing to camera: {e}")
# #
# Audio out # Audio out

View File

@@ -82,5 +82,4 @@ class BaseTransport(ABC):
else: else:
handler(self, *args, **kwargs) handler(self, *args, **kwargs)
except Exception as e: except Exception as e:
logger.error(f"Exception in event handler {event_name}: {e}") logger.exception(f"Exception in event handler {event_name}: {e}")
raise e

View File

@@ -835,8 +835,8 @@ class DailyTransport(BaseTransport):
logger.debug("Event dialin-ready was handled successfully") logger.debug("Event dialin-ready was handled successfully")
except asyncio.TimeoutError: except asyncio.TimeoutError:
logger.error(f"Timeout handling dialin-ready event ({url})") logger.error(f"Timeout handling dialin-ready event ({url})")
except BaseException as e: except Exception as e:
logger.error(f"Error handling dialin-ready event ({url}): {e}") logger.exception(f"Error handling dialin-ready event ({url}): {e}")
async def _on_dialin_ready(self, sip_endpoint): async def _on_dialin_ready(self, sip_endpoint):
if self._params.dialin_settings: if self._params.dialin_settings:

View File

@@ -2,7 +2,7 @@ from typing import List
from pipecat.processors.frame_processor import FrameProcessor from pipecat.processors.frame_processor import FrameProcessor
class TestException(BaseException): class TestException(Exception):
pass pass

View File

@@ -72,9 +72,9 @@ class SileroVADAnalyzer(VADAnalyzer):
self._last_reset_time = curr_time self._last_reset_time = curr_time
return new_confidence return new_confidence
except BaseException as e: except Exception as e:
# This comes from an empty audio array # This comes from an empty audio array
logger.error(f"Error analyzing audio with Silero VAD: {e}") logger.exception(f"Error analyzing audio with Silero VAD: {e}")
return 0 return 0