fix: harden realtime pipeline lifecycle
This commit is contained in:
@@ -9,6 +9,91 @@ from services.conversation_history import ConversationRecorder
|
||||
|
||||
|
||||
class ConversationRecorderTest(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_background_messages_keep_order_and_ignore_streaming_deltas(self):
|
||||
recorder = ConversationRecorder("conv_test")
|
||||
recorder.record_transport_message = AsyncMock()
|
||||
events = [
|
||||
{"type": "transcript", "content": "user"},
|
||||
{"type": "assistant-text-end", "content": "assistant"},
|
||||
]
|
||||
for event in events:
|
||||
recorder.record_transport_message_later(event)
|
||||
recorder.record_transport_message_later({"type": "assistant-text-delta"})
|
||||
await recorder.flush_pending_messages()
|
||||
self.assertEqual(
|
||||
[call.args[0] for call in recorder.record_transport_message.await_args_list],
|
||||
events,
|
||||
)
|
||||
self.assertIsNone(recorder._message_writer)
|
||||
|
||||
async def test_slow_database_does_not_block_audio(self):
|
||||
from pipecat.frames.frames import EndFrame, OutputAudioRawFrame, OutputTransportMessageUrgentFrame
|
||||
from pipecat.pipeline.pipeline import Pipeline
|
||||
from pipecat.pipeline.worker import PipelineWorker
|
||||
from pipecat.processors.frame_processor import FrameProcessor
|
||||
from pipecat.workers.runner import WorkerRunner
|
||||
from services.pipecat.processors import ConversationHistoryProcessor
|
||||
|
||||
entered, release, audio_received = asyncio.Event(), asyncio.Event(), asyncio.Event()
|
||||
recorder = ConversationRecorder("conv_test")
|
||||
|
||||
async def slow_write(message):
|
||||
entered.set()
|
||||
await release.wait()
|
||||
|
||||
recorder.record_transport_message = slow_write
|
||||
|
||||
class AudioSink(FrameProcessor):
|
||||
async def process_frame(self, frame, direction):
|
||||
await super().process_frame(frame, direction)
|
||||
if isinstance(frame, OutputAudioRawFrame):
|
||||
audio_received.set()
|
||||
await self.push_frame(frame, direction)
|
||||
|
||||
worker = PipelineWorker(
|
||||
Pipeline([ConversationHistoryProcessor(recorder), AudioSink()]), enable_rtvi=False,
|
||||
)
|
||||
runner = WorkerRunner(handle_sigint=False)
|
||||
await runner.add_workers(worker)
|
||||
task = asyncio.create_task(runner.run())
|
||||
try:
|
||||
await worker.queue_frame(OutputTransportMessageUrgentFrame(message={"type": "transcript"}))
|
||||
await asyncio.wait_for(entered.wait(), 3)
|
||||
await worker.queue_frame(OutputAudioRawFrame(audio=b"\0" * 320, sample_rate=16000, num_channels=1))
|
||||
await asyncio.wait_for(audio_received.wait(), 1)
|
||||
self.assertFalse(release.is_set())
|
||||
finally:
|
||||
release.set()
|
||||
await recorder.flush_pending_messages()
|
||||
await worker.queue_frame(EndFrame())
|
||||
await asyncio.wait_for(task, 3)
|
||||
|
||||
async def test_history_buffer_is_bounded_and_slow_writer_is_cancelled(self):
|
||||
started = asyncio.Event()
|
||||
cancelled = asyncio.Event()
|
||||
with patch("services.conversation_history.MAX_PENDING_HISTORY_MESSAGES", 2):
|
||||
recorder = ConversationRecorder("conv_test")
|
||||
|
||||
async def blocked_write(message):
|
||||
started.set()
|
||||
try:
|
||||
await asyncio.Event().wait()
|
||||
finally:
|
||||
cancelled.set()
|
||||
|
||||
recorder.record_transport_message = blocked_write
|
||||
recorder.record_transport_message_later({"type": "transcript"})
|
||||
await started.wait()
|
||||
for _ in range(4):
|
||||
recorder.record_transport_message_later({"type": "transcript"})
|
||||
self.assertEqual(recorder._pending_messages.qsize(), 2)
|
||||
with patch("services.conversation_history.HISTORY_FLUSH_TIMEOUT_SECONDS", 0.01):
|
||||
await recorder.flush_pending_messages()
|
||||
self.assertTrue(cancelled.is_set())
|
||||
self.assertTrue(recorder._pending_messages.empty())
|
||||
recorder.record_transport_message_later({"type": "transcript"})
|
||||
self.assertIsNone(recorder._message_writer)
|
||||
|
||||
async def test_completed_conversation_queues_enabled_analysis(self):
|
||||
conversation = SimpleNamespace(
|
||||
status="active",
|
||||
|
||||
Reference in New Issue
Block a user