fix(workflow): track fixed speech at transport output

This commit is contained in:
Xin Wang
2026-08-06 10:21:36 +08:00
parent 5cb13a3cac
commit aaca128f82
6 changed files with 384 additions and 46 deletions

View File

@@ -23,6 +23,7 @@ from services.message_stage import (
MessageStageRunner,
MessageStageSpec,
)
from services.pipecat.call_lifecycle import playback_marker_for
from services.pipecat.realtime_tools import RealtimeTool, RealtimeToolResult
from services.runtime_variables import DynamicVariableError, DynamicVariableStore
from services.system_tools import state_update_properties, system_tool_kind
@@ -99,6 +100,18 @@ class RealtimeWorkflowOutput(WorkflowOutput):
content,
suppress_transcript=True,
)
playback_marker = playback_marker_for(playback_completion)
if playback_marker is not None:
async def finish_playback_tracking() -> None:
# The provider boundary is emitted after its final audio frame.
# Queueing the marker then places it behind that audio at output.
if provider_completion is not None:
await provider_completion
await self._runtime.queue_frame(playback_marker)
playback_marker.completion.mark_queued()
await playback_completion
return asyncio.create_task(finish_playback_tracking())
# Message playback policy and deterministic continuation must use the
# transport boundary. Provider response.done only means generation
# has finished; audio may still be buffered at the output transport.
@@ -983,12 +996,20 @@ class WorkflowRealtimeController:
return
self._runtime.call_end.begin("workflow_completed")
if message:
self._runtime.call_end.arm_after_speech()
completion = await self._output.speak(
message,
source="workflow-end-speech",
node_id=node_id,
)
arm_tracked = getattr(
self._runtime.call_end,
"arm_after_tracked_speech",
None,
)
if callable(arm_tracked):
await arm_tracked()
else:
self._runtime.call_end.arm_after_speech()
if completion:
await completion
else: