更舒适的颜色配置方案,真彩色支持

pull/1496/head
UnCLAS-Prommer 2026-02-14 16:19:04 +08:00
parent 16b16d2ca6
commit c0c003a098
No known key found for this signature in database
3 changed files with 229 additions and 132 deletions

View File

@ -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为需要保存否则在下一次保存时会被删除。

View File

@ -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 转换为模块风格的路径"""

View File

@ -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