Merge pull request #2995 from pipecat-ai/mb/fix-stt-mute-filter-stt-muting
fix: STTMuteFilter no longer sends STTMuteFrame
This commit is contained in:
@@ -66,6 +66,13 @@ reason")`.
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- `STTMuteFilter` no longer sends `STTMuteFrame` to the STT service. The filter
|
||||||
|
now blocks frames locally without instructing the STT service to stop
|
||||||
|
processing audio. This prevents inactivity-related errors (such as 409 errors
|
||||||
|
from Google STT) while maintaining the same muting behavior at the application
|
||||||
|
level. Important: The STTMuteFilter should be placed _after_ the STT service
|
||||||
|
itself.
|
||||||
|
|
||||||
- Bumped the `fastapi` dependency's upperbound to `<0.122.0`.
|
- Bumped the `fastapi` dependency's upperbound to `<0.122.0`.
|
||||||
|
|
||||||
- Updated the default model for `GoogleVertexLLMService` to `gemini-2.5-flash`.
|
- Updated the default model for `GoogleVertexLLMService` to `gemini-2.5-flash`.
|
||||||
|
|||||||
@@ -118,24 +118,16 @@ class STTMuteFilter(FrameProcessor):
|
|||||||
self._first_speech_handled = False
|
self._first_speech_handled = False
|
||||||
self._bot_is_speaking = False
|
self._bot_is_speaking = False
|
||||||
self._function_call_in_progress = False
|
self._function_call_in_progress = False
|
||||||
self._is_muted = False # Initialize as unmuted, will set state on StartFrame if needed
|
self._is_muted = False
|
||||||
|
|
||||||
@property
|
|
||||||
def is_muted(self) -> bool:
|
|
||||||
"""Check if STT is currently muted.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if STT is currently muted and audio frames are being suppressed.
|
|
||||||
"""
|
|
||||||
return self._is_muted
|
|
||||||
|
|
||||||
async def _handle_mute_state(self, should_mute: bool):
|
async def _handle_mute_state(self, should_mute: bool):
|
||||||
"""Handle STT muting and interruption control state changes."""
|
"""Handle STT muting and interruption control state changes."""
|
||||||
if should_mute != self.is_muted:
|
if should_mute != self._is_muted:
|
||||||
logger.debug(f"STTMuteFilter {'muting' if should_mute else 'unmuting'}")
|
logger.debug(f"STTMuteFilter {'muting' if should_mute else 'unmuting'}")
|
||||||
self._is_muted = should_mute
|
self._is_muted = should_mute
|
||||||
await self.push_frame(STTMuteFrame(mute=should_mute), FrameDirection.UPSTREAM)
|
# Note: We don't send STTMuteFrame to the STT service itself.
|
||||||
await self.push_frame(STTMuteFrame(mute=should_mute), FrameDirection.DOWNSTREAM)
|
# The filter blocks frames locally, but the STT service continues
|
||||||
|
# processing audio to keep streaming connections alive (e.g., Google STT).
|
||||||
|
|
||||||
async def _should_mute(self) -> bool:
|
async def _should_mute(self) -> bool:
|
||||||
"""Determine if STT should be muted based on current state and strategies."""
|
"""Determine if STT should be muted based on current state and strategies."""
|
||||||
@@ -215,7 +207,7 @@ class STTMuteFilter(FrameProcessor):
|
|||||||
),
|
),
|
||||||
):
|
):
|
||||||
# Only pass VAD-related frames when not muted
|
# Only pass VAD-related frames when not muted
|
||||||
if not self.is_muted:
|
if not self._is_muted:
|
||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
else:
|
else:
|
||||||
logger.trace(f"{frame.__class__.__name__} suppressed - STT currently muted")
|
logger.trace(f"{frame.__class__.__name__} suppressed - STT currently muted")
|
||||||
@@ -224,5 +216,5 @@ class STTMuteFilter(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
# Finally handle mute state change if needed
|
# Finally handle mute state change if needed
|
||||||
if should_mute is not None and should_mute != self.is_muted:
|
if should_mute is not None and should_mute != self._is_muted:
|
||||||
await self._handle_mute_state(should_mute)
|
await self._handle_mute_state(should_mute)
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ from pipecat.frames.frames import (
|
|||||||
FunctionCallResultFrame,
|
FunctionCallResultFrame,
|
||||||
InputAudioRawFrame,
|
InputAudioRawFrame,
|
||||||
InterimTranscriptionFrame,
|
InterimTranscriptionFrame,
|
||||||
STTMuteFrame,
|
|
||||||
TranscriptionFrame,
|
TranscriptionFrame,
|
||||||
UserStartedSpeakingFrame,
|
UserStartedSpeakingFrame,
|
||||||
UserStoppedSpeakingFrame,
|
UserStoppedSpeakingFrame,
|
||||||
@@ -49,9 +48,7 @@ class TestSTTMuteFilter(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
expected_returned_frames = [
|
expected_returned_frames = [
|
||||||
BotStartedSpeakingFrame,
|
BotStartedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=True
|
|
||||||
BotStoppedSpeakingFrame,
|
BotStoppedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=False
|
|
||||||
BotStartedSpeakingFrame,
|
BotStartedSpeakingFrame,
|
||||||
VADUserStartedSpeakingFrame, # Now passes through
|
VADUserStartedSpeakingFrame, # Now passes through
|
||||||
UserStartedSpeakingFrame, # Now passes through
|
UserStartedSpeakingFrame, # Now passes through
|
||||||
@@ -98,18 +95,14 @@ class TestSTTMuteFilter(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
expected_returned_frames = [
|
expected_returned_frames = [
|
||||||
BotStartedSpeakingFrame,
|
BotStartedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=True
|
|
||||||
BotStoppedSpeakingFrame,
|
BotStoppedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=False
|
|
||||||
VADUserStartedSpeakingFrame,
|
VADUserStartedSpeakingFrame,
|
||||||
UserStartedSpeakingFrame,
|
UserStartedSpeakingFrame,
|
||||||
InputAudioRawFrame,
|
InputAudioRawFrame,
|
||||||
VADUserStoppedSpeakingFrame,
|
VADUserStoppedSpeakingFrame,
|
||||||
UserStoppedSpeakingFrame,
|
UserStoppedSpeakingFrame,
|
||||||
BotStartedSpeakingFrame,
|
BotStartedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=True
|
|
||||||
BotStoppedSpeakingFrame,
|
BotStoppedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=False
|
|
||||||
]
|
]
|
||||||
|
|
||||||
await run_test(
|
await run_test(
|
||||||
@@ -144,9 +137,7 @@ class TestSTTMuteFilter(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
expected_returned_frames = [
|
expected_returned_frames = [
|
||||||
BotStartedSpeakingFrame,
|
BotStartedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=True
|
|
||||||
BotStoppedSpeakingFrame,
|
BotStoppedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=False
|
|
||||||
InterimTranscriptionFrame, # Only passes through after bot stops speaking
|
InterimTranscriptionFrame, # Only passes through after bot stops speaking
|
||||||
TranscriptionFrame, # Only passes through after bot stops speaking
|
TranscriptionFrame, # Only passes through after bot stops speaking
|
||||||
]
|
]
|
||||||
@@ -193,9 +184,7 @@ class TestSTTMuteFilter(unittest.IsolatedAsyncioTestCase):
|
|||||||
# VADUserStoppedSpeakingFrame,
|
# VADUserStoppedSpeakingFrame,
|
||||||
# UserStoppedSpeakingFrame,
|
# UserStoppedSpeakingFrame,
|
||||||
# FunctionCallInProgressFrame,
|
# FunctionCallInProgressFrame,
|
||||||
# STTMuteFrame, # mute=True
|
|
||||||
# FunctionCallResultFrame,
|
# FunctionCallResultFrame,
|
||||||
# STTMuteFrame, # mute=False
|
|
||||||
# VADUserStartedSpeakingFrame,
|
# VADUserStartedSpeakingFrame,
|
||||||
# UserStartedSpeakingFrame,
|
# UserStartedSpeakingFrame,
|
||||||
# VADUserStoppedSpeakingFrame,
|
# VADUserStoppedSpeakingFrame,
|
||||||
@@ -245,10 +234,8 @@ class TestSTTMuteFilter(unittest.IsolatedAsyncioTestCase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
expected_returned_frames = [
|
expected_returned_frames = [
|
||||||
STTMuteFrame, # mute=True after first speech
|
|
||||||
BotStartedSpeakingFrame,
|
BotStartedSpeakingFrame,
|
||||||
BotStoppedSpeakingFrame,
|
BotStoppedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=False after first speech
|
|
||||||
VADUserStartedSpeakingFrame,
|
VADUserStartedSpeakingFrame,
|
||||||
UserStartedSpeakingFrame,
|
UserStartedSpeakingFrame,
|
||||||
InputAudioRawFrame,
|
InputAudioRawFrame,
|
||||||
@@ -320,9 +307,7 @@ class TestSTTMuteFilter(unittest.IsolatedAsyncioTestCase):
|
|||||||
VADUserStoppedSpeakingFrame,
|
VADUserStoppedSpeakingFrame,
|
||||||
UserStoppedSpeakingFrame,
|
UserStoppedSpeakingFrame,
|
||||||
BotStartedSpeakingFrame,
|
BotStartedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=True
|
|
||||||
BotStoppedSpeakingFrame,
|
BotStoppedSpeakingFrame,
|
||||||
STTMuteFrame, # mute=False
|
|
||||||
VADUserStartedSpeakingFrame,
|
VADUserStartedSpeakingFrame,
|
||||||
UserStartedSpeakingFrame,
|
UserStartedSpeakingFrame,
|
||||||
InputAudioRawFrame,
|
InputAudioRawFrame,
|
||||||
|
|||||||
Reference in New Issue
Block a user