LLMConfigureOutputFrame: allow configuring LLM output
This commit is contained in:
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added support for switching between audio+text to text-only modes within the
|
||||||
|
same pipeline. This is done by pushing
|
||||||
|
`LLMConfigureOutputFrame(skip_tts=True)` to enter text-only mode, and
|
||||||
|
disabling it to return to audio+text. The LLM will still generate tokens and
|
||||||
|
add them to the context, but they will not be sent to TTS.
|
||||||
|
|
||||||
- Added `skip_tts` field to `TextFrame`. This lets a text frame bypass TTS while
|
- Added `skip_tts` field to `TextFrame`. This lets a text frame bypass TTS while
|
||||||
still being included in the LLM context. Useful for cases like structured text
|
still being included in the LLM context. Useful for cases like structured text
|
||||||
that isn’t meant to be spoken but should still contribute to context.
|
that isn’t meant to be spoken but should still contribute to context.
|
||||||
|
|||||||
@@ -607,6 +607,21 @@ class LLMEnablePromptCachingFrame(DataFrame):
|
|||||||
enable: bool
|
enable: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LLMConfigureOutputFrame(DataFrame):
|
||||||
|
"""Frame to configure LLM output.
|
||||||
|
|
||||||
|
This frame is used to configure how the LLM produces output. For example, it
|
||||||
|
can tell the LLM to generate tokens that should be added to the context but
|
||||||
|
not spoken by the TTS service (if one is present in the pipeline).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
skip_tts: Whether LLM tokens should skip the TTS service (if any).
|
||||||
|
"""
|
||||||
|
|
||||||
|
skip_tts: bool
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TTSSpeakFrame(DataFrame):
|
class TTSSpeakFrame(DataFrame):
|
||||||
"""Frame containing text that should be spoken by TTS.
|
"""Frame containing text that should be spoken by TTS.
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class DTMFAggregator(FrameProcessor):
|
|||||||
digit_value = frame.button.value
|
digit_value = frame.button.value
|
||||||
self._aggregation += digit_value
|
self._aggregation += digit_value
|
||||||
|
|
||||||
# For first digit, schedule interruption in separate task
|
# For first digit, schedule interruption.
|
||||||
if is_first_digit:
|
if is_first_digit:
|
||||||
await self.push_frame(BotInterruptionFrame(), FrameDirection.UPSTREAM)
|
await self.push_frame(BotInterruptionFrame(), FrameDirection.UPSTREAM)
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ from pipecat.frames.frames import (
|
|||||||
FunctionCallResultFrame,
|
FunctionCallResultFrame,
|
||||||
FunctionCallResultProperties,
|
FunctionCallResultProperties,
|
||||||
FunctionCallsStartedFrame,
|
FunctionCallsStartedFrame,
|
||||||
|
LLMConfigureOutputFrame,
|
||||||
|
LLMTextFrame,
|
||||||
StartFrame,
|
StartFrame,
|
||||||
StartInterruptionFrame,
|
StartInterruptionFrame,
|
||||||
UserImageRequestFrame,
|
UserImageRequestFrame,
|
||||||
@@ -179,6 +181,7 @@ class LLMService(AIService):
|
|||||||
self._function_call_tasks: Dict[asyncio.Task, FunctionCallRunnerItem] = {}
|
self._function_call_tasks: Dict[asyncio.Task, FunctionCallRunnerItem] = {}
|
||||||
self._sequential_runner_task: Optional[asyncio.Task] = None
|
self._sequential_runner_task: Optional[asyncio.Task] = None
|
||||||
self._tracing_enabled: bool = False
|
self._tracing_enabled: bool = False
|
||||||
|
self._skip_tts: bool = False
|
||||||
|
|
||||||
self._register_event_handler("on_function_calls_started")
|
self._register_event_handler("on_function_calls_started")
|
||||||
self._register_event_handler("on_completion_timeout")
|
self._register_event_handler("on_completion_timeout")
|
||||||
@@ -272,6 +275,20 @@ class LLMService(AIService):
|
|||||||
|
|
||||||
if isinstance(frame, StartInterruptionFrame):
|
if isinstance(frame, StartInterruptionFrame):
|
||||||
await self._handle_interruptions(frame)
|
await self._handle_interruptions(frame)
|
||||||
|
elif isinstance(frame, LLMConfigureOutputFrame):
|
||||||
|
self._skip_tts = frame.skip_tts
|
||||||
|
|
||||||
|
async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM):
|
||||||
|
"""Pushes a frame.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
frame: The frame to push.
|
||||||
|
direction: The direction of frame pushing.
|
||||||
|
"""
|
||||||
|
if isinstance(frame, LLMTextFrame):
|
||||||
|
frame.skip_tts = self._skip_tts
|
||||||
|
|
||||||
|
await super().push_frame(frame, direction)
|
||||||
|
|
||||||
async def _handle_interruptions(self, _: StartInterruptionFrame):
|
async def _handle_interruptions(self, _: StartInterruptionFrame):
|
||||||
for function_name, entry in self._functions.items():
|
for function_name, entry in self._functions.items():
|
||||||
|
|||||||
Reference in New Issue
Block a user