Merge pull request #306 from pipecat-ai/aleix/remove-llm-response-start-end-frame
remove LLMResponseStartFrame and LLMResponseEndFrame
This commit is contained in:
22
CHANGELOG.md
22
CHANGELOG.md
@@ -5,6 +5,28 @@ All notable changes to **pipecat** will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- We remove the `LLMResponseStartFrame` and `LLMResponseEndFrame` frames. These
|
||||||
|
were added in the past to properly handle interruptions for the
|
||||||
|
`LLMAssistantContextAggregator`. But the `LLMContextAggregator` is now based
|
||||||
|
on `LLMResponseAggregator` which handles interruptions properly by just
|
||||||
|
processing the `StartInterruptionFrame`, so there's no need for these extra
|
||||||
|
frames any more.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `TTSService` end of sentence detection has been improved. It now works with
|
||||||
|
acronyms, numbers, hours and others.
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- `CartesiaTTSService` now uses websockets which improves speed. It also
|
||||||
|
leverages the new Cartesia contexts which maintains generated audio prosody
|
||||||
|
when multiple inputs are sent, therefore improving audio quality a lot.
|
||||||
|
|
||||||
## [0.0.36] - 2024-07-02
|
## [0.0.36] - 2024-07-02
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -282,27 +282,13 @@ class EndFrame(ControlFrame):
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LLMFullResponseStartFrame(ControlFrame):
|
class LLMFullResponseStartFrame(ControlFrame):
|
||||||
"""Used to indicate the beginning of a full LLM response. Following
|
"""Used to indicate the beginning of an LLM response. Following by one or
|
||||||
LLMResponseStartFrame, TextFrame and LLMResponseEndFrame for each sentence
|
more TextFrame and a final LLMFullResponseEndFrame."""
|
||||||
until a LLMFullResponseEndFrame."""
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LLMFullResponseEndFrame(ControlFrame):
|
class LLMFullResponseEndFrame(ControlFrame):
|
||||||
"""Indicates the end of a full LLM response."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class LLMResponseStartFrame(ControlFrame):
|
|
||||||
"""Used to indicate the beginning of an LLM response. Following TextFrames
|
|
||||||
are part of the LLM response until an LLMResponseEndFrame"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class LLMResponseEndFrame(ControlFrame):
|
|
||||||
"""Indicates the end of an LLM response."""
|
"""Indicates the end of an LLM response."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ from pipecat.frames.frames import (
|
|||||||
InterimTranscriptionFrame,
|
InterimTranscriptionFrame,
|
||||||
LLMFullResponseEndFrame,
|
LLMFullResponseEndFrame,
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
LLMResponseEndFrame,
|
|
||||||
LLMResponseStartFrame,
|
|
||||||
LLMMessagesFrame,
|
LLMMessagesFrame,
|
||||||
StartInterruptionFrame,
|
StartInterruptionFrame,
|
||||||
TranscriptionFrame,
|
TranscriptionFrame,
|
||||||
@@ -173,7 +171,7 @@ class LLMUserResponseAggregator(LLMResponseAggregator):
|
|||||||
|
|
||||||
class LLMFullResponseAggregator(FrameProcessor):
|
class LLMFullResponseAggregator(FrameProcessor):
|
||||||
"""This class aggregates Text frames until it receives a
|
"""This class aggregates Text frames until it receives a
|
||||||
LLMResponseEndFrame, then emits the concatenated text as
|
LLMFullResponseEndFrame, then emits the concatenated text as
|
||||||
a single text frame.
|
a single text frame.
|
||||||
|
|
||||||
given the following frames:
|
given the following frames:
|
||||||
@@ -182,12 +180,12 @@ class LLMFullResponseAggregator(FrameProcessor):
|
|||||||
TextFrame(" world.")
|
TextFrame(" world.")
|
||||||
TextFrame(" I am")
|
TextFrame(" I am")
|
||||||
TextFrame(" an LLM.")
|
TextFrame(" an LLM.")
|
||||||
LLMResponseEndFrame()]
|
LLMFullResponseEndFrame()]
|
||||||
|
|
||||||
this processor will yield nothing for the first 4 frames, then
|
this processor will yield nothing for the first 4 frames, then
|
||||||
|
|
||||||
TextFrame("Hello, world. I am an LLM.")
|
TextFrame("Hello, world. I am an LLM.")
|
||||||
LLMResponseEndFrame()
|
LLMFullResponseEndFrame()
|
||||||
|
|
||||||
when passed the last frame.
|
when passed the last frame.
|
||||||
|
|
||||||
@@ -203,9 +201,9 @@ class LLMFullResponseAggregator(FrameProcessor):
|
|||||||
>>> asyncio.run(print_frames(aggregator, TextFrame(" world.")))
|
>>> asyncio.run(print_frames(aggregator, TextFrame(" world.")))
|
||||||
>>> asyncio.run(print_frames(aggregator, TextFrame(" I am")))
|
>>> asyncio.run(print_frames(aggregator, TextFrame(" I am")))
|
||||||
>>> asyncio.run(print_frames(aggregator, TextFrame(" an LLM.")))
|
>>> asyncio.run(print_frames(aggregator, TextFrame(" an LLM.")))
|
||||||
>>> asyncio.run(print_frames(aggregator, LLMResponseEndFrame()))
|
>>> asyncio.run(print_frames(aggregator, LLMFullResponseEndFrame()))
|
||||||
Hello, world. I am an LLM.
|
Hello, world. I am an LLM.
|
||||||
LLMResponseEndFrame
|
LLMFullResponseEndFrame
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -234,6 +232,11 @@ class LLMContextAggregator(LLMResponseAggregator):
|
|||||||
async def _push_aggregation(self):
|
async def _push_aggregation(self):
|
||||||
if len(self._aggregation) > 0:
|
if len(self._aggregation) > 0:
|
||||||
self._context.add_message({"role": self._role, "content": self._aggregation})
|
self._context.add_message({"role": self._role, "content": self._aggregation})
|
||||||
|
|
||||||
|
# Reset the aggregation. Reset it before pushing it down, otherwise
|
||||||
|
# if the tasks gets cancelled we won't be able to clear things up.
|
||||||
|
self._aggregation = ""
|
||||||
|
|
||||||
frame = OpenAILLMContextFrame(self._context)
|
frame = OpenAILLMContextFrame(self._context)
|
||||||
await self.push_frame(frame)
|
await self.push_frame(frame)
|
||||||
|
|
||||||
@@ -247,9 +250,10 @@ class LLMAssistantContextAggregator(LLMContextAggregator):
|
|||||||
messages=[],
|
messages=[],
|
||||||
context=context,
|
context=context,
|
||||||
role="assistant",
|
role="assistant",
|
||||||
start_frame=LLMResponseStartFrame,
|
start_frame=LLMFullResponseStartFrame,
|
||||||
end_frame=LLMResponseEndFrame,
|
end_frame=LLMFullResponseEndFrame,
|
||||||
accumulator_frame=TextFrame
|
accumulator_frame=TextFrame,
|
||||||
|
handle_interruptions=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ from pipecat.frames.frames import (
|
|||||||
LLMFullResponseEndFrame,
|
LLMFullResponseEndFrame,
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
LLMMessagesFrame,
|
LLMMessagesFrame,
|
||||||
LLMResponseEndFrame,
|
|
||||||
LLMResponseStartFrame,
|
|
||||||
TextFrame)
|
TextFrame)
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
|
|
||||||
@@ -69,9 +67,7 @@ class LangchainProcessor(FrameProcessor):
|
|||||||
{self._transcript_key: text},
|
{self._transcript_key: text},
|
||||||
config={"configurable": {"session_id": self._participant_id}},
|
config={"configurable": {"session_id": self._participant_id}},
|
||||||
):
|
):
|
||||||
await self.push_frame(LLMResponseStartFrame())
|
|
||||||
await self.push_frame(TextFrame(self.__get_token_value(token)))
|
await self.push_frame(TextFrame(self.__get_token_value(token)))
|
||||||
await self.push_frame(LLMResponseEndFrame())
|
|
||||||
except GeneratorExit:
|
except GeneratorExit:
|
||||||
logger.warning(f"{self} generator was closed prematurely")
|
logger.warning(f"{self} generator was closed prematurely")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ from pipecat.frames.frames import (
|
|||||||
VisionImageRawFrame,
|
VisionImageRawFrame,
|
||||||
LLMMessagesFrame,
|
LLMMessagesFrame,
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
LLMResponseStartFrame,
|
|
||||||
LLMResponseEndFrame,
|
|
||||||
LLMFullResponseEndFrame
|
LLMFullResponseEndFrame
|
||||||
)
|
)
|
||||||
from pipecat.processors.frame_processor import FrameDirection
|
from pipecat.processors.frame_processor import FrameDirection
|
||||||
@@ -118,9 +116,7 @@ class AnthropicLLMService(LLMService):
|
|||||||
async for event in response:
|
async for event in response:
|
||||||
# logger.debug(f"Anthropic LLM event: {event}")
|
# logger.debug(f"Anthropic LLM event: {event}")
|
||||||
if (event.type == "content_block_delta"):
|
if (event.type == "content_block_delta"):
|
||||||
await self.push_frame(LLMResponseStartFrame())
|
|
||||||
await self.push_frame(TextFrame(event.delta.text))
|
await self.push_frame(TextFrame(event.delta.text))
|
||||||
await self.push_frame(LLMResponseEndFrame())
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"{self} exception: {e}")
|
logger.exception(f"{self} exception: {e}")
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ from pipecat.frames.frames import (
|
|||||||
VisionImageRawFrame,
|
VisionImageRawFrame,
|
||||||
LLMMessagesFrame,
|
LLMMessagesFrame,
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
LLMResponseStartFrame,
|
|
||||||
LLMResponseEndFrame,
|
|
||||||
LLMFullResponseEndFrame
|
LLMFullResponseEndFrame
|
||||||
)
|
)
|
||||||
from pipecat.processors.frame_processor import FrameDirection
|
from pipecat.processors.frame_processor import FrameDirection
|
||||||
@@ -95,9 +93,7 @@ class GoogleLLMService(LLMService):
|
|||||||
async for chunk in self._async_generator_wrapper(response):
|
async for chunk in self._async_generator_wrapper(response):
|
||||||
try:
|
try:
|
||||||
text = chunk.text
|
text = chunk.text
|
||||||
await self.push_frame(LLMResponseStartFrame())
|
|
||||||
await self.push_frame(TextFrame(text))
|
await self.push_frame(TextFrame(text))
|
||||||
await self.push_frame(LLMResponseEndFrame())
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Google LLMs seem to flag safety issues a lot!
|
# Google LLMs seem to flag safety issues a lot!
|
||||||
if chunk.candidates[0].finish_reason == 3:
|
if chunk.candidates[0].finish_reason == 3:
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ from pipecat.frames.frames import (
|
|||||||
LLMFullResponseEndFrame,
|
LLMFullResponseEndFrame,
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
LLMMessagesFrame,
|
LLMMessagesFrame,
|
||||||
LLMResponseEndFrame,
|
|
||||||
LLMResponseStartFrame,
|
|
||||||
TextFrame,
|
TextFrame,
|
||||||
URLImageRawFrame,
|
URLImageRawFrame,
|
||||||
VisionImageRawFrame
|
VisionImageRawFrame
|
||||||
@@ -151,9 +149,7 @@ class BaseOpenAILLMService(LLMService):
|
|||||||
# Keep iterating through the response to collect all the argument fragments
|
# Keep iterating through the response to collect all the argument fragments
|
||||||
arguments += tool_call.function.arguments
|
arguments += tool_call.function.arguments
|
||||||
elif chunk.choices[0].delta.content:
|
elif chunk.choices[0].delta.content:
|
||||||
await self.push_frame(LLMResponseStartFrame())
|
|
||||||
await self.push_frame(TextFrame(chunk.choices[0].delta.content))
|
await self.push_frame(TextFrame(chunk.choices[0].delta.content))
|
||||||
await self.push_frame(LLMResponseEndFrame())
|
|
||||||
|
|
||||||
# if we got a function name and arguments, check to see if it's a function with
|
# if we got a function name and arguments, check to see if it's a function with
|
||||||
# a registered handler. If so, run the registered callback, save the result to
|
# a registered handler. If so, run the registered callback, save the result to
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
|||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
LLMFullResponseEndFrame,
|
LLMFullResponseEndFrame,
|
||||||
LLMResponseEndFrame,
|
|
||||||
LLMResponseStartFrame,
|
|
||||||
TextFrame
|
TextFrame
|
||||||
)
|
)
|
||||||
from pipecat.utils.test_frame_processor import TestFrameProcessor
|
from pipecat.utils.test_frame_processor import TestFrameProcessor
|
||||||
@@ -64,7 +62,7 @@ if __name__ == "__main__":
|
|||||||
llm.register_function("get_current_weather", get_weather_from_api)
|
llm.register_function("get_current_weather", get_weather_from_api)
|
||||||
t = TestFrameProcessor([
|
t = TestFrameProcessor([
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
[LLMResponseStartFrame, TextFrame, LLMResponseEndFrame],
|
TextFrame,
|
||||||
LLMFullResponseEndFrame
|
LLMFullResponseEndFrame
|
||||||
])
|
])
|
||||||
llm.link(t)
|
llm.link(t)
|
||||||
@@ -98,7 +96,7 @@ if __name__ == "__main__":
|
|||||||
llm.register_function("get_current_weather", get_weather_from_api)
|
llm.register_function("get_current_weather", get_weather_from_api)
|
||||||
t = TestFrameProcessor([
|
t = TestFrameProcessor([
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
[LLMResponseStartFrame, TextFrame, LLMResponseEndFrame],
|
TextFrame,
|
||||||
LLMFullResponseEndFrame
|
LLMFullResponseEndFrame
|
||||||
])
|
])
|
||||||
llm.link(t)
|
llm.link(t)
|
||||||
@@ -121,7 +119,7 @@ if __name__ == "__main__":
|
|||||||
api_key = os.getenv("OPENAI_API_KEY")
|
api_key = os.getenv("OPENAI_API_KEY")
|
||||||
t = TestFrameProcessor([
|
t = TestFrameProcessor([
|
||||||
LLMFullResponseStartFrame,
|
LLMFullResponseStartFrame,
|
||||||
[LLMResponseStartFrame, TextFrame, LLMResponseEndFrame],
|
TextFrame,
|
||||||
LLMFullResponseEndFrame
|
LLMFullResponseEndFrame
|
||||||
])
|
])
|
||||||
llm = OpenAILLMService(
|
llm = OpenAILLMService(
|
||||||
|
|||||||
Reference in New Issue
Block a user