Merge pull request #327 from pipecat-ai/aleix/bot-start-stop-speaking-frames

bot start stop speaking frames
This commit is contained in:
Aleix Conchillo Flaqué
2024-07-27 17:21:23 -07:00
committed by GitHub
4 changed files with 51 additions and 14 deletions

View File

@@ -9,10 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Added new `BotStartedSpeakingFrame` and `BotStoppedSpeakingFrame` control
frames. These frames are pushed upstream and they should wrap
`BotSpeakingFrame`.
- Transports now allow you to register event handlers without decorators. - Transports now allow you to register event handlers without decorators.
### Changed ### Changed
- `BotSpeakingFrame` is now a control frame.
- `StartFrame` is now a control frame similar to `EndFrame`. - `StartFrame` is now a control frame similar to `EndFrame`.
- `DeepgramTTSService` now is more customizable. You can adjust the encoding and - `DeepgramTTSService` now is more customizable. You can adjust the encoding and

View File

@@ -270,17 +270,6 @@ class BotInterruptionFrame(SystemFrame):
pass pass
@dataclass
class BotSpeakingFrame(SystemFrame):
"""Emitted by transport outputs while the bot is still speaking. This can be
used, for example, to detect when a user is idle. That is, while the bot is
speaking we don't want to trigger any user idle timeout since the user might
be listening.
"""
pass
@dataclass @dataclass
class MetricsFrame(SystemFrame): class MetricsFrame(SystemFrame):
"""Emitted by processor that can compute metrics like latencies. """Emitted by processor that can compute metrics like latencies.
@@ -348,6 +337,33 @@ class UserStoppedSpeakingFrame(ControlFrame):
pass pass
@dataclass
class BotStartedSpeakingFrame(ControlFrame):
"""Emitted upstream by transport outputs to indicate the bot started speaking.
"""
pass
@dataclass
class BotStoppedSpeakingFrame(ControlFrame):
"""Emitted upstream by transport outputs to indicate the bot stopped speaking.
"""
pass
@dataclass
class BotSpeakingFrame(ControlFrame):
"""Emitted upstream by transport outputs while the bot is still
speaking. This can be used, for example, to detect when a user is idle. That
is, while the bot is speaking we don't want to trigger any user idle timeout
since the user might be listening.
"""
pass
@dataclass @dataclass
class TTSStartedFrame(ControlFrame): class TTSStartedFrame(ControlFrame):
"""Used to indicate the beginning of a TTS response. Following """Used to indicate the beginning of a TTS response. Following

View File

@@ -8,7 +8,12 @@ import asyncio
from typing import Awaitable, Callable from typing import Awaitable, Callable
from pipecat.frames.frames import BotSpeakingFrame, Frame, StartInterruptionFrame, StopInterruptionFrame, SystemFrame from pipecat.frames.frames import (
BotSpeakingFrame,
Frame,
SystemFrame,
UserStartedSpeakingFrame,
UserStoppedSpeakingFrame)
from pipecat.processors.async_frame_processor import AsyncFrameProcessor from pipecat.processors.async_frame_processor import AsyncFrameProcessor
from pipecat.processors.frame_processor import FrameDirection from pipecat.processors.frame_processor import FrameDirection
@@ -47,10 +52,10 @@ class UserIdleProcessor(AsyncFrameProcessor):
await self.queue_frame(frame, direction) await self.queue_frame(frame, direction)
# We shouldn't call the idle callback if the user or the bot are speaking. # We shouldn't call the idle callback if the user or the bot are speaking.
if isinstance(frame, StartInterruptionFrame): if isinstance(frame, UserStartedSpeakingFrame):
self._interrupted = True self._interrupted = True
self._idle_event.set() self._idle_event.set()
elif isinstance(frame, StopInterruptionFrame): elif isinstance(frame, UserStoppedSpeakingFrame):
self._interrupted = False self._interrupted = False
self._idle_event.set() self._idle_event.set()
elif isinstance(frame, BotSpeakingFrame): elif isinstance(frame, BotSpeakingFrame):

View File

@@ -15,6 +15,8 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
from pipecat.frames.frames import ( from pipecat.frames.frames import (
AudioRawFrame, AudioRawFrame,
BotSpeakingFrame, BotSpeakingFrame,
BotStartedSpeakingFrame,
BotStoppedSpeakingFrame,
CancelFrame, CancelFrame,
MetricsFrame, MetricsFrame,
SpriteFrame, SpriteFrame,
@@ -25,6 +27,8 @@ from pipecat.frames.frames import (
StartInterruptionFrame, StartInterruptionFrame,
StopInterruptionFrame, StopInterruptionFrame,
SystemFrame, SystemFrame,
TTSStartedFrame,
TTSStoppedFrame,
TransportMessageFrame) TransportMessageFrame)
from pipecat.transports.base_transport import TransportParams from pipecat.transports.base_transport import TransportParams
@@ -172,6 +176,12 @@ class BaseOutputTransport(FrameProcessor):
await self._set_camera_images(frame.images) await self._set_camera_images(frame.images)
elif isinstance(frame, TransportMessageFrame): elif isinstance(frame, TransportMessageFrame):
await self.send_message(frame) await self.send_message(frame)
elif isinstance(frame, TTSStartedFrame):
await self._internal_push_frame(BotStartedSpeakingFrame(), FrameDirection.UPSTREAM)
await self._internal_push_frame(frame)
elif isinstance(frame, TTSStoppedFrame):
await self._internal_push_frame(BotStoppedSpeakingFrame(), FrameDirection.UPSTREAM)
await self._internal_push_frame(frame)
else: else:
await self._internal_push_frame(frame) await self._internal_push_frame(frame)