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 (
|
from pipecat.frames.frames import (
|
||||||
BotStartedSpeakingFrame,
|
BotStartedSpeakingFrame,
|
||||||
BotStoppedSpeakingFrame,
|
BotStoppedSpeakingFrame,
|
||||||
|
CancelFrame,
|
||||||
EndFrame,
|
EndFrame,
|
||||||
EndTaskFrame,
|
EndTaskFrame,
|
||||||
Frame,
|
Frame,
|
||||||
@@ -373,6 +374,8 @@ class SilenceWatchdog(FrameProcessor):
|
|||||||
self._prompts = 0
|
self._prompts = 0
|
||||||
self._bot_speaking = False
|
self._bot_speaking = False
|
||||||
self._ending = False
|
self._ending = False
|
||||||
|
self._stopped = False # call is closing/ended — never arm again
|
||||||
|
self._buf = ""
|
||||||
|
|
||||||
def _cancel(self):
|
def _cancel(self):
|
||||||
if self._timer and not self._timer.done():
|
if self._timer and not self._timer.done():
|
||||||
@@ -409,7 +412,19 @@ class SilenceWatchdog(FrameProcessor):
|
|||||||
|
|
||||||
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)
|
||||||
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._prompts = 0 # caller engaged again — reset
|
||||||
self._cancel()
|
self._cancel()
|
||||||
elif isinstance(frame, BotStartedSpeakingFrame):
|
elif isinstance(frame, BotStartedSpeakingFrame):
|
||||||
@@ -417,7 +432,9 @@ class SilenceWatchdog(FrameProcessor):
|
|||||||
self._cancel()
|
self._cancel()
|
||||||
elif isinstance(frame, BotStoppedSpeakingFrame):
|
elif isinstance(frame, BotStoppedSpeakingFrame):
|
||||||
self._bot_speaking = False
|
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())
|
asyncio.create_task(self._end_soon())
|
||||||
else:
|
else:
|
||||||
self._arm() # start counting silence once the agent finishes
|
self._arm() # start counting silence once the agent finishes
|
||||||
|
|||||||
Reference in New Issue
Block a user