more robust cancellation
This commit is contained in:
@@ -81,6 +81,17 @@ class GreedyLLMAggregator(FrameProcessor):
|
|||||||
logger.debug(f"error: {e}")
|
logger.debug(f"error: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
class ClearableDeepgramTTSService(DeepgramTTSService):
|
||||||
|
def __init___(self, **kwargs):
|
||||||
|
super().__init(**kwargs)
|
||||||
|
|
||||||
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
|
await super().process_frame(frame, direction)
|
||||||
|
|
||||||
|
if isinstance(frame, StartInterruptionFrame):
|
||||||
|
self._current_sentence = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BufferedSentence:
|
class BufferedSentence:
|
||||||
audio_frames: List[AudioRawFrame] = field(default_factory=list)
|
audio_frames: List[AudioRawFrame] = field(default_factory=list)
|
||||||
@@ -144,8 +155,16 @@ class VADGate(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(frame, LLMFullResponseStartFrame):
|
# There are two ways we can be interrupted. During greedy inference, a new
|
||||||
|
# LLM response can start. Or, during playout, we can get a traditional
|
||||||
|
# user interruption frame.
|
||||||
|
if (isinstance(frame, LLMFullResponseStartFrame) or
|
||||||
|
isinstance(frame, StartInterruptionFrame)):
|
||||||
|
logger.debug(f"{frame} - Handle interruption in VADGate")
|
||||||
self._sentences = []
|
self._sentences = []
|
||||||
|
if self._audio_pusher_task:
|
||||||
|
self._audio_pusher_task.cancel()
|
||||||
|
self._audio_pusher_task = None
|
||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -166,9 +185,8 @@ class VADGate(FrameProcessor):
|
|||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
if not self._sentences:
|
if not self._sentences:
|
||||||
logger.debug("Audio buffer empty")
|
await asyncio.sleep(0.01)
|
||||||
self._audio_pusher_task = None
|
continue
|
||||||
return
|
|
||||||
|
|
||||||
if self.vad_analyzer._vad_state != VADState.QUIET:
|
if self.vad_analyzer._vad_state != VADState.QUIET:
|
||||||
await asyncio.sleep(0.01)
|
await asyncio.sleep(0.01)
|
||||||
@@ -190,12 +208,13 @@ class VADGate(FrameProcessor):
|
|||||||
# assume linear16 encoding (2 bytes per sample). todo: add some more
|
# assume linear16 encoding (2 bytes per sample). todo: add some more
|
||||||
# metadata to AudioRawFrame, maybe
|
# metadata to AudioRawFrame, maybe
|
||||||
duration += (len(frame.audio) / 2 / frame.num_channels) / sample_rate
|
duration += (len(frame.audio) / 2 / frame.num_channels) / sample_rate
|
||||||
|
await asyncio.sleep(duration - 20 / 1000)
|
||||||
if self.context:
|
if self.context:
|
||||||
|
logger.debug(f"Appending assistant message to context: [{s.text_frame.text}]")
|
||||||
self.context.messages.append(
|
self.context.messages.append(
|
||||||
{"role": "assistant", "content": s.text_frame.text}
|
{"role": "assistant", "content": s.text_frame.text}
|
||||||
)
|
)
|
||||||
await self.push_frame(s.text_frame)
|
await self.push_frame(s.text_frame)
|
||||||
await asyncio.sleep(duration - 20 / 1000)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Exception {e}")
|
logger.debug(f"Exception {e}")
|
||||||
@@ -215,7 +234,7 @@ async def main(room_url: str, token):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
tts = DeepgramTTSService(
|
tts = ClearableDeepgramTTSService(
|
||||||
aiohttp_session=session,
|
aiohttp_session=session,
|
||||||
api_key=os.getenv("DEEPGRAM_API_KEY"),
|
api_key=os.getenv("DEEPGRAM_API_KEY"),
|
||||||
voice="aura-asteria-en",
|
voice="aura-asteria-en",
|
||||||
|
|||||||
Reference in New Issue
Block a user