fix: harden realtime pipeline lifecycle

This commit is contained in:
Xin Wang
2026-09-07 08:27:52 +08:00
parent 43f2cc445e
commit 8025ed8aa0
12 changed files with 445 additions and 42 deletions
@@ -0,0 +1,56 @@
"""Run a media worker and persist its actual terminal outcome."""
import asyncio
from pipecat.frames.frames import CancelFrame, EndFrame, ErrorFrame
from pipecat.pipeline.worker import PipelineWorker
from pipecat.workers.runner import WorkerRunner
from services.conversation_history import ConversationRecorder
async def run_media_worker(
worker: PipelineWorker,
recorder: ConversationRecorder | None,
) -> None:
# A runner can return normally after a fatal ErrorFrame or a failed worker.
# Only an observed, expected terminal frame counts as normal completion.
status = "failed"
fatal_error = False
@worker.event_handler("on_pipeline_error")
async def on_pipeline_error(_worker, frame: ErrorFrame):
nonlocal status, fatal_error
if frame.fatal:
fatal_error = True
status = "failed"
@worker.event_handler("on_pipeline_finished")
async def on_pipeline_finished(_worker, frame):
nonlocal status
normal_end = isinstance(frame, EndFrame) or (
isinstance(frame, CancelFrame)
and frame.reason in {"client_disconnected", "idle timeout"}
)
if normal_end and not fatal_error:
status = "completed"
runner = WorkerRunner(handle_sigint=False)
can_finalize = True
try:
await runner.add_workers(worker)
await runner.run()
# Pipecat's runner consumes cancellation while draining its workers.
# Preserve the owning server task's cancellation after that cleanup.
owner = asyncio.current_task()
if owner is not None and owner.cancelling():
raise asyncio.CancelledError
except GeneratorExit:
can_finalize = False # Synchronously closed coroutines cannot await.
raise
except BaseException:
status = "failed"
raise
finally:
if recorder and can_finalize:
await recorder.finish(status=status)