From b5a192557b0fcd42503bbfb452a0626f1d6f442b Mon Sep 17 00:00:00 2001 From: Mohamed Mamdouh Date: Fri, 12 Jun 2026 13:39:19 +0100 Subject: [PATCH] fix: use system role for user-idle prompt injections The UserIdleHandler injected its "are you still there?" and disconnect prompts as role="user" messages. These are agent-side directives, not user utterances, so they should be injected as role="system" to avoid polluting the conversation transcript with fake user turns and to read correctly by the LLM. Updated the realtime append tests to match. Also forward ports 3000 (UI) and 8000 (API) in the devcontainer so the running services are reachable from the host. Co-Authored-By: Claude Opus 4.8 --- .devcontainer/devcontainer.json | 2 ++ api/services/workflow/pipecat_engine_callbacks.py | 4 ++-- api/tests/test_realtime_message_append.py | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f9edc8e1..0a87b0a2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -24,6 +24,8 @@ "postCreateCommand": "bash .devcontainer/scripts/post-create.sh", "postStartCommand": "bash .devcontainer/scripts/post-start.sh", "forwardPorts": [ + 3000, + 8000, 5432, 6379, 9000, diff --git a/api/services/workflow/pipecat_engine_callbacks.py b/api/services/workflow/pipecat_engine_callbacks.py index 0c80704b..9e58f1df 100644 --- a/api/services/workflow/pipecat_engine_callbacks.py +++ b/api/services/workflow/pipecat_engine_callbacks.py @@ -46,14 +46,14 @@ class UserIdleHandler: if self._retry_count == 1: message = { - "role": "user", + "role": "system", "content": "The user has been quiet. Politely and briefly ask if they're still there in the language that the user has been speaking so far.", } await aggregator.push_frame(LLMMessagesAppendFrame([message], run_llm=True)) return message = { - "role": "user", + "role": "system", "content": "The user has been quiet. We will be disconnecting the call now. Wish them a good day in the language that the user has been speaking so far.", } await aggregator.push_frame(LLMMessagesAppendFrame([message], run_llm=True)) diff --git a/api/tests/test_realtime_message_append.py b/api/tests/test_realtime_message_append.py index 56ec8927..2025dc5d 100644 --- a/api/tests/test_realtime_message_append.py +++ b/api/tests/test_realtime_message_append.py @@ -20,7 +20,7 @@ async def test_openai_realtime_messages_append_frame_sends_conversation_item(): await service._handle_messages_append( LLMMessagesAppendFrame( - [{"role": "user", "content": "Are you still there?"}], + [{"role": "system", "content": "Are you still there?"}], run_llm=True, ) ) @@ -28,7 +28,7 @@ async def test_openai_realtime_messages_append_frame_sends_conversation_item(): service.send_client_event.assert_awaited_once() event = service.send_client_event.await_args.args[0] assert isinstance(event, events.ConversationItemCreateEvent) - assert event.item.role == "user" + assert event.item.role == "system" assert event.item.type == "message" assert event.item.content == [ events.ItemContent(type="input_text", text="Are you still there?") @@ -53,7 +53,7 @@ async def test_user_idle_handler_uses_realtime_append_path(): assert frame.run_llm is True assert frame.messages == [ { - "role": "user", + "role": "system", "content": "The user has been quiet. Politely and briefly ask if they're still there in the language that the user has been speaking so far.", } ]