Refactor workflow routing and greeting management in Brain classes
- Update WorkflowBrain to handle greeting playback more effectively, ensuring that the initial greeting completes before transitioning to the first node. - Introduce new methods for managing greeting states and conditions, enhancing the interaction flow for user turns. - Refactor WorkflowLLMRouter to improve routing logic and ensure proper handling of conditional paths. - Enhance tests to verify the correct behavior of greeting management and routing under various scenarios, including waiting for audio playback to finish. - Update frontend components to reflect changes in edge handling and improve user experience in workflow configurations.
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
from loguru import logger
|
||||
|
||||
from pipecat.frames.frames import (
|
||||
BotStartedSpeakingFrame,
|
||||
BotStoppedSpeakingFrame,
|
||||
EndFrame,
|
||||
LLMMessagesAppendFrame,
|
||||
OutputTransportMessageUrgentFrame,
|
||||
@@ -33,6 +35,28 @@ def bind_cascade_pipeline_events(
|
||||
pending_text_inputs: list[str] = []
|
||||
greeting_transcript_sent = False
|
||||
greeting_timestamp = ""
|
||||
greeting_playback_pending = False
|
||||
greeting_playback_started = False
|
||||
|
||||
# FlowManager already observes downstream frames for its own actions. Add
|
||||
# to that filter instead of replacing it, then use the real transport
|
||||
# playback boundary to release Workflow startup.
|
||||
worker.add_reached_downstream_filter(
|
||||
(BotStartedSpeakingFrame, BotStoppedSpeakingFrame)
|
||||
)
|
||||
|
||||
@worker.event_handler("on_frame_reached_downstream")
|
||||
async def on_frame_reached_downstream(_worker, frame):
|
||||
nonlocal greeting_playback_pending, greeting_playback_started
|
||||
if not greeting_playback_pending:
|
||||
return
|
||||
if isinstance(frame, BotStartedSpeakingFrame):
|
||||
greeting_playback_started = True
|
||||
return
|
||||
if isinstance(frame, BotStoppedSpeakingFrame) and greeting_playback_started:
|
||||
greeting_playback_pending = False
|
||||
greeting_playback_started = False
|
||||
await brain.on_greeting_finished()
|
||||
|
||||
async def queue_transcript(role: str, content: str, timestamp: str) -> None:
|
||||
if not content:
|
||||
@@ -132,7 +156,7 @@ def bind_cascade_pipeline_events(
|
||||
|
||||
@transport.event_handler("on_client_connected")
|
||||
async def on_client_connected(_transport, _client):
|
||||
nonlocal greeting_timestamp
|
||||
nonlocal greeting_timestamp, greeting_playback_pending
|
||||
if vision_enabled:
|
||||
try:
|
||||
vision_state["client_id"] = get_transport_client_id(
|
||||
@@ -145,16 +169,24 @@ def bind_cascade_pipeline_events(
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001 - media availability is optional
|
||||
logger.warning(f"视觉理解摄像头捕获初始化失败: {exc}")
|
||||
if greeting:
|
||||
has_greeting = bool(greeting.strip())
|
||||
if has_greeting:
|
||||
# Preserve the actual playback order. The transcript is delivered
|
||||
# later on client-ready, but the preview sorts by this timestamp.
|
||||
greeting_timestamp = greeting_timestamp or time_now_iso8601()
|
||||
if brain.spec.owns_context:
|
||||
brain.prepare_greeting_context(greeting, context)
|
||||
greeting_playback_pending = True
|
||||
|
||||
# Initialize the Workflow before the greeting is queued so a very
|
||||
# short TTS response cannot finish before the brain arms its startup
|
||||
# gate. Other brain types simply ignore greeting_pending.
|
||||
await brain.on_connected(greeting_pending=has_greeting)
|
||||
|
||||
if has_greeting:
|
||||
await worker.queue_frame(
|
||||
TTSSpeakFrame(greeting, append_to_context=False)
|
||||
)
|
||||
await brain.on_connected()
|
||||
|
||||
@transport.event_handler("on_client_disconnected")
|
||||
async def on_client_disconnected(_transport, _client):
|
||||
|
||||
Reference in New Issue
Block a user