From 538c3ae3f27fe523924dd77a77a8129aac928e9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 14 Jun 2025 07:03:25 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../focus_chat/heartflow_message_processor.py | 2 +- .../focus_chat/planners/action_manager.py | 2 +- src/chat/message_receive/bot.py | 1 - src/chat/normal_chat/normal_chat.py | 4 +- .../normal_chat_action_modifier.py | 10 ++- src/chat/utils/chat_message_builder.py | 79 ++++++++++--------- src/chat/utils/utils_image.py | 44 +++++------ src/plugin_system/core/plugin_manager.py | 8 +- src/plugins/built_in/core_actions/plugin.py | 2 +- 9 files changed, 77 insertions(+), 75 deletions(-) diff --git a/src/chat/focus_chat/heartflow_message_processor.py b/src/chat/focus_chat/heartflow_message_processor.py index 5671a6c14..b7bde6b91 100644 --- a/src/chat/focus_chat/heartflow_message_processor.py +++ b/src/chat/focus_chat/heartflow_message_processor.py @@ -11,7 +11,7 @@ from src.common.logger import get_logger import math import re import traceback -from typing import Optional, Tuple, Dict, Any +from typing import Optional, Tuple from maim_message import UserInfo from src.person_info.relationship_manager import get_relationship_manager diff --git a/src/chat/focus_chat/planners/action_manager.py b/src/chat/focus_chat/planners/action_manager.py index c11d89665..555a6df4f 100644 --- a/src/chat/focus_chat/planners/action_manager.py +++ b/src/chat/focus_chat/planners/action_manager.py @@ -378,7 +378,7 @@ class ActionManager: filtered_actions[action_name] = action_info logger.debug(f"动作 {action_name} 在模式 {mode} 下可用 (mode_enable: {action_mode})") # else: - # logger.debug(f"动作 {action_name} 在模式 {mode} 下不可用 (mode_enable: {action_mode})") + # logger.debug(f"动作 {action_name} 在模式 {mode} 下不可用 (mode_enable: {action_mode})") logger.debug(f"模式 {mode} 下可用动作: {list(filtered_actions.keys())}") return filtered_actions diff --git a/src/chat/message_receive/bot.py b/src/chat/message_receive/bot.py index 11556277d..2fcb84c38 100644 --- a/src/chat/message_receive/bot.py +++ b/src/chat/message_receive/bot.py @@ -51,7 +51,6 @@ class ChatBot: async def _process_commands_with_new_system(self, message: MessageRecv): """使用新插件系统处理命令""" try: - text = message.processed_plain_text # 使用新的组件注册中心查找命令 diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index d8fa4cfaa..246a9679a 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -182,7 +182,9 @@ class NormalChat: interested_rate=interest_value * self.willing_amplifier, ) except Exception as e: - logger.error(f"[{self.stream_name}] 处理兴趣消息{msg_id}时出错: {e}\n{traceback.format_exc()}") + logger.error( + f"[{self.stream_name}] 处理兴趣消息{msg_id}时出错: {e}\n{traceback.format_exc()}" + ) finally: self.interest_dict.pop(msg_id, None) diff --git a/src/chat/normal_chat/normal_chat_action_modifier.py b/src/chat/normal_chat/normal_chat_action_modifier.py index cc57c92f6..a4c2402a5 100644 --- a/src/chat/normal_chat/normal_chat_action_modifier.py +++ b/src/chat/normal_chat/normal_chat_action_modifier.py @@ -323,14 +323,16 @@ class NormalChatActionModifier: # 统计1分钟内的回复数量 recent_reply_count = sum(1 for reply in recent_replies if reply["time"] > one_minute_ago) - + print(f"recent_reply_count: {recent_reply_count}") print(f"reply_threshold: {reply_threshold}") - + should_switch = recent_reply_count > reply_threshold if should_switch: - logger.debug(f"{self.log_prefix} 检测到1分钟内回复数量({recent_reply_count})大于{reply_threshold},满足切换到focus模式条件") - + logger.debug( + f"{self.log_prefix} 检测到1分钟内回复数量({recent_reply_count})大于{reply_threshold},满足切换到focus模式条件" + ) + return should_switch def get_available_actions_count(self) -> int: diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index 469750855..12a57f60f 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -180,7 +180,7 @@ def _build_readable_messages_internal( return "", [], pic_id_mapping or {}, pic_counter message_details_raw: List[Tuple[float, str, str]] = [] - + # 使用传入的映射字典,如果没有则创建新的 if pic_id_mapping is None: pic_id_mapping = {} @@ -189,20 +189,20 @@ def _build_readable_messages_internal( def process_pic_ids(content: str) -> str: """处理内容中的图片ID,将其替换为[图片x]格式""" nonlocal current_pic_counter - + # 匹配 [picid:xxxxx] 格式 - pic_pattern = r'\[picid:([^\]]+)\]' - + pic_pattern = r"\[picid:([^\]]+)\]" + def replace_pic_id(match): nonlocal current_pic_counter pic_id = match.group(1) - + if pic_id not in pic_id_mapping: pic_id_mapping[pic_id] = f"图片{current_pic_counter}" current_pic_counter += 1 - + return f"[{pic_id_mapping[pic_id]}]" - + return re.sub(pic_pattern, replace_pic_id, content) # 1 & 2: 获取发送者信息并提取消息组件 @@ -415,7 +415,7 @@ def _build_readable_messages_internal( # 4 & 5: 格式化为字符串 output_lines = [] - + for _i, merged in enumerate(merged_messages): # 使用指定的 timestamp_mode 格式化时间 readable_time = translate_timestamp_to_human_readable(merged["start_time"], mode=timestamp_mode) @@ -445,29 +445,32 @@ def _build_readable_messages_internal( formatted_string = "".join(output_lines).strip() # 返回格式化后的字符串、消息详情列表、图片映射字典和更新后的计数器 - return formatted_string, [(t, n, c) for t, n, c, is_action in message_details if not is_action], pic_id_mapping, current_pic_counter + return ( + formatted_string, + [(t, n, c) for t, n, c, is_action in message_details if not is_action], + pic_id_mapping, + current_pic_counter, + ) def build_pic_mapping_info(pic_id_mapping: Dict[str, str]) -> str: """ 构建图片映射信息字符串,显示图片的具体描述内容 - + Args: pic_id_mapping: 图片ID到显示名称的映射字典 - + Returns: 格式化的映射信息字符串 """ if not pic_id_mapping: return "" - - mapping_lines = [] - + # 按图片编号排序 sorted_items = sorted(pic_id_mapping.items(), key=lambda x: int(x[1].replace("图片", ""))) - + for pic_id, display_name in sorted_items: # 从数据库中获取图片描述 description = "内容正在阅读" @@ -475,12 +478,12 @@ def build_pic_mapping_info(pic_id_mapping: Dict[str, str]) -> str: image = Images.get_or_none(Images.image_id == pic_id) if image and image.description: description = image.description - except Exception as e: + except Exception: # 如果查询失败,保持默认描述 pass - + mapping_lines.append(f"[{display_name}] 的内容:{description}") - + return "\n".join(mapping_lines) @@ -498,12 +501,12 @@ async def build_readable_messages_with_list( formatted_string, details_list, pic_id_mapping, _ = _build_readable_messages_internal( messages, replace_bot_name, merge_messages, timestamp_mode, truncate ) - + # 生成图片映射信息并添加到最前面 pic_mapping_info = build_pic_mapping_info(pic_id_mapping) if pic_mapping_info: formatted_string = f"{pic_mapping_info}\n\n{formatted_string}" - + return formatted_string, details_list @@ -575,7 +578,7 @@ def build_readable_messages( formatted_string, _, pic_id_mapping, _ = _build_readable_messages_internal( copy_messages, replace_bot_name, merge_messages, timestamp_mode, truncate ) - + # 生成图片映射信息并添加到最前面 pic_mapping_info = build_pic_mapping_info(pic_id_mapping) if pic_mapping_info: @@ -593,25 +596,29 @@ def build_readable_messages( # 分别格式化,但使用共享的图片映射 formatted_before, _, pic_id_mapping, pic_counter = _build_readable_messages_internal( - messages_before_mark, replace_bot_name, merge_messages, timestamp_mode, truncate, - pic_id_mapping, pic_counter + messages_before_mark, + replace_bot_name, + merge_messages, + timestamp_mode, + truncate, + pic_id_mapping, + pic_counter, ) formatted_after, _, pic_id_mapping, _ = _build_readable_messages_internal( - messages_after_mark, replace_bot_name, merge_messages, timestamp_mode, False, - pic_id_mapping, pic_counter + messages_after_mark, replace_bot_name, merge_messages, timestamp_mode, False, pic_id_mapping, pic_counter ) read_mark_line = "\n--- 以上消息是你已经看过---\n--- 请关注以下未读的新消息---\n" # 生成图片映射信息 pic_mapping_info = f"图片信息:\n{build_pic_mapping_info(pic_id_mapping)}\n聊天记录信息:\n" - + # 组合结果 result_parts = [] if pic_mapping_info: result_parts.append(pic_mapping_info) result_parts.append("\n") - + if formatted_before and formatted_after: result_parts.extend([formatted_before, read_mark_line, formatted_after]) elif formatted_before: @@ -620,7 +627,7 @@ def build_readable_messages( result_parts.extend([read_mark_line, formatted_after]) else: result_parts.append(read_mark_line.strip()) - + return "".join(result_parts) @@ -636,7 +643,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str: person_map = {} current_char = ord("A") output_lines = [] - + # 图片ID映射字典 pic_id_mapping = {} pic_counter = 1 @@ -644,20 +651,20 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str: def process_pic_ids(content: str) -> str: """处理内容中的图片ID,将其替换为[图片x]格式""" nonlocal pic_counter - + # 匹配 [picid:xxxxx] 格式 - pic_pattern = r'\[picid:([^\]]+)\]' - + pic_pattern = r"\[picid:([^\]]+)\]" + def replace_pic_id(match): nonlocal pic_counter pic_id = match.group(1) - + if pic_id not in pic_id_mapping: pic_id_mapping[pic_id] = f"图片{pic_counter}" pic_counter += 1 - + return f"[{pic_id_mapping[pic_id]}]" - + return re.sub(pic_pattern, replace_pic_id, content) def get_anon_name(platform, user_id): @@ -754,7 +761,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str: if pic_mapping_info: final_output_lines.append(pic_mapping_info) final_output_lines.append("\n\n") - + final_output_lines.extend(output_lines) formatted_string = "".join(final_output_lines).strip() return formatted_string diff --git a/src/chat/utils/utils_image.py b/src/chat/utils/utils_image.py index fceaabb5c..de5fe3cad 100644 --- a/src/chat/utils/utils_image.py +++ b/src/chat/utils/utils_image.py @@ -372,14 +372,14 @@ class ImageManager: Tuple[str, str]: (图片ID, 描述) """ try: - # 生成图片ID + # 生成图片ID # 计算图片哈希 image_bytes = base64.b64decode(image_base64) image_hash = hashlib.md5(image_bytes).hexdigest() - + # 检查图片是否已存在 existing_image = Images.get_or_none(Images.emoji_hash == image_hash) - + if existing_image: # print(f"图片已存在: {existing_image.image_id}") # print(f"图片描述: {existing_image.description}") @@ -391,18 +391,18 @@ class ImageManager: else: # print(f"图片不存在: {image_hash}") image_id = str(uuid.uuid4()) - + # 保存新图片 current_timestamp = time.time() image_dir = os.path.join(self.IMAGE_DIR, "images") os.makedirs(image_dir, exist_ok=True) filename = f"{image_id}.png" file_path = os.path.join(image_dir, filename) - + # 保存文件 with open(file_path, "wb") as f: f.write(image_bytes) - + # 保存到数据库 Images.create( image_id=image_id, @@ -411,14 +411,14 @@ class ImageManager: base64=image_base64, type="image", timestamp=current_timestamp, - vlm_processed=False + vlm_processed=False, ) - + # 启动异步VLM处理 asyncio.create_task(self._process_image_with_vlm(image_id, image_base64)) - + return image_id, f"[picid:{image_id}]" - + except Exception as e: logger.error(f"处理图片失败: {str(e)}") return "", "[图片]" @@ -434,7 +434,7 @@ class ImageManager: # 计算图片哈希 image_bytes = base64.b64decode(image_base64) image_hash = hashlib.md5(image_bytes).hexdigest() - + # 先检查缓存的描述 cached_description = self._get_description_from_db(image_hash, "image") if cached_description: @@ -445,39 +445,35 @@ class ImageManager: image.vlm_processed = True image.save() return - + # 获取图片格式 image_format = Image.open(io.BytesIO(image_bytes)).format.lower() - + # 构建prompt prompt = """请用中文描述这张图片的内容。如果有文字,请把文字描述概括出来,请留意其主题,直观感受,输出为一段平文本,最多50字""" - + # 获取VLM描述 - description, _ = await self._llm.generate_response_for_image( - prompt, - image_base64, - image_format - ) - + description, _ = await self._llm.generate_response_for_image(prompt, image_base64, image_format) + if description is None: logger.warning("VLM未能生成图片描述") description = "无法生成描述" - + # 再次检查缓存,防止并发写入时重复生成 cached_description = self._get_description_from_db(image_hash, "image") if cached_description: logger.warning(f"虽然生成了描述,但是找到缓存图片描述: {cached_description}") description = cached_description - + # 更新数据库 image = Images.get(Images.image_id == image_id) image.description = description image.vlm_processed = True image.save() - + # 保存描述到ImageDescriptions表 self._save_description_to_db(image_hash, description, "image") - + except Exception as e: logger.error(f"VLM处理图片失败: {str(e)}") diff --git a/src/plugin_system/core/plugin_manager.py b/src/plugin_system/core/plugin_manager.py index 7defe0870..de2305234 100644 --- a/src/plugin_system/core/plugin_manager.py +++ b/src/plugin_system/core/plugin_manager.py @@ -32,12 +32,8 @@ class PluginManager: def _ensure_plugin_directories(self): """确保所有插件目录存在,如果不存在则创建""" - default_directories = [ - "src/plugins/built_in", - "src/plugins/examples", - "plugins" - ] - + default_directories = ["src/plugins/built_in", "src/plugins/examples", "plugins"] + for directory in default_directories: if not os.path.exists(directory): os.makedirs(directory, exist_ok=True) diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 5af9564d2..edca3d443 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -267,7 +267,7 @@ class ChangeToFocusChatAction(BaseAction): "聊天上下文中自己的回复条数较多(超过3-4条)", "对话进行得非常热烈活跃", "用户表现出深入交流的意图", - "话题需要更专注和深入的讨论" + "话题需要更专注和深入的讨论", ] async def execute(self) -> Tuple[bool, str]: