services(openai): send LLMResponseStartFrame for each completion

This commit is contained in:
Aleix Conchillo Flaqué
2024-05-17 17:47:33 -07:00
parent 0e8c7a9b28
commit 2b8f1c4cda
5 changed files with 11 additions and 9 deletions

View File

@@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added initial interruptions support.
- Added initial interruptions support. The assitant contexts (or aggregators)
should now be placed after the output transport. This way, only the completed
spoken context is added to the assistant context.
- Added `VADParams` so you can control voice confidence level and others.

View File

@@ -71,7 +71,8 @@ async def main(room_url: str, token):
llm,
tts,
transport.output(),
tma_out])
tma_out
])
task = PipelineTask(pipeline, allow_interruptions=True)

View File

@@ -47,8 +47,7 @@ class FrameProcessor:
async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM):
if direction == FrameDirection.DOWNSTREAM and self._next:
if not isinstance(frame, AudioRawFrame):
logger.trace(f"Pushing {frame} from {self} to {self._next}")
logger.trace(f"Pushing {frame} from {self} to {self._next}")
await self._next.process_frame(frame, direction)
elif direction == FrameDirection.UPSTREAM and self._prev:
logger.trace(f"Pushing {frame} upstream from {self} to {self._prev}")

View File

@@ -74,10 +74,12 @@ class TTSService(AIService):
await self._push_tts_frames(text)
async def _push_tts_frames(self, text: str):
await self.push_frame(TextFrame(text))
await self.push_frame(TTSStartedFrame())
await self.process_generator(self.run_tts(text))
await self.push_frame(TTSStoppedFrame())
# We send the original text after the audio. This way, if we are
# interrupted, the text is not added to the assistant context.
await self.push_frame(TextFrame(text))
async def process_frame(self, frame: Frame, direction: FrameDirection):
if isinstance(frame, TextFrame):

View File

@@ -100,8 +100,6 @@ class BaseOpenAILLMService(LLMService):
function_name = ""
arguments = ""
await self.push_frame(LLMResponseStartFrame())
chunk_stream: AsyncStream[ChatCompletionChunk] = (
await self._stream_chat_completions(context)
)
@@ -132,15 +130,15 @@ class BaseOpenAILLMService(LLMService):
# completes
arguments += tool_call.function.arguments
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(LLMResponseEndFrame())
# if we got a function name and arguments, yield the frame with all the info so
# frame consumers can take action based on the function call.
# if function_name and arguments:
# yield LLMFunctionCallFrame(function_name=function_name, arguments=arguments)
await self.push_frame(LLMResponseEndFrame())
async def process_frame(self, frame: Frame, direction: FrameDirection):
context = None
if isinstance(frame, OpenAILLMContextFrame):