Refactor message sending architecture and implement legacy driver support
- Removed UniversalMessageSender from group_generator.py and private_generator.py. - Updated PlatformIOManager to manage legacy send drivers and ensure send pipeline readiness. - Enhanced LegacyPlatformDriver to utilize prepared messages for sending. - Refactored send_service to unify message sending logic and integrate with Platform IO. - Added regression tests for Platform IO legacy driver and send service functionality.
This commit is contained in:
@@ -1,27 +1,28 @@
|
||||
import time
|
||||
"""PFC 侧消息发送封装。"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from maim_message import Seg
|
||||
from rich.traceback import install
|
||||
|
||||
from src.common.data_models.mai_message_data_model import MaiMessage, UserInfo
|
||||
from src.chat.message_receive.chat_manager import BotChatSession
|
||||
from src.chat.message_receive.message import MessageSending
|
||||
from src.chat.message_receive.uni_message_sender import UniversalMessageSender
|
||||
from src.chat.utils.utils import get_bot_account
|
||||
from src.common.data_models.mai_message_data_model import MaiMessage
|
||||
from src.common.logger import get_logger
|
||||
from src.config.config import global_config
|
||||
from src.services import send_service as send_api
|
||||
|
||||
install(extra_lines=3)
|
||||
|
||||
|
||||
logger = get_logger("message_sender")
|
||||
|
||||
|
||||
class DirectMessageSender:
|
||||
"""直接消息发送器"""
|
||||
"""直接消息发送器。"""
|
||||
|
||||
def __init__(self, private_name: str):
|
||||
def __init__(self, private_name: str) -> None:
|
||||
"""初始化直接消息发送器。
|
||||
|
||||
Args:
|
||||
private_name: 当前私聊实例的名称。
|
||||
"""
|
||||
self.private_name = private_name
|
||||
|
||||
async def send_message(
|
||||
@@ -30,58 +31,31 @@ class DirectMessageSender:
|
||||
content: str,
|
||||
reply_to_message: Optional[MaiMessage] = None,
|
||||
) -> None:
|
||||
"""发送消息到聊天流
|
||||
"""发送文本消息到聊天流。
|
||||
|
||||
Args:
|
||||
chat_stream: 聊天会话
|
||||
content: 消息内容
|
||||
reply_to_message: 要回复的消息(可选)
|
||||
chat_stream: 目标聊天会话。
|
||||
content: 待发送的文本内容。
|
||||
reply_to_message: 可选的引用回复锚点消息。
|
||||
|
||||
Raises:
|
||||
RuntimeError: 当消息发送失败时抛出。
|
||||
"""
|
||||
try:
|
||||
# 创建消息内容
|
||||
segments = Seg(type="seglist", data=[Seg(type="text", data=content)])
|
||||
|
||||
# 获取麦麦的信息
|
||||
bot_user_id = get_bot_account(chat_stream.platform)
|
||||
if not bot_user_id:
|
||||
logger.error(f"[私聊][{self.private_name}]平台 {chat_stream.platform} 未配置机器人账号,无法发送消息")
|
||||
raise RuntimeError(f"平台 {chat_stream.platform} 未配置机器人账号")
|
||||
bot_user_info = UserInfo(
|
||||
user_id=bot_user_id,
|
||||
user_nickname=global_config.bot.nickname,
|
||||
sent = await send_api.text_to_stream(
|
||||
text=content,
|
||||
stream_id=chat_stream.session_id,
|
||||
set_reply=reply_to_message is not None,
|
||||
reply_message=reply_to_message,
|
||||
storage_message=True,
|
||||
)
|
||||
|
||||
# 用当前时间作为message_id,和之前那套sender一样
|
||||
message_id = f"dm{round(time.time(), 2)}"
|
||||
|
||||
# 构建发送者信息(私聊时为接收者)
|
||||
sender_info = None
|
||||
if reply_to_message and reply_to_message.message_info and reply_to_message.message_info.user_info:
|
||||
sender_info = reply_to_message.message_info.user_info
|
||||
|
||||
# 构建消息对象
|
||||
message = MessageSending(
|
||||
message_id=message_id,
|
||||
session=chat_stream,
|
||||
bot_user_info=bot_user_info,
|
||||
sender_info=sender_info,
|
||||
message_segment=segments,
|
||||
reply=reply_to_message,
|
||||
is_head=True,
|
||||
is_emoji=False,
|
||||
thinking_start_time=time.time(),
|
||||
)
|
||||
|
||||
# 发送消息
|
||||
message_sender = UniversalMessageSender()
|
||||
sent = await message_sender.send_message(message, typing=False, set_reply=False, storage_message=True)
|
||||
|
||||
if sent:
|
||||
logger.info(f"[私聊][{self.private_name}]PFC消息已发送: {content}")
|
||||
else:
|
||||
logger.error(f"[私聊][{self.private_name}]PFC消息发送失败")
|
||||
raise RuntimeError("消息发送失败")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[私聊][{self.private_name}]PFC消息发送失败: {str(e)}")
|
||||
logger.error(f"[私聊][{self.private_name}]PFC消息发送失败")
|
||||
raise RuntimeError("消息发送失败")
|
||||
except Exception as exc:
|
||||
logger.error(f"[私聊][{self.private_name}]PFC消息发送失败: {exc}")
|
||||
raise
|
||||
|
||||
@@ -60,8 +60,7 @@ async def _send_message(message: SessionMessage, show_log: bool = True) -> bool:
|
||||
|
||||
发送顺序为:
|
||||
1. WebUI 特殊链路
|
||||
2. Platform IO 适配器链路
|
||||
3. 旧版 ``maim_message`` / API Server 链路
|
||||
2. 旧版 ``maim_message`` / API Server 链路
|
||||
|
||||
Args:
|
||||
message: 待发送的内部会话消息。
|
||||
@@ -124,32 +123,6 @@ async def _send_message(message: SessionMessage, show_log: bool = True) -> bool:
|
||||
logger.info(f"已将消息 '{message_preview}' 发往 WebUI 聊天室")
|
||||
return True
|
||||
|
||||
try:
|
||||
from src.plugin_runtime.integration import get_plugin_runtime_manager
|
||||
|
||||
delivery_batch = await get_plugin_runtime_manager().try_send_message_via_platform_io(message)
|
||||
if delivery_batch is not None:
|
||||
if delivery_batch.has_success:
|
||||
successful_driver_ids = [
|
||||
receipt.driver_id or "unknown"
|
||||
for receipt in delivery_batch.sent_receipts
|
||||
]
|
||||
if show_log:
|
||||
logger.info(
|
||||
f"已通过 Platform IO 将消息 '{message_preview}' 发往平台'{platform}' "
|
||||
f"(drivers: {', '.join(successful_driver_ids)})"
|
||||
)
|
||||
return True
|
||||
|
||||
failed_details = "; ".join(
|
||||
f"driver={receipt.driver_id} status={receipt.status} error={receipt.error}"
|
||||
for receipt in delivery_batch.failed_receipts
|
||||
) or "未命中任何发送路由"
|
||||
logger.warning(f"Platform IO 发送失败: platform={platform} {failed_details}")
|
||||
return False
|
||||
except Exception as exc:
|
||||
logger.warning(f"检查 Platform IO 出站链路时出现异常,将回退旧发送链: {exc}")
|
||||
|
||||
# Fallback 逻辑: 尝试通过 API Server 发送
|
||||
async def send_with_new_api(legacy_exception: Optional[Exception] = None) -> bool:
|
||||
"""通过 API Server 回退链路发送消息。
|
||||
@@ -260,8 +233,21 @@ async def _send_message(message: SessionMessage, show_log: bool = True) -> bool:
|
||||
raise e # 重新抛出其他异常
|
||||
|
||||
|
||||
async def send_prepared_message_to_platform(message: SessionMessage, show_log: bool = True) -> bool:
|
||||
"""发送一条已完成预处理的消息到底层平台。
|
||||
|
||||
Args:
|
||||
message: 已经完成回复组件注入、文本处理等预处理的消息对象。
|
||||
show_log: 是否输出发送成功日志。
|
||||
|
||||
Returns:
|
||||
bool: 发送成功时返回 ``True``。
|
||||
"""
|
||||
return await _send_message(message, show_log=show_log)
|
||||
|
||||
|
||||
class UniversalMessageSender:
|
||||
"""管理消息的注册、即时处理、发送和存储,并跟踪思考状态。"""
|
||||
"""旧链与 WebUI 的底层发送器。"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""初始化统一消息发送器。"""
|
||||
@@ -276,17 +262,18 @@ class UniversalMessageSender:
|
||||
storage_message: bool = True,
|
||||
show_log: bool = True,
|
||||
) -> bool:
|
||||
"""
|
||||
处理、发送并存储一条消息。
|
||||
"""通过旧链或 WebUI 发送并存储一条消息。
|
||||
|
||||
参数:
|
||||
message: MessageSession 对象,待发送的消息。
|
||||
Args:
|
||||
message: 待发送的内部消息对象。
|
||||
typing: 是否模拟打字等待。
|
||||
set_reply: 是否构建回复引用消息。
|
||||
set_reply: 是否构建引用回复消息。
|
||||
reply_message_id: 被引用消息的 ID。
|
||||
storage_message: 是否在发送成功后写入数据库。
|
||||
show_log: 是否输出发送日志。
|
||||
|
||||
|
||||
用法:
|
||||
- typing=True 时,发送前会有打字等待。
|
||||
Returns:
|
||||
bool: 发送成功时返回 ``True``。
|
||||
"""
|
||||
if not message.message_id:
|
||||
logger.error("消息缺少 message_id,无法发送")
|
||||
@@ -339,7 +326,7 @@ class UniversalMessageSender:
|
||||
)
|
||||
await asyncio.sleep(typing_time)
|
||||
|
||||
sent_msg = await _send_message(message, show_log=show_log)
|
||||
sent_msg = await send_prepared_message_to_platform(message, show_log=show_log)
|
||||
if not sent_msg:
|
||||
return False
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ from maim_message import BaseMessageInfo, MessageBase, Seg, UserInfo as MaimUser
|
||||
from src.common.data_models.mai_message_data_model import MaiMessage
|
||||
from src.chat.message_receive.message import SessionMessage
|
||||
from src.chat.message_receive.chat_manager import BotChatSession
|
||||
from src.chat.message_receive.uni_message_sender import UniversalMessageSender
|
||||
from src.chat.utils.timer_calculator import Timer # <--- Import Timer
|
||||
from src.chat.utils.utils import get_bot_account, get_chat_type_and_target_info, is_bot_self
|
||||
from src.prompt.prompt_manager import prompt_manager
|
||||
@@ -51,10 +50,15 @@ class DefaultReplyer:
|
||||
chat_stream: BotChatSession,
|
||||
request_type: str = "replyer",
|
||||
):
|
||||
"""初始化群聊回复器。
|
||||
|
||||
Args:
|
||||
chat_stream: 当前绑定的聊天会话。
|
||||
request_type: LLM 请求类型标识。
|
||||
"""
|
||||
self.express_model = LLMRequest(model_set=model_config.model_task_config.replyer, request_type=request_type)
|
||||
self.chat_stream = chat_stream
|
||||
self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_stream.session_id)
|
||||
self.heart_fc_sender = UniversalMessageSender()
|
||||
|
||||
from src.chat.tool_executor import ToolExecutor
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ from maim_message import BaseMessageInfo, MessageBase, Seg, UserInfo as MaimUser
|
||||
from src.common.data_models.mai_message_data_model import MaiMessage
|
||||
from src.chat.message_receive.message import SessionMessage
|
||||
from src.chat.message_receive.chat_manager import BotChatSession
|
||||
from src.chat.message_receive.uni_message_sender import UniversalMessageSender
|
||||
from src.chat.utils.timer_calculator import Timer
|
||||
from src.chat.utils.utils import get_bot_account, get_chat_type_and_target_info, is_bot_self
|
||||
from src.prompt.prompt_manager import prompt_manager
|
||||
@@ -47,10 +46,15 @@ class PrivateReplyer:
|
||||
chat_stream: BotChatSession,
|
||||
request_type: str = "replyer",
|
||||
):
|
||||
"""初始化私聊回复器。
|
||||
|
||||
Args:
|
||||
chat_stream: 当前绑定的聊天会话。
|
||||
request_type: LLM 请求类型标识。
|
||||
"""
|
||||
self.express_model = LLMRequest(model_set=model_config.model_task_config.replyer, request_type=request_type)
|
||||
self.chat_stream = chat_stream
|
||||
self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_stream.session_id)
|
||||
self.heart_fc_sender = UniversalMessageSender()
|
||||
# self.memory_activator = MemoryActivator()
|
||||
|
||||
from src.chat.tool_executor import ToolExecutor
|
||||
|
||||
Reference in New Issue
Block a user