Stop spurious watchdog re-prompts after the call ends
The SilenceWatchdog armed a timer on the goodbye turn; it then fired ~silence_secs later, after EndCallProcessor had already hung up — logging phantom "re-prompt"s (3 of 5 in the last batch were after "Goodbye"). Now it stops for good on a closing keyword (LLMFullResponseEnd) or an EndFrame/CancelFrame, so it never re-arms once the call is closing. Real mid-call silences still re-prompt. Runtime-tested: no reprompt after goodbye / endframe; reprompt on normal silence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
21
bot.py
21
bot.py
@@ -25,6 +25,7 @@ from pipecat.audio.vad.vad_analyzer import VADParams
|
||||
from pipecat.frames.frames import (
|
||||
BotStartedSpeakingFrame,
|
||||
BotStoppedSpeakingFrame,
|
||||
CancelFrame,
|
||||
EndFrame,
|
||||
EndTaskFrame,
|
||||
Frame,
|
||||
@@ -373,6 +374,8 @@ class SilenceWatchdog(FrameProcessor):
|
||||
self._prompts = 0
|
||||
self._bot_speaking = False
|
||||
self._ending = False
|
||||
self._stopped = False # call is closing/ended — never arm again
|
||||
self._buf = ""
|
||||
|
||||
def _cancel(self):
|
||||
if self._timer and not self._timer.done():
|
||||
@@ -409,7 +412,19 @@ class SilenceWatchdog(FrameProcessor):
|
||||
|
||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||
await super().process_frame(frame, direction)
|
||||
if isinstance(frame, UserStartedSpeakingFrame):
|
||||
# Stop for good once the call is closing — otherwise a timer armed on the goodbye
|
||||
# turn fires ~silence_secs later, after the call already ended (spurious re-prompt).
|
||||
if isinstance(frame, (EndFrame, CancelFrame)):
|
||||
self._stopped = True
|
||||
self._cancel()
|
||||
elif isinstance(frame, LLMTextFrame):
|
||||
self._buf += frame.text
|
||||
elif isinstance(frame, LLMFullResponseEndFrame):
|
||||
if EndCallProcessor._is_closing(self._buf):
|
||||
self._stopped = True
|
||||
self._cancel()
|
||||
self._buf = ""
|
||||
elif isinstance(frame, UserStartedSpeakingFrame):
|
||||
self._prompts = 0 # caller engaged again — reset
|
||||
self._cancel()
|
||||
elif isinstance(frame, BotStartedSpeakingFrame):
|
||||
@@ -417,7 +432,9 @@ class SilenceWatchdog(FrameProcessor):
|
||||
self._cancel()
|
||||
elif isinstance(frame, BotStoppedSpeakingFrame):
|
||||
self._bot_speaking = False
|
||||
if self._ending:
|
||||
if self._stopped:
|
||||
pass # call ending — don't re-arm
|
||||
elif self._ending:
|
||||
asyncio.create_task(self._end_soon())
|
||||
else:
|
||||
self._arm() # start counting silence once the agent finishes
|
||||
|
||||
Reference in New Issue
Block a user