From c99363680d9001a089fbaa90e742c21779127307 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Sat, 9 May 2026 16:45:42 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E7=A7=BB=E9=99=A4=5Fsource=5Fmessa?= =?UTF-8?q?ges=5Fby=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 2 +- pytests/test_maisaka_builtin_context.py | 19 +++++++++++++++++-- pytests/test_maisaka_monitor_protocol.py | 2 +- src/maisaka/builtin_tool/context.py | 2 +- src/maisaka/builtin_tool/reply.py | 2 +- .../builtin_tool/view_complex_message.py | 2 +- src/maisaka/runtime.py | 19 +++++++++++++++++-- 7 files changed, 39 insertions(+), 9 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 922b524c..23e4b27c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,7 @@ services: - ./data/MaiMBot/emoji:/data/emoji # 持久化表情包 - ./data/MaiMBot/plugins:/MaiMBot/plugins # 插件目录 - ./data/MaiMBot/logs:/MaiMBot/logs # 日志目录 - - ./depends-data:/MaiMBot/depends-data:ro # 运行时资源文件 + - ./depends-data:/MaiMBot/depends-data # 运行时资源文件 # - site-packages:/usr/local/lib/python3.13/site-packages # 持久化Python包,需要时启用 restart: always networks: diff --git a/pytests/test_maisaka_builtin_context.py b/pytests/test_maisaka_builtin_context.py index 46e20648..4d4bf3e9 100644 --- a/pytests/test_maisaka_builtin_context.py +++ b/pytests/test_maisaka_builtin_context.py @@ -6,6 +6,7 @@ from src.common.data_models.mai_message_data_model import MessageInfo, UserInfo from src.common.data_models.message_component_data_model import AtComponent, MessageSequence, ReplyComponent, TextComponent from src.config.config import global_config from src.maisaka.builtin_tool.context import BuiltinToolRuntimeContext +from src.maisaka.runtime import MaisakaHeartFlowChatting def _build_sent_message() -> SessionMessage: @@ -63,7 +64,9 @@ def test_post_process_reply_message_sequences_converts_at_marker_before_bracket_ ) ) ) - runtime = SimpleNamespace(_source_messages_by_id={"12160142": target_message}) + runtime = SimpleNamespace( + find_source_message_by_id=lambda message_id: target_message if message_id == "12160142" else None + ) engine = SimpleNamespace(_get_runtime_manager=lambda: None) tool_ctx = BuiltinToolRuntimeContext(engine=engine, runtime=runtime) @@ -85,7 +88,7 @@ def test_post_process_reply_message_sequences_ignores_at_marker_when_disabled(mo "src.maisaka.builtin_tool.context.process_llm_response", lambda text: [text.strip()] if text.strip() else [], ) - runtime = SimpleNamespace(_source_messages_by_id={}) + runtime = SimpleNamespace(find_source_message_by_id=lambda message_id: None) engine = SimpleNamespace(_get_runtime_manager=lambda: None) tool_ctx = BuiltinToolRuntimeContext(engine=engine, runtime=runtime) @@ -96,3 +99,15 @@ def test_post_process_reply_message_sequences_ignores_at_marker_when_disabled(mo assert len(components) == 1 assert isinstance(components[0], TextComponent) assert components[0].text == "at[12160142] 就这个群" + + +def test_runtime_finds_source_message_from_history() -> None: + target_message = _build_sent_message() + runtime = object.__new__(MaisakaHeartFlowChatting) + runtime._chat_history = [ + SimpleNamespace(message_id="other-message-id", original_message=SimpleNamespace()), + SimpleNamespace(message_id="real-message-id", original_message=target_message), + ] + + assert runtime.find_source_message_by_id("real-message-id") is target_message + assert runtime.find_source_message_by_id("missing-message-id") is None diff --git a/pytests/test_maisaka_monitor_protocol.py b/pytests/test_maisaka_monitor_protocol.py index 6cf8b4e7..7fede4e3 100644 --- a/pytests/test_maisaka_monitor_protocol.py +++ b/pytests/test_maisaka_monitor_protocol.py @@ -199,7 +199,7 @@ async def test_reply_tool_puts_monitor_detail_into_metadata(monkeypatch: pytest. ), ) runtime = SimpleNamespace( - _source_messages_by_id={"msg-1": target_message}, + find_source_message_by_id=lambda message_id: target_message if message_id == "msg-1" else None, log_prefix="[test]", chat_stream=SimpleNamespace(platform=reply_tool_module.CLI_PLATFORM_NAME), session_id="session-1", diff --git a/src/maisaka/builtin_tool/context.py b/src/maisaka/builtin_tool/context.py index ddd267dc..92ca39cd 100644 --- a/src/maisaka/builtin_tool/context.py +++ b/src/maisaka/builtin_tool/context.py @@ -149,7 +149,7 @@ class BuiltinToolRuntimeContext: def _build_at_component_for_message_id(self, message_id: str) -> Optional[AtComponent]: """根据消息编号构造 at 组件。""" - target_message = self.runtime._source_messages_by_id.get(message_id) + target_message = self.runtime.find_source_message_by_id(message_id) if target_message is None: return None diff --git a/src/maisaka/builtin_tool/reply.py b/src/maisaka/builtin_tool/reply.py index a7bb5661..9b3f8447 100644 --- a/src/maisaka/builtin_tool/reply.py +++ b/src/maisaka/builtin_tool/reply.py @@ -113,7 +113,7 @@ async def handle_tool( "reply 工具需要提供有效的 `msg_id` 参数。", ) - target_message = tool_ctx.runtime._source_messages_by_id.get(target_message_id) + target_message = tool_ctx.runtime.find_source_message_by_id(target_message_id) if target_message is None: return tool_ctx.build_failure_result( invocation.tool_name, diff --git a/src/maisaka/builtin_tool/view_complex_message.py b/src/maisaka/builtin_tool/view_complex_message.py index 741a1b4c..49a42cb2 100644 --- a/src/maisaka/builtin_tool/view_complex_message.py +++ b/src/maisaka/builtin_tool/view_complex_message.py @@ -52,7 +52,7 @@ async def handle_tool( "查看复杂消息工具需要提供有效的 `msg_id` 参数。", ) - target_message = tool_ctx.runtime._source_messages_by_id.get(target_message_id) + target_message = tool_ctx.runtime.find_source_message_by_id(target_message_id) if target_message is None: return tool_ctx.build_failure_result( invocation.tool_name, diff --git a/src/maisaka/runtime.py b/src/maisaka/runtime.py index bdb39e1f..eba82a58 100644 --- a/src/maisaka/runtime.py +++ b/src/maisaka/runtime.py @@ -90,7 +90,6 @@ class MaisakaHeartFlowChatting: self._mcp_manager: Optional[MCPManager] = None self._mcp_host_bridge: Optional[MCPHostLLMBridge] = None self._current_cycle_detail: Optional[CycleDetail] = None - self._source_messages_by_id: dict[str, SessionMessage] = {} self._running = False self._cycle_counter = 0 self._internal_loop_task: Optional[asyncio.Task] = None @@ -315,7 +314,6 @@ class MaisakaHeartFlowChatting: self._update_message_trigger_state(message) self.message_cache.append(message) self._message_received_at_by_id[message.message_id] = received_at - self._source_messages_by_id[message.message_id] = message if self._is_reply_effect_tracking_enabled(): asyncio.create_task(self._reply_effect_tracker.observe_user_message(message)) if self._agent_state == self._STATE_RUNNING: @@ -487,6 +485,23 @@ class MaisakaHeartFlowChatting: f"最近10分钟样本数={len(self._recent_reply_latencies)}" ) + def find_source_message_by_id(self, message_id: str) -> Optional[SessionMessage]: + """从 Maisaka 历史中查找指定消息编号对应的原始消息。""" + normalized_message_id = str(message_id or "").strip() + if not normalized_message_id: + return None + + for history_message in reversed(self._chat_history): + if str(getattr(history_message, "message_id", "") or "").strip() != normalized_message_id: + continue + + original_message = getattr(history_message, "original_message", None) + if original_message is None: + continue + return original_message + + return None + def _should_trigger_message_turn_by_idle_compensation( self, *,