processors(realtime-ai): add support making TTS to speak
This commit is contained in:
@@ -177,6 +177,15 @@ class LLMMessagesUpdateFrame(DataFrame):
|
|||||||
messages: List[dict]
|
messages: List[dict]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TTSSpeakFrame(DataFrame):
|
||||||
|
"""A frame that contains a text that should be spoken by the TTS in the
|
||||||
|
pipeline (if any).
|
||||||
|
|
||||||
|
"""
|
||||||
|
text: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TransportMessageFrame(DataFrame):
|
class TransportMessageFrame(DataFrame):
|
||||||
message: Any
|
message: Any
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from pipecat.frames.frames import (
|
|||||||
LLMMessagesUpdateFrame,
|
LLMMessagesUpdateFrame,
|
||||||
LLMModelUpdateFrame,
|
LLMModelUpdateFrame,
|
||||||
StartFrame,
|
StartFrame,
|
||||||
|
TTSSpeakFrame,
|
||||||
TTSVoiceUpdateFrame,
|
TTSVoiceUpdateFrame,
|
||||||
TransportMessageFrame)
|
TransportMessageFrame)
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -57,9 +58,19 @@ class RealtimeAISetup(BaseModel):
|
|||||||
config: Optional[RealtimeAIConfig] = None
|
config: Optional[RealtimeAIConfig] = None
|
||||||
|
|
||||||
|
|
||||||
|
class RealtimeAILLMMessageData(BaseModel):
|
||||||
|
messages: List[dict]
|
||||||
|
|
||||||
|
|
||||||
|
class RealtimeAITTSMessageData(BaseModel):
|
||||||
|
text: str
|
||||||
|
|
||||||
|
|
||||||
class RealtimeAIMessageData(BaseModel):
|
class RealtimeAIMessageData(BaseModel):
|
||||||
setup: Optional[RealtimeAISetup] = None
|
setup: Optional[RealtimeAISetup] = None
|
||||||
config: Optional[RealtimeAIConfig] = None
|
config: Optional[RealtimeAIConfig] = None
|
||||||
|
llm: Optional[RealtimeAILLMMessageData] = None
|
||||||
|
tts: Optional[RealtimeAITTSMessageData] = None
|
||||||
|
|
||||||
|
|
||||||
class RealtimeAIMessage(BaseModel):
|
class RealtimeAIMessage(BaseModel):
|
||||||
@@ -135,9 +146,11 @@ class RealtimeAIProcessor(FrameProcessor):
|
|||||||
case "llm-get-context":
|
case "llm-get-context":
|
||||||
await self._handle_llm_get_context()
|
await self._handle_llm_get_context()
|
||||||
case "llm-append-context":
|
case "llm-append-context":
|
||||||
await self._handle_llm_append_context(message.data.config)
|
await self._handle_llm_append_context(message.data.llm)
|
||||||
case "llm-update-context":
|
case "llm-update-context":
|
||||||
await self._handle_llm_update_context(message.data.config)
|
await self._handle_llm_update_context(message.data.llm)
|
||||||
|
case "tts-speak":
|
||||||
|
await self._handle_tts_speak(message.data.tts)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
await self._send_response(message.type, False, f"invalid message: {e}")
|
await self._send_response(message.type, False, f"invalid message: {e}")
|
||||||
|
|
||||||
@@ -206,14 +219,19 @@ class RealtimeAIProcessor(FrameProcessor):
|
|||||||
message = TransportMessageFrame(message=response.model_dump(exclude_none=True))
|
message = TransportMessageFrame(message=response.model_dump(exclude_none=True))
|
||||||
await self.push_frame(message)
|
await self.push_frame(message)
|
||||||
|
|
||||||
async def _handle_llm_append_context(self, config: RealtimeAIConfig):
|
async def _handle_llm_append_context(self, data: RealtimeAILLMMessageData):
|
||||||
if config.llm and config.llm.messages:
|
if data and data.messages:
|
||||||
frame = LLMMessagesAppendFrame(config.llm.messages)
|
frame = LLMMessagesAppendFrame(data.messages)
|
||||||
await self.push_frame(frame)
|
await self.push_frame(frame)
|
||||||
|
|
||||||
async def _handle_llm_update_context(self, config: RealtimeAIConfig):
|
async def _handle_llm_update_context(self, data: RealtimeAILLMMessageData):
|
||||||
if config.llm and config.llm.messages:
|
if data and data.messages:
|
||||||
frame = LLMMessagesUpdateFrame(config.llm.messages)
|
frame = LLMMessagesUpdateFrame(data.messages)
|
||||||
|
await self.push_frame(frame)
|
||||||
|
|
||||||
|
async def _handle_tts_speak(self, data: RealtimeAITTSMessageData):
|
||||||
|
if data and data.text:
|
||||||
|
frame = TTSSpeakFrame(text=data.text)
|
||||||
await self.push_frame(frame)
|
await self.push_frame(frame)
|
||||||
|
|
||||||
async def _send_response(self, type: str, success: bool, error: str | None = None):
|
async def _send_response(self, type: str, success: bool, error: str | None = None):
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from pipecat.frames.frames import (
|
|||||||
LLMFullResponseEndFrame,
|
LLMFullResponseEndFrame,
|
||||||
StartFrame,
|
StartFrame,
|
||||||
StartInterruptionFrame,
|
StartInterruptionFrame,
|
||||||
|
TTSSpeakFrame,
|
||||||
TTSStartedFrame,
|
TTSStartedFrame,
|
||||||
TTSStoppedFrame,
|
TTSStoppedFrame,
|
||||||
TTSVoiceUpdateFrame,
|
TTSVoiceUpdateFrame,
|
||||||
@@ -178,7 +179,7 @@ class TTSService(AIService):
|
|||||||
if text:
|
if text:
|
||||||
await self._push_tts_frames(text)
|
await self._push_tts_frames(text)
|
||||||
|
|
||||||
async def _push_tts_frames(self, text: str):
|
async def _push_tts_frames(self, text: str, text_passthrough: bool = True):
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
if not text:
|
if not text:
|
||||||
return
|
return
|
||||||
@@ -209,6 +210,8 @@ class TTSService(AIService):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
else:
|
else:
|
||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
elif isinstance(frame, TTSSpeakFrame):
|
||||||
|
await self._push_tts_frames(frame.text, False)
|
||||||
elif isinstance(frame, TTSVoiceUpdateFrame):
|
elif isinstance(frame, TTSVoiceUpdateFrame):
|
||||||
await self.set_voice(frame.voice)
|
await self.set_voice(frame.voice)
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user