diff --git a/changelogs/mai_next_todo.md b/changelogs/mai_next_todo.md index 0ee0d1ab..71711b5e 100644 --- a/changelogs/mai_next_todo.md +++ b/changelogs/mai_next_todo.md @@ -159,6 +159,7 @@ version 0.3.0 - 2026-01-11 - [ ] 映射表 - [ ] `platform_group_user_session_id_map` `平台_群组_用户`-`会话ID` 映射表 - [ ] 将大部分的数据模型均以`Mai`开头命名 +- [x] logger的颜色配置修改为HEX格式,使用自动转换为256色/真彩色的方式实现兼容,同时增加了背景颜色和加粗选项 ### 细节说明 1. Prompt管理系统中保存用户自定义Prompt的时候会只保存被标记为需要保存的Prompt,其他的Prompt文件会全部删除,以防止用户删除Prompt后文件依然存在的问题。因此,如果想在运行时通过修改文件的方式来添加Prompt,需要确保通过对应方法标记该Prompt为需要保存,否则在下一次保存时会被删除。 \ No newline at end of file diff --git a/src/common/logger.py b/src/common/logger.py index 7acc2896..0435b4c8 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -11,6 +11,9 @@ from pathlib import Path from typing import Callable, Optional from datetime import datetime, timedelta +from .logger_color_and_mapping import MODULE_ALIASES, RESET_COLOR, CONVERTED_MODULE_COLORS as MODULE_COLORS + + # 创建logs目录 LOG_DIR = Path("logs") LOG_DIR.mkdir(exist_ok=True) @@ -427,138 +430,6 @@ def reconfigure_existing_loggers(): logger_obj.addHandler(handler) -# 定义模块颜色映射 -MODULE_COLORS = { - # 发送 - # "\033[38;5;67m" 这个颜色代码的含义如下: - # \033 :转义序列的起始,表示后面是控制字符(ESC) - # [38;5;67m : - # 38 :设置前景色(字体颜色),如果是背景色则用 48 - # 5 :表示使用8位(256色)模式 - # 67 :具体的颜色编号(0-255),这里是较暗的蓝色 - "sender": "\033[38;5;24m", # 67号色,较暗的蓝色,适合不显眼的日志 - "send_api": "\033[38;5;24m", # 208号色,橙色,适合突出显示 - # 生成 - "replyer": "\033[38;5;208m", # 橙色 - "llm_api": "\033[38;5;208m", # 橙色 - # 消息处理 - "chat": "\033[38;5;82m", # 亮蓝色 - "chat_image": "\033[38;5;68m", # 浅蓝色 - # emoji - "emoji": "\033[38;5;214m", # 橙黄色,偏向橙色 - "emoji_api": "\033[38;5;214m", # 橙黄色,偏向橙色 - # 核心模块 - "main": "\033[1;97m", # 亮白色+粗体 (主程序) - "memory": "\033[38;5;34m", # 天蓝色 - "memory_retrieval": "\033[38;5;34m", # 天蓝色 - "config": "\033[93m", # 亮黄色 - "common": "\033[95m", # 亮紫色 - "tools": "\033[96m", # 亮青色 - "lpmm": "\033[96m", - "plugin_system": "\033[91m", # 亮红色 - "person_info": "\033[32m", # 绿色 - "manager": "\033[35m", # 紫色 - "llm_models": "\033[36m", # 青色 - "remote": "\033[38;5;242m", # 深灰色,更不显眼 - "planner": "\033[36m", - "relation": "\033[38;5;139m", # 柔和的紫色,不刺眼 - # 聊天相关模块 - "hfc": "\033[38;5;175m", # 柔和的粉色,不显眼但保持粉色系 - "bc": "\033[38;5;175m", # 柔和的粉色,不显眼但保持粉色系 - "sub_heartflow": "\033[38;5;207m", # 粉紫色 - "subheartflow_manager": "\033[38;5;201m", # 深粉色 - "background_tasks": "\033[38;5;240m", # 灰色 - "chat_message": "\033[38;5;45m", # 青色 - "chat_stream": "\033[38;5;51m", # 亮青色 - "message_storage": "\033[38;5;33m", # 深蓝色 - "expressor": "\033[38;5;166m", # 橙色 - # jargon相关 - "jargon": "\033[38;5;220m", # 金黄色,突出显示 - # 插件系统 - "plugins": "\033[31m", # 红色 - "plugin_api": "\033[33m", # 黄色 - "plugin_manager": "\033[38;5;208m", # 红色 - "base_plugin": "\033[38;5;202m", # 橙红色 - "base_command": "\033[38;5;208m", # 橙色 - "component_registry": "\033[38;5;214m", # 橙黄色 - "stream_api": "\033[38;5;220m", # 黄色 - "config_api": "\033[38;5;226m", # 亮黄色 - "heartflow_api": "\033[38;5;154m", # 黄绿色 - "action_apis": "\033[38;5;118m", # 绿色 - "independent_apis": "\033[38;5;82m", # 绿色 - "database_api": "\033[38;5;10m", # 绿色 - "utils_api": "\033[38;5;14m", # 青色 - "message_api": "\033[38;5;6m", # 青色 - # 管理器模块 - "async_task_manager": "\033[38;5;129m", # 紫色 - "mood": "\033[38;5;135m", # 紫红色 - "local_storage": "\033[38;5;141m", # 紫色 - "willing": "\033[38;5;147m", # 浅紫色 - # 工具模块 - "tool_use": "\033[38;5;172m", # 橙褐色 - "tool_executor": "\033[38;5;172m", # 橙褐色 - "base_tool": "\033[38;5;178m", # 金黄色 - # 工具和实用模块 - "prompt_build": "\033[38;5;105m", # 紫色 - "chat_utils": "\033[38;5;111m", # 蓝色 - "maibot_statistic": "\033[38;5;129m", # 紫色 - # 特殊功能插件 - "mute_plugin": "\033[38;5;240m", # 灰色 - "core_actions": "\033[38;5;117m", # 深红色 - "tts_action": "\033[38;5;58m", # 深黄色 - "doubao_pic_plugin": "\033[38;5;64m", # 深绿色 - # Action组件 - "no_reply_action": "\033[38;5;214m", # 亮橙色,显眼但不像警告 - "reply_action": "\033[38;5;46m", # 亮绿色 - "base_action": "\033[38;5;250m", # 浅灰色 - # 数据库和消息 - "database_model": "\033[38;5;94m", # 橙褐色 - "maim_message": "\033[38;5;140m", # 紫褐色 - # 日志系统 - "logger": "\033[38;5;8m", # 深灰色 - "confirm": "\033[1;93m", # 黄色+粗体 - # 模型相关 - "model_utils": "\033[38;5;164m", # 紫红色 - "relationship_fetcher": "\033[38;5;170m", # 浅紫色 - "relationship_builder": "\033[38;5;93m", # 浅蓝色 - "conflict_tracker": "\033[38;5;82m", # 柔和的粉色,不显眼但保持粉色系 -} - -# 定义模块别名映射 - 将真实的logger名称映射到显示的别名 -MODULE_ALIASES = { - # 示例映射 - "sender": "消息发送", - "send_api": "消息发送API", - "replyer": "言语", - "llm_api": "生成API", - "emoji": "表情包", - "emoji_api": "表情包API", - "chat": "所见", - "chat_image": "识图", - "action_manager": "动作", - "memory_activator": "记忆", - "tool_use": "工具", - "expressor": "表达方式", - "database_model": "数据库", - "mood": "情绪", - "memory": "记忆", - "memory_retrieval": "回忆", - "tool_executor": "工具", - "hfc": "聊天节奏", - "plugin_manager": "插件", - "relationship_builder": "关系", - "llm_models": "模型", - "person_info": "人物", - "chat_stream": "聊天流", - "planner": "规划器", - "config": "配置", - "main": "主程序", - "chat_history_summarizer": "聊天概括器", -} - -RESET_COLOR = "\033[0m" - - def convert_pathname_to_module(logger, method_name, event_dict): # sourcery skip: extract-method, use-string-remove-affix """将 pathname 转换为模块风格的路径""" diff --git a/src/common/logger_color_and_mapping.py b/src/common/logger_color_and_mapping.py new file mode 100644 index 00000000..f6df0d4c --- /dev/null +++ b/src/common/logger_color_and_mapping.py @@ -0,0 +1,225 @@ +# 定义模块颜色映射 +from typing import Optional, Tuple, Dict + +import itertools +import os +import sys + + +MODULE_COLORS: Dict[str, Tuple[str, Optional[str], bool]] = { + "sender": ("#005f87", None, False), # 较暗的蓝色,适合不显眼的日志 + "send_api": ("#005f87", None, False), # 橙色,适合突出显示 + # 生成 + "replyer": ("#ff8700", None, False), + "llm_api": ("#ff8700", None, False), + # 消息处理 + "chat": ("#5fff00", None, False), + "chat_image": ("#5f87d7", None, False), + # emoji + "emoji": ("#ffaf00", None, False), # 橙黄色,偏向橙色 + "emoji_api": ("#ffaf00", None, False), # 橙黄色,偏向橙色 + # 核心模块 + "main": ("#ffffff", None, True), # 亮白色 + 粗体 (主程序) + "memory": ("#00af00", None, False), + "memory_retrieval": ("#00af00", None, False), + "config": ("#ffff00", None, False), + "common": ("#ff00ff", None, False), + "tools": ("#00ffff", None, False), + "lpmm": ("#00ffff", None, False), + "plugin_system": ("#ff0000", None, False), + "person_info": ("#008000", None, False), + "manager": ("#800080", None, False), + "llm_models": ("#008080", None, False), + "remote": ("#6c6c6c", None, False), # 深灰色,更不显眼 + "planner": ("#008080", None, False), + "relation": ("#af87af", None, False), # 柔和的紫色,不刺眼 + # 聊天相关模块 + "hfc": ("#d787af", None, False), # 柔和的粉色,不显眼但保持粉色系 + "bc": ("#d787af", None, False), # 柔和的粉色,不显眼但保持粉色系 + "sub_heartflow": ("#ff5fff", None, False), + "subheartflow_manager": ("#ff00ff", None, False), + "background_tasks": ("#585858", None, False), + "chat_message": ("#00d7ff", None, False), + "chat_stream": ("#00ffff", None, False), + "message_storage": ("#0087ff", None, False), + "expressor": ("#d75f00", None, False), + # jargon相关 + "jargon": ("#ffd700", None, False), # 金黄色,突出显示 + # 插件系统 + "plugins": ("#800000", None, False), + "plugin_api": ("#808000", None, False), + "plugin_manager": ("#ff8700", None, False), + "base_plugin": ("#ff5f00", None, False), + "base_command": ("#ff8700", None, False), + "component_registry": ("#ffaf00", None, False), + "stream_api": ("#ffd700", None, False), + "config_api": ("#ffff00", None, False), + "heartflow_api": ("#afff00", None, False), + "action_apis": ("#87ff00", None, False), + "independent_apis": ("#5fff00", None, False), + "database_api": ("#00ff00", None, False), + "utils_api": ("#00ffff", None, False), + "message_api": ("#008080", None, False), + # 管理器模块 + "async_task_manager": ("#af00ff", None, False), + "mood": ("#af5fff", None, False), + "local_storage": ("#af87ff", None, False), + "willing": ("#afafff", None, False), + # 工具模块 + "tool_use": ("#d78700", None, False), + "tool_executor": ("#d78700", None, False), + "base_tool": ("#d7af00", None, False), + # 工具和实用模块 + "prompt_build": ("#8787ff", None, False), + "chat_utils": ("#87afff", None, False), + "maibot_statistic": ("#af00ff", None, False), + # 特殊功能插件 + "mute_plugin": ("#585858", None, False), + "core_actions": ("#87d7ff", None, False), + "tts_action": ("#5f5f00", None, False), + "doubao_pic_plugin": ("#5f8700", None, False), + # Action组件 + "no_reply_action": ("#ffaf00", None, False), # 亮橙色,显眼但不像警告 + "reply_action": ("#00ff00", None, False), + "base_action": ("#bcbcbc", None, False), + # 数据库和消息 + "database_model": ("#875f00", None, False), + "maim_message": ("#af87d7", None, False), + # 日志系统 + "logger": ("#808080", None, False), + "confirm": ("#ffff00", None, True), # 黄色 + 粗体 + # 模型相关 + "model_utils": ("#d700d7", None, False), + "relationship_fetcher": ("#d75fd7", None, False), + "relationship_builder": ("#8700ff", None, False), + "conflict_tracker": ("#5fff00", None, False), # 柔和的粉色,不显眼但保持粉色系 +} + +# 定义模块别名映射 - 将真实的logger名称映射到显示的别名 +MODULE_ALIASES = { + # 示例映射 + "sender": "消息发送", + "send_api": "消息发送API", + "replyer": "言语", + "llm_api": "生成API", + "emoji": "表情包", + "emoji_api": "表情包API", + "chat": "所见", + "chat_image": "识图", + "action_manager": "动作", + "memory_activator": "记忆", + "tool_use": "工具", + "expressor": "表达方式", + "database_model": "数据库", + "mood": "情绪", + "memory": "记忆", + "memory_retrieval": "回忆", + "tool_executor": "工具", + "hfc": "聊天节奏", + "plugin_manager": "插件", + "relationship_builder": "关系", + "llm_models": "模型", + "person_info": "人物", + "chat_stream": "聊天流", + "planner": "规划器", + "config": "配置", + "main": "主程序", + "chat_history_summarizer": "聊天概括器", +} + +RESET_COLOR = "\033[0m" + +CONVERTED_MODULE_COLORS = {} + + +def hex_to_rgb(hex_color: str) -> Tuple[int, int, int]: + s = hex_color.lstrip("#") + if len(s) == 3: + s = "".join(ch * 2 for ch in s) + return int(s[:2], 16), int(s[2:4], 16), int(s[4:6], 16) + + +def supports_truecolor() -> bool: + # sourcery skip: assign-if-exp, reintroduce-else + ct = os.environ.get("COLORTERM", "").lower() + if "truecolor" in ct or "24bit" in ct: + return True + if "WT_SESSION" in os.environ: + return True + return sys.stdout.isatty() + + +def rgb_pair_to_ansi_truecolor( + fg: Tuple[int, int, int], bg: Optional[Tuple[int, int, int]] = None, bold: bool = False +) -> str: + prefix = "1;" if bold else "" + fr, fg_g, fb = fg + if bg is None: + return f"\033[{prefix}38;2;{fr};{fg_g};{fb}m" + br, bg_g, bb = bg + return f"\033[{prefix}38;2;{fr};{fg_g};{fb};48;2;{br};{bg_g};{bb}m" + + +def rgb_to_256_index(r: int, g: int, b: int) -> int: + base16 = [ + (0, 0, 0), + (128, 0, 0), + (0, 128, 0), + (128, 128, 0), + (0, 0, 128), + (128, 0, 128), + (0, 128, 128), + (192, 192, 192), + (128, 128, 128), + (255, 0, 0), + (0, 255, 0), + (255, 255, 0), + (0, 0, 255), + (255, 0, 255), + (0, 255, 255), + (255, 255, 255), + ] + palette = base16[:] + levels = [0, 95, 135, 175, 215, 255] + for ri, gi, bi in itertools.product(range(6), range(6), range(6)): + palette.append((levels[ri], levels[gi], levels[bi])) + for i in range(24): + v = 8 + i * 10 + palette.append((v, v, v)) + best_idx = 0 + best_dist = float("inf") + for idx, (pr, pg, pb) in enumerate(palette): + d = (pr - r) ** 2 + (pg - g) ** 2 + (pb - b) ** 2 + if d < best_dist: + best_dist = d + best_idx = idx + return best_idx + + +def idx_pair_to_ansi_256(fg_idx: int, bg_idx: Optional[int] = None, bold: bool = False) -> str: + prefix = "1;" if bold else "" + if bg_idx is None: + return f"\033[{prefix}38;5;{fg_idx}m" + return f"\033[{prefix}38;5;{fg_idx};48;5;{bg_idx}m" + + +def hex_pair_to_ansi(hex_fg: str, hex_bg: Optional[str] = None, bold: bool = False) -> str: + """ + 返回 escape_str + 背景可选(hex_bg=None 表示只设置前景色) + """ + fg_rgb = hex_to_rgb(hex_fg) + bg_rgb = hex_to_rgb(hex_bg) if hex_bg is not None else None + fg_idx = rgb_to_256_index(*fg_rgb) + bg_idx = rgb_to_256_index(*bg_rgb) if bg_rgb is not None else None + return idx_pair_to_ansi_256(fg_idx, bg_idx, bold) + + +if not supports_truecolor(): + for name, (hex_fore_color, hex_back_color, bold) in MODULE_COLORS.items(): + escape_str = hex_pair_to_ansi(hex_fore_color, hex_back_color, bold) + CONVERTED_MODULE_COLORS[name] = escape_str +else: + for name, (hex_fore_color, hex_back_color, bold) in MODULE_COLORS.items(): + escape_str = rgb_pair_to_ansi_truecolor(hex_to_rgb(hex_fore_color), hex_to_rgb(hex_back_color) if hex_back_color else None, bold) + CONVERTED_MODULE_COLORS[name] = escape_str \ No newline at end of file