Enhance greeting context management in Brain classes

- Introduce greeting context handling in BaseBrain and WorkflowBrain to manage assistant greetings effectively.
- Implement prepare_greeting_context method to add greeting messages to the local context while preserving playback order.
- Update pipeline event handling to ensure greeting timestamps are maintained until the client is ready.
- Enhance tests to verify the correct behavior of greeting context management in various scenarios.
This commit is contained in:
Xin Wang
2026-07-14 13:26:47 +08:00
parent 35cbee4786
commit d069e5282e
6 changed files with 327 additions and 30 deletions

View File

@@ -9,6 +9,7 @@ from pipecat.frames.frames import (
LLMContextFrame,
LLMFullResponseEndFrame,
LLMFullResponseStartFrame,
LLMMessagesAppendFrame,
LLMMessagesUpdateFrame,
LLMRunFrame,
LLMTextFrame,
@@ -19,6 +20,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.frame_processor import FrameDirection
from schemas import AssistantUpsert, REALTIME_CAPABLE_TYPES
from services.brains import BrainRuntime, SPECS, build_brain
from services.brains.base import GREETING_CONTEXT_MARKER
from services.brains.dify_llm import (
DifyLLMService,
last_user_text,
@@ -227,6 +229,19 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
brain = build_brain(cfg)
self.assertEqual(brain.system_prompt(cfg), "服务用户 王先生")
self.assertEqual(await brain.greeting(cfg), "您好,王先生")
context = LLMContext(
messages=[{"role": "system", "content": brain.system_prompt(cfg)}]
)
brain.prepare_greeting_context("您好,王先生", context)
self.assertEqual(
[message["role"] for message in context.get_messages()],
["system", "system"],
)
self.assertNotEqual(context.get_messages()[0]["role"], "assistant")
self.assertEqual(
context.get_messages()[1]["content"],
f"{GREETING_CONTEXT_MARKER}\n您好,王先生",
)
async def test_end_call_tool_is_owned_by_prompt_brain(self):
brain = build_brain(
@@ -604,7 +619,10 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
{
"id": "start",
"type": "start",
"data": {"name": "Start"},
"data": {
"name": "Start",
"greeting": "欢迎,{{user_name}}",
},
},
{
"id": "agent",
@@ -710,6 +728,14 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
apply_turn_config=apply_turn_config,
)
await brain.setup(cfg, runtime)
greeting = await brain.greeting(cfg)
self.assertEqual(greeting, "欢迎,王先生")
greeting_message = {
"role": "system",
"content": f"{GREETING_CONTEXT_MARKER}\n欢迎,王先生",
}
brain.prepare_greeting_context(greeting, context)
self.assertEqual(context.get_messages(), [greeting_message])
await brain.on_connected()
self.assertEqual(brain._manager.current_node, "agent")
variable_events = [
@@ -758,7 +784,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
agent_config = brain._agent_config("agent")
self.assertIn("王先生", agent_config["role_message"])
self.assertIn("工作流路由已在用户一轮输入结束时完成", agent_config["role_message"])
self.assertEqual(agent_config["task_messages"], [])
self.assertEqual(agent_config["task_messages"], [greeting_message])
self.assertFalse(agent_config["respond_immediately"])
self.assertFalse(any(isinstance(frame, LLMRunFrame) for frame in worker.frames))
self.assertEqual(
@@ -785,7 +811,21 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(fixed_config["pre_actions"][0]["text"], "您好,王先生")
self.assertEqual(
fixed_config["task_messages"],
[{"role": "assistant", "content": "您好,王先生"}],
[
greeting_message,
{"role": "assistant", "content": "您好,王先生"},
],
)
self.assertEqual(
brain._agent_config(
"agent",
[{"role": "assistant", "content": "正在进入下一阶段"}],
)["task_messages"],
[
greeting_message,
{"role": "assistant", "content": "正在进入下一阶段"},
{"role": "assistant", "content": "您好,王先生"},
],
)
self.assertEqual(fixed_config["pre_actions"][0]["node_id"], "agent")
worker.frames.clear()
@@ -800,8 +840,19 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
]
self.assertEqual(
context_updates[-1].messages,
[{"role": "assistant", "content": "您好,王先生"}],
[
greeting_message,
{"role": "assistant", "content": "您好,王先生"},
],
)
self.assertFalse(
any(
isinstance(frame, OutputTransportMessageUrgentFrame)
and frame.message.get("source") == "workflow-fixed-reply"
for frame in queued
)
)
await brain.on_client_ready()
fixed_reply_events = [
frame.message
for frame in queued
@@ -833,6 +884,22 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
self.assertTrue(call_end.ending)
self.assertTrue(call_end.armed)
self.assertTrue(any(getattr(frame, "text", "") == "感谢来电" for frame in queued))
transition_context_frames = [
frame
for frame in worker.frames
if isinstance(frame, LLMMessagesAppendFrame)
and frame.messages
== [{"role": "assistant", "content": "正在为你结束流程"}]
]
self.assertTrue(transition_context_frames)
transition_events = [
frame.message
for frame in queued
if isinstance(frame, OutputTransportMessageUrgentFrame)
and frame.message.get("source") == "workflow-edge-transition"
]
self.assertEqual(transition_events[0]["content"], "正在为你结束流程")
self.assertEqual(transition_events[0]["nodeId"], "end")
assistant_transcripts = [
frame.message.get("content")
for frame in queued