improve error handling and don't swallow exceptions
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user