mirror of
https://github.com/FuQuan233/nonebot-plugin-llmchat.git
synced 2026-08-13 10:09:27 +00:00
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
import asyncio
|
||
from collections.abc import Awaitable, Callable
|
||
from typing import Any
|
||
|
||
from nonebot import logger
|
||
|
||
from .state import ChatState
|
||
|
||
EventProcessor = Callable[[int, bool, ChatState, Any], Awaitable[None]]
|
||
|
||
|
||
class MessageDispatcher:
|
||
"""每个会话只运行一个worker,并可靠接管竞态窗口中新入队的消息。"""
|
||
|
||
def __init__(self, processor: EventProcessor):
|
||
self._processor = processor
|
||
self._tasks: set[asyncio.Task[None]] = set()
|
||
self._closing = False
|
||
|
||
async def enqueue(self, context_id: int, is_group: bool, state: ChatState, event: Any) -> None:
|
||
if self._closing:
|
||
return
|
||
await state.queue.put(event)
|
||
async with state.worker_lock:
|
||
if state.worker_task is None or state.worker_task.done():
|
||
self._start_worker(context_id, is_group, state)
|
||
|
||
def _start_worker(self, context_id: int, is_group: bool, state: ChatState) -> None:
|
||
task = asyncio.create_task(
|
||
self._run_worker(context_id, is_group, state),
|
||
name=f"llmchat:{'group' if is_group else 'private'}:{context_id}",
|
||
)
|
||
state.worker_task = task
|
||
self._tasks.add(task)
|
||
task.add_done_callback(self._tasks.discard)
|
||
|
||
async def _run_worker(self, context_id: int, is_group: bool, state: ChatState) -> None:
|
||
current_task = asyncio.current_task()
|
||
try:
|
||
while True:
|
||
try:
|
||
event = state.queue.get_nowait()
|
||
except asyncio.QueueEmpty:
|
||
break
|
||
try:
|
||
await self._processor(context_id, is_group, state, event)
|
||
except asyncio.CancelledError:
|
||
raise
|
||
except Exception:
|
||
logger.exception(f"处理会话 {context_id} 的消息失败")
|
||
finally:
|
||
state.queue.task_done()
|
||
finally:
|
||
async with state.worker_lock:
|
||
if state.worker_task is current_task:
|
||
state.worker_task = None
|
||
if not self._closing and not state.queue.empty():
|
||
self._start_worker(context_id, is_group, state)
|
||
|
||
async def shutdown(self) -> None:
|
||
self._closing = True
|
||
tasks = list(self._tasks)
|
||
for task in tasks:
|
||
task.cancel()
|
||
if tasks:
|
||
await asyncio.gather(*tasks, return_exceptions=True)
|