Re-inject turn completion instructions after LLM context reset

When filter_incomplete_user_turns is enabled and an LLMMessagesUpdateFrame
replaces the context via set_messages(), the turn completion instructions
system message was lost. This caused the LLM to stop emitting turn
completion markers. Re-inject the instructions after set_messages() to
fix this.
This commit is contained in:
Mark Backman
2026-03-01 16:30:49 -05:00
parent 024c62946f
commit 91c46ffbf4
3 changed files with 27 additions and 0 deletions

1
changelog/3888.fixed.md Normal file
View File

@@ -0,0 +1 @@
- Fixed turn completion instructions being lost when `LLMMessagesUpdateFrame` replaces the LLM context. When `filter_incomplete_user_turns` is enabled, the turn completion system message is now re-injected after context replacement.

View File

@@ -642,6 +642,9 @@ class LLMUserAggregator(LLMContextAggregator):
async def _handle_llm_messages_update(self, frame: LLMMessagesUpdateFrame):
self.set_messages(frame.messages)
if self._params.filter_incomplete_user_turns:
config = self._params.user_turn_completion_config or UserTurnCompletionConfig()
self._context.add_message({"role": "system", "content": config.completion_instructions})
if frame.run_llm:
await self.push_context_frame()

View File

@@ -50,6 +50,7 @@ from pipecat.turns.user_mute import (
MuteUntilFirstBotCompleteUserMuteStrategy,
)
from pipecat.turns.user_stop import SpeechTimeoutUserTurnStopStrategy
from pipecat.turns.user_turn_completion_mixin import UserTurnCompletionConfig
from pipecat.turns.user_turn_strategies import UserTurnStrategies
USER_TURN_STOP_TIMEOUT = 0.2
@@ -155,6 +156,28 @@ class TestLLMUserAggregator(unittest.IsolatedAsyncioTestCase):
)
assert context.messages[0]["content"] == "Hi there!"
async def test_llm_messages_update_reinjects_turn_completion_instructions(self):
context = LLMContext()
params = LLMUserAggregatorParams(filter_incomplete_user_turns=True)
pipeline = Pipeline([LLMUserAggregator(context, params=params)])
new_messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"},
]
frames_to_send = [LLMMessagesUpdateFrame(messages=new_messages)]
await run_test(
pipeline,
frames_to_send=frames_to_send,
)
config = UserTurnCompletionConfig()
# The context should contain the new messages plus the re-injected instructions
assert len(context.messages) == 3
assert context.messages[0]["content"] == "You are a helpful assistant."
assert context.messages[1]["content"] == "Hello!"
assert context.messages[2]["role"] == "system"
assert context.messages[2]["content"] == config.completion_instructions
async def test_default_user_turn_strategies(self):
context = LLMContext()
user_aggregator = LLMUserAggregator(