fix: 优化chat/config.py的日志&格式

This commit is contained in:
AL76
2025-03-09 22:41:27 +08:00
parent 980cde7230
commit 41aa974d2e

View File

@@ -6,45 +6,46 @@ import tomli
from loguru import logger from loguru import logger
from packaging import version from packaging import version
from packaging.version import Version, InvalidVersion from packaging.version import Version, InvalidVersion
from packaging.specifiers import SpecifierSet,InvalidSpecifier from packaging.specifiers import SpecifierSet, InvalidSpecifier
@dataclass @dataclass
class BotConfig: class BotConfig:
"""机器人配置类""" """机器人配置类"""
INNER_VERSION: Version = None INNER_VERSION: Version = None
BOT_QQ: Optional[int] = 1 BOT_QQ: Optional[int] = 1
BOT_NICKNAME: Optional[str] = None BOT_NICKNAME: Optional[str] = None
# 消息处理相关配置 # 消息处理相关配置
MIN_TEXT_LENGTH: int = 2 # 最小处理文本长度 MIN_TEXT_LENGTH: int = 2 # 最小处理文本长度
MAX_CONTEXT_SIZE: int = 15 # 上下文最大消息数 MAX_CONTEXT_SIZE: int = 15 # 上下文最大消息数
emoji_chance: float = 0.2 # 发送表情包的基础概率 emoji_chance: float = 0.2 # 发送表情包的基础概率
ENABLE_PIC_TRANSLATE: bool = True # 是否启用图片翻译 ENABLE_PIC_TRANSLATE: bool = True # 是否启用图片翻译
talk_allowed_groups = set() talk_allowed_groups = set()
talk_frequency_down_groups = set() talk_frequency_down_groups = set()
thinking_timeout: int = 100 # 思考时间 thinking_timeout: int = 100 # 思考时间
response_willing_amplifier: float = 1.0 # 回复意愿放大系数 response_willing_amplifier: float = 1.0 # 回复意愿放大系数
response_interested_rate_amplifier: float = 1.0 # 回复兴趣度放大系数 response_interested_rate_amplifier: float = 1.0 # 回复兴趣度放大系数
down_frequency_rate: float = 3.5 # 降低回复频率的群组回复意愿降低系数 down_frequency_rate: float = 3.5 # 降低回复频率的群组回复意愿降低系数
ban_user_id = set() ban_user_id = set()
build_memory_interval: int = 30 # 记忆构建间隔(秒) build_memory_interval: int = 30 # 记忆构建间隔(秒)
forget_memory_interval: int = 300 # 记忆遗忘间隔(秒) forget_memory_interval: int = 300 # 记忆遗忘间隔(秒)
EMOJI_CHECK_INTERVAL: int = 120 # 表情包检查间隔(分钟) EMOJI_CHECK_INTERVAL: int = 120 # 表情包检查间隔(分钟)
EMOJI_REGISTER_INTERVAL: int = 10 # 表情包注册间隔(分钟) EMOJI_REGISTER_INTERVAL: int = 10 # 表情包注册间隔(分钟)
EMOJI_SAVE: bool = True # 偷表情包 EMOJI_SAVE: bool = True # 偷表情包
EMOJI_CHECK: bool = False #是否开启过滤 EMOJI_CHECK: bool = False # 是否开启过滤
EMOJI_CHECK_PROMPT: str = "符合公序良俗" # 表情包过滤要求 EMOJI_CHECK_PROMPT: str = "符合公序良俗" # 表情包过滤要求
ban_words = set() ban_words = set()
max_response_length: int = 1024 # 最大回复长度 max_response_length: int = 1024 # 最大回复长度
# 模型配置 # 模型配置
llm_reasoning: Dict[str, str] = field(default_factory=lambda: {}) llm_reasoning: Dict[str, str] = field(default_factory=lambda: {})
llm_reasoning_minor: Dict[str, str] = field(default_factory=lambda: {}) llm_reasoning_minor: Dict[str, str] = field(default_factory=lambda: {})
@@ -60,34 +61,34 @@ class BotConfig:
MODEL_R1_PROBABILITY: float = 0.8 # R1模型概率 MODEL_R1_PROBABILITY: float = 0.8 # R1模型概率
MODEL_V3_PROBABILITY: float = 0.1 # V3模型概率 MODEL_V3_PROBABILITY: float = 0.1 # V3模型概率
MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率 MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率
enable_advance_output: bool = False # 是否启用高级输出 enable_advance_output: bool = False # 是否启用高级输出
enable_kuuki_read: bool = True # 是否启用读空气功能 enable_kuuki_read: bool = True # 是否启用读空气功能
mood_update_interval: float = 1.0 # 情绪更新间隔 单位秒
mood_decay_rate: float = 0.95 # 情绪衰减率
mood_intensity_factor: float = 0.7 # 情绪强度因子
keywords_reaction_rules = [] # 关键词回复规则 mood_update_interval: float = 1.0 # 情绪更新间隔 单位秒
mood_decay_rate: float = 0.95 # 情绪衰减率
mood_intensity_factor: float = 0.7 # 情绪强度因子
chinese_typo_enable=True # 是否启用中文错别字生成器 keywords_reaction_rules = [] # 关键词回复规则
chinese_typo_error_rate=0.03 # 单字替换概率
chinese_typo_min_freq=7 # 最小字频阈值 chinese_typo_enable = True # 是否启用中文错别字生成器
chinese_typo_tone_error_rate=0.2 # 声调错误概率 chinese_typo_error_rate = 0.03 # 单字替换概率
chinese_typo_word_replace_rate=0.02 # 整词替换概率 chinese_typo_min_freq = 7 # 最小字频阈值
chinese_typo_tone_error_rate = 0.2 # 声调错误概率
chinese_typo_word_replace_rate = 0.02 # 整词替换概率
# 默认人设 # 默认人设
PROMPT_PERSONALITY=[ PROMPT_PERSONALITY = [
"曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧", "曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧",
"是一个女大学生,你有黑色头发,你会刷小红书", "是一个女大学生,你有黑色头发,你会刷小红书",
"是一个女大学生你会刷b站对ACG文化感兴趣" "是一个女大学生你会刷b站对ACG文化感兴趣"
] ]
PROMPT_SCHEDULE_GEN="一个曾经学习地质,现在学习心理学和脑科学的女大学生喜欢刷qq贴吧知乎和小红书" PROMPT_SCHEDULE_GEN = "一个曾经学习地质,现在学习心理学和脑科学的女大学生喜欢刷qq贴吧知乎和小红书"
PERSONALITY_1: float = 0.6 # 第一种人格概率 PERSONALITY_1: float = 0.6 # 第一种人格概率
PERSONALITY_2: float = 0.3 # 第二种人格概率 PERSONALITY_2: float = 0.3 # 第二种人格概率
PERSONALITY_3: float = 0.1 # 第三种人格概率 PERSONALITY_3: float = 0.1 # 第三种人格概率
@staticmethod @staticmethod
def get_config_dir() -> str: def get_config_dir() -> str:
"""获取配置文件目录""" """获取配置文件目录"""
@@ -97,7 +98,7 @@ class BotConfig:
if not os.path.exists(config_dir): if not os.path.exists(config_dir):
os.makedirs(config_dir) os.makedirs(config_dir)
return config_dir return config_dir
@classmethod @classmethod
def convert_to_specifierset(cls, value: str) -> SpecifierSet: def convert_to_specifierset(cls, value: str) -> SpecifierSet:
"""将 字符串 版本表达式转换成 SpecifierSet """将 字符串 版本表达式转换成 SpecifierSet
@@ -117,7 +118,7 @@ class BotConfig:
exit(1) exit(1)
return converted return converted
@classmethod @classmethod
def get_config_version(cls, toml: dict) -> Version: def get_config_version(cls, toml: dict) -> Version:
"""提取配置文件的 SpecifierSet 版本数据 """提取配置文件的 SpecifierSet 版本数据
@@ -129,14 +130,14 @@ class BotConfig:
if 'inner' in toml: if 'inner' in toml:
try: try:
config_version : str = toml["inner"]["version"] config_version: str = toml["inner"]["version"]
except KeyError as e: except KeyError as e:
logger.error(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件") logger.error(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件")
raise KeyError(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件") raise KeyError(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件")
else: else:
toml["inner"] = { "version": "0.0.0" } toml["inner"] = {"version": "0.0.0"}
config_version = toml["inner"]["version"] config_version = toml["inner"]["version"]
try: try:
ver = version.parse(config_version) ver = version.parse(config_version)
except InvalidVersion as e: except InvalidVersion as e:
@@ -148,38 +149,38 @@ class BotConfig:
raise InvalidVersion("配置文件中 inner段 的 version 键是错误的版本描述\n") raise InvalidVersion("配置文件中 inner段 的 version 键是错误的版本描述\n")
return ver return ver
@classmethod @classmethod
def load_config(cls, config_path: str = None) -> "BotConfig": def load_config(cls, config_path: str = None) -> "BotConfig":
"""从TOML配置文件加载配置""" """从TOML配置文件加载配置"""
config = cls() config = cls()
def personality(parent: dict): def personality(parent: dict):
personality_config=parent['personality'] personality_config = parent['personality']
personality=personality_config.get('prompt_personality') personality = personality_config.get('prompt_personality')
if len(personality) >= 2: if len(personality) >= 2:
logger.info(f"载入自定义人格:{personality}") logger.info(f"载入自定义人格:{personality}")
config.PROMPT_PERSONALITY=personality_config.get('prompt_personality',config.PROMPT_PERSONALITY) config.PROMPT_PERSONALITY = personality_config.get('prompt_personality', config.PROMPT_PERSONALITY)
logger.info(f"载入自定义日程prompt:{personality_config.get('prompt_schedule',config.PROMPT_SCHEDULE_GEN)}") logger.info(f"载入自定义日程prompt:{personality_config.get('prompt_schedule', config.PROMPT_SCHEDULE_GEN)}")
config.PROMPT_SCHEDULE_GEN=personality_config.get('prompt_schedule',config.PROMPT_SCHEDULE_GEN) config.PROMPT_SCHEDULE_GEN = personality_config.get('prompt_schedule', config.PROMPT_SCHEDULE_GEN)
if config.INNER_VERSION in SpecifierSet(">=0.0.2"): if config.INNER_VERSION in SpecifierSet(">=0.0.2"):
config.PERSONALITY_1=personality_config.get('personality_1_probability',config.PERSONALITY_1) config.PERSONALITY_1 = personality_config.get('personality_1_probability', config.PERSONALITY_1)
config.PERSONALITY_2=personality_config.get('personality_2_probability',config.PERSONALITY_2) config.PERSONALITY_2 = personality_config.get('personality_2_probability', config.PERSONALITY_2)
config.PERSONALITY_3=personality_config.get('personality_3_probability',config.PERSONALITY_3) config.PERSONALITY_3 = personality_config.get('personality_3_probability', config.PERSONALITY_3)
def emoji(parent: dict): def emoji(parent: dict):
emoji_config = parent["emoji"] emoji_config = parent["emoji"]
config.EMOJI_CHECK_INTERVAL = emoji_config.get("check_interval", config.EMOJI_CHECK_INTERVAL) config.EMOJI_CHECK_INTERVAL = emoji_config.get("check_interval", config.EMOJI_CHECK_INTERVAL)
config.EMOJI_REGISTER_INTERVAL = emoji_config.get("register_interval", config.EMOJI_REGISTER_INTERVAL) config.EMOJI_REGISTER_INTERVAL = emoji_config.get("register_interval", config.EMOJI_REGISTER_INTERVAL)
config.EMOJI_CHECK_PROMPT = emoji_config.get('check_prompt',config.EMOJI_CHECK_PROMPT) config.EMOJI_CHECK_PROMPT = emoji_config.get('check_prompt', config.EMOJI_CHECK_PROMPT)
config.EMOJI_SAVE = emoji_config.get('auto_save',config.EMOJI_SAVE) config.EMOJI_SAVE = emoji_config.get('auto_save', config.EMOJI_SAVE)
config.EMOJI_CHECK = emoji_config.get('enable_check',config.EMOJI_CHECK) config.EMOJI_CHECK = emoji_config.get('enable_check', config.EMOJI_CHECK)
def cq_code(parent: dict): def cq_code(parent: dict):
cq_code_config = parent["cq_code"] cq_code_config = parent["cq_code"]
config.ENABLE_PIC_TRANSLATE = cq_code_config.get("enable_pic_translate", config.ENABLE_PIC_TRANSLATE) config.ENABLE_PIC_TRANSLATE = cq_code_config.get("enable_pic_translate", config.ENABLE_PIC_TRANSLATE)
def bot(parent: dict): def bot(parent: dict):
# 机器人基础配置 # 机器人基础配置
bot_config = parent["bot"] bot_config = parent["bot"]
@@ -191,12 +192,13 @@ class BotConfig:
response_config = parent["response"] response_config = parent["response"]
config.MODEL_R1_PROBABILITY = response_config.get("model_r1_probability", config.MODEL_R1_PROBABILITY) config.MODEL_R1_PROBABILITY = response_config.get("model_r1_probability", config.MODEL_R1_PROBABILITY)
config.MODEL_V3_PROBABILITY = response_config.get("model_v3_probability", config.MODEL_V3_PROBABILITY) config.MODEL_V3_PROBABILITY = response_config.get("model_v3_probability", config.MODEL_V3_PROBABILITY)
config.MODEL_R1_DISTILL_PROBABILITY = response_config.get("model_r1_distill_probability", config.MODEL_R1_DISTILL_PROBABILITY) config.MODEL_R1_DISTILL_PROBABILITY = response_config.get("model_r1_distill_probability",
config.MODEL_R1_DISTILL_PROBABILITY)
config.max_response_length = response_config.get("max_response_length", config.max_response_length) config.max_response_length = response_config.get("max_response_length", config.max_response_length)
def model(parent: dict): def model(parent: dict):
# 加载模型配置 # 加载模型配置
model_config:dict = parent["model"] model_config: dict = parent["model"]
config_list = [ config_list = [
"llm_reasoning", "llm_reasoning",
@@ -213,24 +215,24 @@ class BotConfig:
for item in config_list: for item in config_list:
if item in model_config: if item in model_config:
cfg_item:dict = model_config[item] cfg_item: dict = model_config[item]
# base_url 的例子: SILICONFLOW_BASE_URL # base_url 的例子: SILICONFLOW_BASE_URL
# key 的例子: SILICONFLOW_KEY # key 的例子: SILICONFLOW_KEY
cfg_target = { cfg_target = {
"name" : "", "name": "",
"base_url" : "", "base_url": "",
"key" : "", "key": "",
"pri_in" : 0, "pri_in": 0,
"pri_out" : 0 "pri_out": 0
} }
if config.INNER_VERSION in SpecifierSet("<=0.0.0"): if config.INNER_VERSION in SpecifierSet("<=0.0.0"):
cfg_target = cfg_item cfg_target = cfg_item
elif config.INNER_VERSION in SpecifierSet(">=0.0.1"): elif config.INNER_VERSION in SpecifierSet(">=0.0.1"):
stable_item = ["name","pri_in","pri_out"] stable_item = ["name", "pri_in", "pri_out"]
pricing_item = ["pri_in","pri_out"] pricing_item = ["pri_in", "pri_out"]
# 从配置中原始拷贝稳定字段 # 从配置中原始拷贝稳定字段
for i in stable_item: for i in stable_item:
# 如果 字段 属于计费项 且获取不到,那默认值是 0 # 如果 字段 属于计费项 且获取不到,那默认值是 0
@@ -244,18 +246,16 @@ class BotConfig:
logger.error(f"{item} 中的必要字段 {e} 不存在,请检查") logger.error(f"{item} 中的必要字段 {e} 不存在,请检查")
raise KeyError(f"{item} 中的必要字段 {e} 不存在,请检查") raise KeyError(f"{item} 中的必要字段 {e} 不存在,请检查")
provider = cfg_item.get("provider") provider = cfg_item.get("provider")
if provider == None: if provider == None:
logger.error(f"provider 字段在模型配置 {item} 中不存在,请检查") logger.error(f"provider 字段在模型配置 {item} 中不存在,请检查")
raise KeyError(f"provider 字段在模型配置 {item} 中不存在,请检查") raise KeyError(f"provider 字段在模型配置 {item} 中不存在,请检查")
cfg_target["base_url"] = f"{provider}_BASE_URL" cfg_target["base_url"] = f"{provider}_BASE_URL"
cfg_target["key"] = f"{provider}_KEY" cfg_target["key"] = f"{provider}_KEY"
# 如果 列表中的项目在 model_config 中,利用反射来设置对应项目 # 如果 列表中的项目在 model_config 中,利用反射来设置对应项目
setattr(config,item,cfg_target) setattr(config, item, cfg_target)
else: else:
logger.error(f"模型 {item} 在config中不存在请检查") logger.error(f"模型 {item} 在config中不存在请检查")
raise KeyError(f"模型 {item} 在config中不存在请检查") raise KeyError(f"模型 {item} 在config中不存在请检查")
@@ -265,12 +265,14 @@ class BotConfig:
config.MIN_TEXT_LENGTH = msg_config.get("min_text_length", config.MIN_TEXT_LENGTH) config.MIN_TEXT_LENGTH = msg_config.get("min_text_length", config.MIN_TEXT_LENGTH)
config.MAX_CONTEXT_SIZE = msg_config.get("max_context_size", config.MAX_CONTEXT_SIZE) config.MAX_CONTEXT_SIZE = msg_config.get("max_context_size", config.MAX_CONTEXT_SIZE)
config.emoji_chance = msg_config.get("emoji_chance", config.emoji_chance) config.emoji_chance = msg_config.get("emoji_chance", config.emoji_chance)
config.ban_words=msg_config.get("ban_words",config.ban_words) config.ban_words = msg_config.get("ban_words", config.ban_words)
if config.INNER_VERSION in SpecifierSet(">=0.0.2"): if config.INNER_VERSION in SpecifierSet(">=0.0.2"):
config.thinking_timeout = msg_config.get("thinking_timeout", config.thinking_timeout) config.thinking_timeout = msg_config.get("thinking_timeout", config.thinking_timeout)
config.response_willing_amplifier = msg_config.get("response_willing_amplifier", config.response_willing_amplifier) config.response_willing_amplifier = msg_config.get("response_willing_amplifier",
config.response_interested_rate_amplifier = msg_config.get("response_interested_rate_amplifier", config.response_interested_rate_amplifier) config.response_willing_amplifier)
config.response_interested_rate_amplifier = msg_config.get("response_interested_rate_amplifier",
config.response_interested_rate_amplifier)
config.down_frequency_rate = msg_config.get("down_frequency_rate", config.down_frequency_rate) config.down_frequency_rate = msg_config.get("down_frequency_rate", config.down_frequency_rate)
def memory(parent: dict): def memory(parent: dict):
@@ -294,8 +296,10 @@ class BotConfig:
config.chinese_typo_enable = chinese_typo_config.get("enable", config.chinese_typo_enable) config.chinese_typo_enable = chinese_typo_config.get("enable", config.chinese_typo_enable)
config.chinese_typo_error_rate = chinese_typo_config.get("error_rate", config.chinese_typo_error_rate) config.chinese_typo_error_rate = chinese_typo_config.get("error_rate", config.chinese_typo_error_rate)
config.chinese_typo_min_freq = chinese_typo_config.get("min_freq", config.chinese_typo_min_freq) config.chinese_typo_min_freq = chinese_typo_config.get("min_freq", config.chinese_typo_min_freq)
config.chinese_typo_tone_error_rate = chinese_typo_config.get("tone_error_rate", config.chinese_typo_tone_error_rate) config.chinese_typo_tone_error_rate = chinese_typo_config.get("tone_error_rate",
config.chinese_typo_word_replace_rate = chinese_typo_config.get("word_replace_rate", config.chinese_typo_word_replace_rate) config.chinese_typo_tone_error_rate)
config.chinese_typo_word_replace_rate = chinese_typo_config.get("word_replace_rate",
config.chinese_typo_word_replace_rate)
def groups(parent: dict): def groups(parent: dict):
groups_config = parent["groups"] groups_config = parent["groups"]
@@ -382,7 +386,7 @@ class BotConfig:
except(tomli.TOMLDecodeError) as e: except(tomli.TOMLDecodeError) as e:
logger.critical(f"配置文件bot_config.toml填写有误请检查第{e.lineno}行第{e.colno}处:{e.msg}") logger.critical(f"配置文件bot_config.toml填写有误请检查第{e.lineno}行第{e.colno}处:{e.msg}")
exit(1) exit(1)
# 获取配置文件版本 # 获取配置文件版本
config.INNER_VERSION = cls.get_config_version(toml_dict) config.INNER_VERSION = cls.get_config_version(toml_dict)
@@ -406,31 +410,32 @@ class BotConfig:
f"当前程序仅支持以下版本范围: {group_specifierset}" f"当前程序仅支持以下版本范围: {group_specifierset}"
) )
raise InvalidVersion(f"当前程序仅支持以下版本范围: {group_specifierset}") raise InvalidVersion(f"当前程序仅支持以下版本范围: {group_specifierset}")
# 如果 necessary 项目存在,而且显式声明是 False进入特殊处理 # 如果 necessary 项目存在,而且显式声明是 False进入特殊处理
elif "necessary" in include_configs[key] and include_configs[key].get("necessary") == False: elif "necessary" in include_configs[key] and include_configs[key].get("necessary") == False:
# 通过 pass 处理的项虽然直接忽略也是可以的,但是为了不增加理解困难,依然需要在这里显式处理 # 通过 pass 处理的项虽然直接忽略也是可以的,但是为了不增加理解困难,依然需要在这里显式处理
if key == "keywords_reaction": if key == "keywords_reaction":
pass pass
else: else:
# 如果用户根本没有需要的配置项,提示缺少配置 # 如果用户根本没有需要的配置项,提示缺少配置
logger.error(f"配置文件中缺少必需的字段: '{key}'") logger.error(f"配置文件中缺少必需的字段: '{key}'")
raise KeyError(f"配置文件中缺少必需的字段: '{key}'") raise KeyError(f"配置文件中缺少必需的字段: '{key}'")
logger.success(f"成功加载配置文件: {config_path}") logger.success(f"成功加载配置文件: {config_path}")
return config return config
# 获取配置文件路径 # 获取配置文件路径
bot_config_floder_path = BotConfig.get_config_dir() bot_config_floder_path = BotConfig.get_config_dir()
print(f"正在品鉴配置文件目录: {bot_config_floder_path}") logger.debug(f"正在品鉴配置文件目录: {bot_config_floder_path}")
bot_config_path = os.path.join(bot_config_floder_path, "bot_config.toml") bot_config_path = os.path.join(bot_config_floder_path, "bot_config.toml")
if os.path.exists(bot_config_path): if os.path.exists(bot_config_path):
# 如果开发环境配置文件不存在,则使用默认配置文件 # 如果开发环境配置文件不存在,则使用默认配置文件
print(f"异常的新鲜,异常的美味: {bot_config_path}") logger.debug(f"异常的新鲜,异常的美味: {bot_config_path}")
logger.info("使用bot配置文件") logger.info("使用bot配置文件")
else: else:
# 配置文件不存在 # 配置文件不存在
@@ -439,8 +444,6 @@ else:
global_config = BotConfig.load_config(config_path=bot_config_path) global_config = BotConfig.load_config(config_path=bot_config_path)
if not global_config.enable_advance_output: if not global_config.enable_advance_output:
logger.remove() logger.remove()
pass pass