fix: harden realtime pipeline lifecycle
This commit is contained in:
@@ -16,6 +16,8 @@ from services.object_storage import delete_object, put_object, storage_uri
|
||||
|
||||
|
||||
MAX_WORKFLOW_TRACE_EVENTS = 1000
|
||||
MAX_PENDING_HISTORY_MESSAGES = 256
|
||||
HISTORY_FLUSH_TIMEOUT_SECONDS = 3.0
|
||||
|
||||
|
||||
def _parse_timestamp(value: object) -> datetime:
|
||||
@@ -39,6 +41,63 @@ class ConversationRecorder:
|
||||
self._lock = asyncio.Lock()
|
||||
self._seen_events: set[str] = set()
|
||||
self._pending_artifacts: set[asyncio.Task[None]] = set()
|
||||
self._pending_messages: asyncio.Queue[dict] = asyncio.Queue(
|
||||
maxsize=MAX_PENDING_HISTORY_MESSAGES,
|
||||
)
|
||||
self._message_writer: asyncio.Task[None] | None = None
|
||||
self._accepting_messages = True
|
||||
|
||||
def record_transport_message_later(self, message: object) -> None:
|
||||
"""Keep database latency off the media path, with bounded buffering."""
|
||||
if (
|
||||
not self._accepting_messages
|
||||
or not isinstance(message, dict)
|
||||
or message.get("type") not in {
|
||||
"transcript", "assistant-text-end", "workflow-event",
|
||||
}
|
||||
):
|
||||
return
|
||||
try:
|
||||
self._pending_messages.put_nowait(deepcopy(message))
|
||||
except asyncio.QueueFull:
|
||||
logger.warning(f"对话历史写入积压,跳过本条记录: session={self.session_id}")
|
||||
return
|
||||
if self._message_writer is None:
|
||||
self._message_writer = asyncio.create_task(
|
||||
self._write_pending_messages(),
|
||||
name=f"conversation-history-writer:{self.session_id}",
|
||||
)
|
||||
|
||||
async def _write_pending_messages(self) -> None:
|
||||
while True:
|
||||
message = await self._pending_messages.get()
|
||||
try:
|
||||
await self.record_transport_message(message)
|
||||
except Exception as exc:
|
||||
logger.warning(f"对话历史写入失败: {type(exc).__name__}")
|
||||
finally:
|
||||
self._pending_messages.task_done()
|
||||
|
||||
async def flush_pending_messages(self) -> None:
|
||||
"""Drain final transcripts before finishing the conversation row."""
|
||||
self._accepting_messages = False
|
||||
writer = self._message_writer
|
||||
if writer is None:
|
||||
return
|
||||
try:
|
||||
await asyncio.wait_for(
|
||||
self._pending_messages.join(),
|
||||
timeout=HISTORY_FLUSH_TIMEOUT_SECONDS,
|
||||
)
|
||||
except TimeoutError:
|
||||
logger.warning(f"对话历史收尾超时,丢弃剩余缓冲: session={self.session_id}")
|
||||
finally:
|
||||
writer.cancel()
|
||||
await asyncio.gather(writer, return_exceptions=True)
|
||||
self._message_writer = None
|
||||
while not self._pending_messages.empty():
|
||||
self._pending_messages.get_nowait()
|
||||
self._pending_messages.task_done()
|
||||
|
||||
@classmethod
|
||||
async def start(
|
||||
@@ -288,6 +347,7 @@ class ConversationRecorder:
|
||||
raise
|
||||
|
||||
async def _finish(self, *, status: str) -> None:
|
||||
await self.flush_pending_messages()
|
||||
pending_artifacts = list(self._pending_artifacts)
|
||||
if pending_artifacts:
|
||||
await asyncio.gather(*pending_artifacts, return_exceptions=True)
|
||||
|
||||
Reference in New Issue
Block a user