feat: trigger prompt reply after opening confirmation

This commit is contained in:
Xin Wang
2026-08-04 11:49:49 +08:00
parent 775e4058bc
commit 1af1cd7fed
2 changed files with 29 additions and 4 deletions

View File

@@ -10,7 +10,11 @@ from uuid import uuid4
from loguru import logger
from models import AssistantConfig
from pipecat.adapters.schemas.function_schema import FunctionSchema
from pipecat.frames.frames import OutputTransportMessageUrgentFrame, TTSSpeakFrame
from pipecat.frames.frames import (
LLMRunFrame,
OutputTransportMessageUrgentFrame,
TTSSpeakFrame,
)
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.frame_processor import FrameProcessor
from pipecat.services.llm_service import (
@@ -165,6 +169,7 @@ class PromptBrain(BaseBrain):
raise RuntimeError("PromptBrain 尚未初始化")
opening_message = self._opening_message()
opening_actions = self._startup_actions("opening")
generate_after_confirmation = opening_message is not None
speech = (
self._render_greeting(self._cfg).strip()
if opening_message is not None
@@ -179,7 +184,8 @@ class PromptBrain(BaseBrain):
speak=self._speak_opening,
set_input_enabled=runtime.set_input_enabled,
input_already_blocked=self._opening_input_blocked,
release_input_on_success=not bool(opening_actions),
# Keep the gate until the automatic first reply is queued.
release_input_on_success=False,
release_input_on_failure=False,
)
if not message_result.succeeded:
@@ -191,7 +197,13 @@ class PromptBrain(BaseBrain):
if opening_actions:
result = await self._action_stages.run(
self._opening_actions_stage_spec(),
set_input_enabled=runtime.set_input_enabled,
# The Prompt opening lifecycle owns the input gate when a
# confirmation message will trigger an automatic reply.
set_input_enabled=(
None
if generate_after_confirmation
else runtime.set_input_enabled
),
input_already_blocked=self._opening_input_blocked,
release_input_on_failure=False,
on_outcome=self._publish_opening_outcome,
@@ -204,6 +216,11 @@ class PromptBrain(BaseBrain):
raise
self._opening_finished = True
self._opening_input_blocked = False
if generate_after_confirmation and not runtime.call_end.ending:
logger.debug("Prompt 开场确认完成,触发自动首句")
await runtime.queue_frame(LLMRunFrame())
if runtime.set_input_enabled is not None:
runtime.set_input_enabled(True)
async def _speak_opening(self, content: str) -> Awaitable[None] | None:
if self._output is None:

View File

@@ -353,7 +353,7 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
["preflight_1", "preflight_2"],
)
async def test_opening_stage_starts_speech_and_releases_on_confirmation(self):
async def test_opening_stage_triggers_reply_after_confirmation(self):
tool = RuntimeTool(
id="opening_data",
name="加载开场数据",
@@ -454,6 +454,10 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
await opening_task
self.assertEqual(input_states, [False, True])
self.assertEqual(called_tool_ids, ["opening_data"])
self.assertEqual(
sum(isinstance(frame, LLMRunFrame) for frame in queued),
1,
)
self.assertEqual(
len(
[
@@ -469,6 +473,10 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
# Replayed client-ready must not execute startup actions twice.
await brain.on_client_ready()
self.assertEqual(brain._actions.execute.await_count, 1)
self.assertEqual(
sum(isinstance(frame, LLMRunFrame) for frame in queued),
1,
)
async def test_required_opening_failure_keeps_input_blocked_and_ends_call(self):
cfg = AssistantConfig(