57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
"""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)
|