Merge branch 'dev' of https://github.com/MaiM-with-u/MaiBot into dev
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
|
||||
## 🔥 更新和安装
|
||||
|
||||
**最新版本: v0.9.0** ([更新日志](changelogs/changelog.md))
|
||||
**最新版本: v0.9.1** ([更新日志](changelogs/changelog.md))
|
||||
|
||||
可前往 [Release](https://github.com/MaiM-with-u/MaiBot/releases/) 页面下载最新版本
|
||||
可前往 [启动器发布页面](https://github.com/MaiM-with-u/mailauncher/releases/)下载最新启动器
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## [0.9.1] - 2025-7-25
|
||||
|
||||
- 修复表达方式迁移空目录问题
|
||||
- 修复reply_to空字段问题
|
||||
- 将metioned bot 和 at应用到focus prompt中
|
||||
|
||||
|
||||
|
||||
## [0.9.0] - 2025-7-25
|
||||
|
||||
### 摘要
|
||||
|
||||
@@ -87,36 +87,90 @@ class ExpressionLearner:
|
||||
request_type="expressor.learner",
|
||||
)
|
||||
self.llm_model = None
|
||||
self._ensure_expression_directories()
|
||||
self._auto_migrate_json_to_db()
|
||||
self._migrate_old_data_create_date()
|
||||
|
||||
def _ensure_expression_directories(self):
|
||||
"""
|
||||
确保表达方式相关的目录结构存在
|
||||
"""
|
||||
base_dir = os.path.join("data", "expression")
|
||||
directories_to_create = [
|
||||
base_dir,
|
||||
os.path.join(base_dir, "learnt_style"),
|
||||
os.path.join(base_dir, "learnt_grammar"),
|
||||
]
|
||||
|
||||
for directory in directories_to_create:
|
||||
try:
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
logger.debug(f"确保目录存在: {directory}")
|
||||
except Exception as e:
|
||||
logger.error(f"创建目录失败 {directory}: {e}")
|
||||
|
||||
def _auto_migrate_json_to_db(self):
|
||||
"""
|
||||
自动将/data/expression/learnt_style 和 learnt_grammar 下所有expressions.json迁移到数据库。
|
||||
迁移完成后在/data/expression/done.done写入标记文件,存在则跳过。
|
||||
"""
|
||||
done_flag = os.path.join("data", "expression", "done.done")
|
||||
base_dir = os.path.join("data", "expression")
|
||||
done_flag = os.path.join(base_dir, "done.done")
|
||||
|
||||
# 确保基础目录存在
|
||||
try:
|
||||
os.makedirs(base_dir, exist_ok=True)
|
||||
logger.debug(f"确保目录存在: {base_dir}")
|
||||
except Exception as e:
|
||||
logger.error(f"创建表达方式目录失败: {e}")
|
||||
return
|
||||
|
||||
if os.path.exists(done_flag):
|
||||
logger.info("表达方式JSON已迁移,无需重复迁移。")
|
||||
return
|
||||
base_dir = os.path.join("data", "expression")
|
||||
|
||||
logger.info("开始迁移表达方式JSON到数据库...")
|
||||
migrated_count = 0
|
||||
|
||||
for type in ["learnt_style", "learnt_grammar"]:
|
||||
type_str = "style" if type == "learnt_style" else "grammar"
|
||||
type_dir = os.path.join(base_dir, type)
|
||||
if not os.path.exists(type_dir):
|
||||
logger.debug(f"目录不存在,跳过: {type_dir}")
|
||||
continue
|
||||
for chat_id in os.listdir(type_dir):
|
||||
|
||||
try:
|
||||
chat_ids = os.listdir(type_dir)
|
||||
logger.debug(f"在 {type_dir} 中找到 {len(chat_ids)} 个聊天ID目录")
|
||||
except Exception as e:
|
||||
logger.error(f"读取目录失败 {type_dir}: {e}")
|
||||
continue
|
||||
|
||||
for chat_id in chat_ids:
|
||||
expr_file = os.path.join(type_dir, chat_id, "expressions.json")
|
||||
if not os.path.exists(expr_file):
|
||||
continue
|
||||
try:
|
||||
with open(expr_file, "r", encoding="utf-8") as f:
|
||||
expressions = json.load(f)
|
||||
|
||||
if not isinstance(expressions, list):
|
||||
logger.warning(f"表达方式文件格式错误,跳过: {expr_file}")
|
||||
continue
|
||||
|
||||
for expr in expressions:
|
||||
if not isinstance(expr, dict):
|
||||
continue
|
||||
|
||||
situation = expr.get("situation")
|
||||
style_val = expr.get("style")
|
||||
count = expr.get("count", 1)
|
||||
last_active_time = expr.get("last_active_time", time.time())
|
||||
|
||||
if not situation or not style_val:
|
||||
logger.warning(f"表达方式缺少必要字段,跳过: {expr}")
|
||||
continue
|
||||
|
||||
# 查重:同chat_id+type+situation+style
|
||||
from src.common.database.database_model import Expression
|
||||
|
||||
@@ -141,14 +195,28 @@ class ExpressionLearner:
|
||||
type=type_str,
|
||||
create_date=last_active_time, # 迁移时使用last_active_time作为创建时间
|
||||
)
|
||||
logger.info(f"已迁移 {expr_file} 到数据库")
|
||||
migrated_count += 1
|
||||
logger.info(f"已迁移 {expr_file} 到数据库,包含 {len(expressions)} 个表达方式")
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"JSON解析失败 {expr_file}: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"迁移表达方式 {expr_file} 失败: {e}")
|
||||
|
||||
# 标记迁移完成
|
||||
try:
|
||||
# 确保done.done文件的父目录存在
|
||||
done_parent_dir = os.path.dirname(done_flag)
|
||||
if not os.path.exists(done_parent_dir):
|
||||
os.makedirs(done_parent_dir, exist_ok=True)
|
||||
logger.debug(f"为done.done创建父目录: {done_parent_dir}")
|
||||
|
||||
with open(done_flag, "w", encoding="utf-8") as f:
|
||||
f.write("done\n")
|
||||
logger.info("表达方式JSON迁移已完成,已写入done.done标记文件")
|
||||
logger.info(f"表达方式JSON迁移已完成,共迁移 {migrated_count} 个表达方式,已写入done.done标记文件")
|
||||
except PermissionError as e:
|
||||
logger.error(f"权限不足,无法写入done.done标记文件: {e}")
|
||||
except OSError as e:
|
||||
logger.error(f"文件系统错误,无法写入done.done标记文件: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"写入done.done标记文件失败: {e}")
|
||||
|
||||
@@ -266,9 +334,17 @@ class ExpressionLearner:
|
||||
for type in ["style", "grammar"]:
|
||||
base_dir = os.path.join("data", "expression", f"learnt_{type}")
|
||||
if not os.path.exists(base_dir):
|
||||
logger.debug(f"目录不存在,跳过衰减: {base_dir}")
|
||||
continue
|
||||
|
||||
for chat_id in os.listdir(base_dir):
|
||||
try:
|
||||
chat_ids = os.listdir(base_dir)
|
||||
logger.debug(f"在 {base_dir} 中找到 {len(chat_ids)} 个聊天ID目录进行衰减")
|
||||
except Exception as e:
|
||||
logger.error(f"读取目录失败 {base_dir}: {e}")
|
||||
continue
|
||||
|
||||
for chat_id in chat_ids:
|
||||
file_path = os.path.join(base_dir, chat_id, "expressions.json")
|
||||
if not os.path.exists(file_path):
|
||||
continue
|
||||
@@ -277,14 +353,24 @@ class ExpressionLearner:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
expressions = json.load(f)
|
||||
|
||||
if not isinstance(expressions, list):
|
||||
logger.warning(f"表达方式文件格式错误,跳过衰减: {file_path}")
|
||||
continue
|
||||
|
||||
# 应用全局衰减
|
||||
decayed_expressions = self.apply_decay_to_expressions(expressions, current_time)
|
||||
|
||||
# 保存衰减后的结果
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(decayed_expressions, f, ensure_ascii=False, indent=2)
|
||||
|
||||
logger.debug(f"已对 {file_path} 应用衰减,剩余 {len(decayed_expressions)} 个表达方式")
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"JSON解析失败,跳过衰减 {file_path}: {e}")
|
||||
except PermissionError as e:
|
||||
logger.error(f"权限不足,无法更新 {file_path}: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"全局衰减{type}表达方式失败: {e}")
|
||||
logger.error(f"全局衰减{type}表达方式失败 {file_path}: {e}")
|
||||
continue
|
||||
|
||||
learnt_style: Optional[List[Tuple[str, str, str]]] = []
|
||||
|
||||
@@ -111,9 +111,9 @@ class HeartFCMessageReceiver:
|
||||
subheartflow: SubHeartflow = await heartflow.get_or_create_subheartflow(chat.stream_id) # type: ignore
|
||||
|
||||
# subheartflow.add_message_to_normal_chat_cache(message, interested_rate, is_mentioned)
|
||||
|
||||
chat_mood = mood_manager.get_mood_by_chat_id(subheartflow.chat_id)
|
||||
asyncio.create_task(chat_mood.update_mood_by_message(message, interested_rate))
|
||||
if global_config.mood.enable_mood:
|
||||
chat_mood = mood_manager.get_mood_by_chat_id(subheartflow.chat_id)
|
||||
asyncio.create_task(chat_mood.update_mood_by_message(message, interested_rate))
|
||||
|
||||
# 3. 日志记录
|
||||
mes_name = chat.group_info.group_name if chat.group_info else "私聊"
|
||||
|
||||
@@ -279,23 +279,29 @@ class ActionPlanner:
|
||||
self.last_obs_time_mark = time.time()
|
||||
|
||||
if mode == ChatMode.FOCUS:
|
||||
mentioned_bonus = ""
|
||||
if global_config.chat.mentioned_bot_inevitable_reply:
|
||||
mentioned_bonus = "\n- 有人提到你"
|
||||
if global_config.chat.at_bot_inevitable_reply:
|
||||
mentioned_bonus = "\n- 有人提到你,或者at你"
|
||||
|
||||
|
||||
by_what = "聊天内容"
|
||||
target_prompt = '\n "target_message_id":"触发action的消息id"'
|
||||
no_action_block = """重要说明1:
|
||||
no_action_block = f"""重要说明1:
|
||||
- 'no_reply' 表示只进行不进行回复,等待合适的回复时机
|
||||
- 当你刚刚发送了消息,没有人回复时,选择no_reply
|
||||
- 当你一次发送了太多消息,为了避免打扰聊天节奏,选择no_reply
|
||||
|
||||
动作:reply
|
||||
动作描述:参与聊天回复,发送文本进行表达
|
||||
- 你想要闲聊或者随便附和
|
||||
- 有人提到你
|
||||
- 你想要闲聊或者随便附和{mentioned_bonus}
|
||||
- 如果你刚刚进行了回复,不要对同一个话题重复回应
|
||||
{
|
||||
{{
|
||||
"action": "reply",
|
||||
"target_message_id":"触发action的消息id",
|
||||
"reason":"回复的原因"
|
||||
}
|
||||
}}
|
||||
|
||||
"""
|
||||
else:
|
||||
|
||||
@@ -74,6 +74,7 @@ def init_prompt():
|
||||
|
||||
你正在{chat_target_2},{reply_target_block}
|
||||
对这句话,你想表达,原句:{raw_reply},原因是:{reason}。你现在要思考怎么组织回复
|
||||
你现在的心情是:{mood_state}
|
||||
你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。请你修改你想表达的原句,符合你的表达风格和语言习惯
|
||||
{config_expression_style},你可以完全重组回复,保留最基本的表达含义就好,但重组后保持语意通顺。
|
||||
{keywords_reaction_prompt}
|
||||
@@ -450,6 +451,9 @@ class DefaultReplyer:
|
||||
def _parse_reply_target(self, target_message: str) -> tuple:
|
||||
sender = ""
|
||||
target = ""
|
||||
# 添加None检查,防止NoneType错误
|
||||
if target_message is None:
|
||||
return sender, target
|
||||
if ":" in target_message or ":" in target_message:
|
||||
# 使用正则表达式匹配中文或英文冒号
|
||||
parts = re.split(pattern=r"[::]", string=target_message, maxsplit=1)
|
||||
@@ -462,6 +466,10 @@ class DefaultReplyer:
|
||||
# 关键词检测与反应
|
||||
keywords_reaction_prompt = ""
|
||||
try:
|
||||
# 添加None检查,防止NoneType错误
|
||||
if target is None:
|
||||
return keywords_reaction_prompt
|
||||
|
||||
# 处理关键词规则
|
||||
for rule in global_config.keyword_reaction.keyword_rules:
|
||||
if any(keyword in target for keyword in rule.keywords):
|
||||
@@ -524,7 +532,7 @@ class DefaultReplyer:
|
||||
# 其他用户的对话
|
||||
background_dialogue_list.append(msg_dict)
|
||||
except Exception as e:
|
||||
logger.error(f"无法处理历史消息记录: {msg_dict}, 错误: {e}")
|
||||
logger.error(f"记录: {msg_dict}, 错误: {e}")
|
||||
|
||||
# 构建背景对话 prompt
|
||||
background_dialogue_prompt = ""
|
||||
@@ -613,9 +621,12 @@ class DefaultReplyer:
|
||||
is_group_chat = bool(chat_stream.group_info)
|
||||
reply_to = reply_data.get("reply_to", "none")
|
||||
extra_info_block = reply_data.get("extra_info", "") or reply_data.get("extra_info_block", "")
|
||||
|
||||
chat_mood = mood_manager.get_mood_by_chat_id(chat_id)
|
||||
mood_prompt = chat_mood.mood_state
|
||||
|
||||
if global_config.mood.enable_mood:
|
||||
chat_mood = mood_manager.get_mood_by_chat_id(chat_id)
|
||||
mood_prompt = chat_mood.mood_state
|
||||
else:
|
||||
mood_prompt = ""
|
||||
|
||||
sender, target = self._parse_reply_target(reply_to)
|
||||
|
||||
@@ -876,6 +887,13 @@ class DefaultReplyer:
|
||||
reason = reply_data.get("reason", "")
|
||||
sender, target = self._parse_reply_target(reply_to)
|
||||
|
||||
# 添加情绪状态获取
|
||||
if global_config.mood.enable_mood:
|
||||
chat_mood = mood_manager.get_mood_by_chat_id(chat_id)
|
||||
mood_prompt = chat_mood.mood_state
|
||||
else:
|
||||
mood_prompt = ""
|
||||
|
||||
message_list_before_now_half = get_raw_msg_before_timestamp_with_chat(
|
||||
chat_id=chat_id,
|
||||
timestamp=time.time(),
|
||||
@@ -956,6 +974,7 @@ class DefaultReplyer:
|
||||
reply_target_block=reply_target_block,
|
||||
raw_reply=raw_reply,
|
||||
reason=reason,
|
||||
mood_state=mood_prompt, # 添加情绪状态参数
|
||||
config_expression_style=global_config.expression.expression_style,
|
||||
keywords_reaction_prompt=keywords_reaction_prompt,
|
||||
moderation_prompt=moderation_prompt_block,
|
||||
|
||||
@@ -78,7 +78,7 @@ def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, float]:
|
||||
# print(f"is_mentioned: {is_mentioned}")
|
||||
# print(f"is_at: {is_at}")
|
||||
|
||||
if is_at and global_config.normal_chat.at_bot_inevitable_reply:
|
||||
if is_at and global_config.chat.at_bot_inevitable_reply:
|
||||
reply_probability = 1.0
|
||||
logger.debug("被@,回复概率设置为100%")
|
||||
else:
|
||||
@@ -103,7 +103,7 @@ def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, float]:
|
||||
for nickname in nicknames:
|
||||
if nickname in message_content:
|
||||
is_mentioned = True
|
||||
if is_mentioned and global_config.normal_chat.mentioned_bot_inevitable_reply:
|
||||
if is_mentioned and global_config.chat.mentioned_bot_inevitable_reply:
|
||||
reply_probability = 1.0
|
||||
logger.debug("被提及,回复概率设置为100%")
|
||||
return is_mentioned, reply_probability
|
||||
|
||||
@@ -35,7 +35,7 @@ class ClassicalWillingManager(BaseWillingManager):
|
||||
if interested_rate > 0.2:
|
||||
current_willing += interested_rate - 0.2
|
||||
|
||||
if willing_info.is_mentioned_bot and global_config.normal_chat.mentioned_bot_inevitable_reply and current_willing < 2:
|
||||
if willing_info.is_mentioned_bot and global_config.chat.mentioned_bot_inevitable_reply and current_willing < 2:
|
||||
current_willing += 1 if current_willing < 1.0 else 0.05
|
||||
|
||||
self.chat_reply_willing[chat_id] = min(current_willing, 1.0)
|
||||
|
||||
@@ -49,7 +49,7 @@ TEMPLATE_DIR = os.path.join(PROJECT_ROOT, "template")
|
||||
|
||||
# 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
|
||||
# 对该字段的更新,请严格参照语义化版本规范:https://semver.org/lang/zh-CN/
|
||||
MMC_VERSION = "0.9.0"
|
||||
MMC_VERSION = "0.9.1"
|
||||
|
||||
|
||||
def get_key_comment(toml_table, key):
|
||||
|
||||
@@ -84,6 +84,12 @@ class ChatConfig(ConfigBase):
|
||||
use_s4u_prompt_mode: bool = False
|
||||
"""是否使用 s4u 对话构建模式,该模式会分开处理当前对话对象和其他所有对话的内容进行 prompt 构建"""
|
||||
|
||||
mentioned_bot_inevitable_reply: bool = False
|
||||
"""提及 bot 必然回复"""
|
||||
|
||||
at_bot_inevitable_reply: bool = False
|
||||
"""@bot 必然回复"""
|
||||
|
||||
# 修改:基于时段的回复频率配置,改为数组格式
|
||||
time_based_talk_frequency: list[str] = field(default_factory=lambda: [])
|
||||
"""
|
||||
@@ -270,11 +276,7 @@ class NormalChatConfig(ConfigBase):
|
||||
response_interested_rate_amplifier: float = 1.0
|
||||
"""回复兴趣度放大系数"""
|
||||
|
||||
mentioned_bot_inevitable_reply: bool = False
|
||||
"""提及 bot 必然回复"""
|
||||
|
||||
at_bot_inevitable_reply: bool = False
|
||||
"""@bot 必然回复"""
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -406,15 +408,9 @@ class MoodConfig(ConfigBase):
|
||||
|
||||
enable_mood: bool = False
|
||||
"""是否启用情绪系统"""
|
||||
|
||||
mood_update_interval: int = 1
|
||||
"""情绪更新间隔(秒)"""
|
||||
|
||||
mood_decay_rate: float = 0.95
|
||||
"""情绪衰减率"""
|
||||
|
||||
mood_intensity_factor: float = 0.7
|
||||
"""情绪强度因子"""
|
||||
|
||||
mood_update_threshold: float = 1.0
|
||||
"""情绪更新阈值,越高,更新越慢"""
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -109,10 +109,15 @@ class LLMRequest:
|
||||
|
||||
def __init__(self, model: dict, **kwargs):
|
||||
# 将大写的配置键转换为小写并从config中获取实际值
|
||||
logger.debug(f"🔍 [模型初始化] 开始初始化模型: {model.get('name', 'Unknown')}")
|
||||
logger.debug(f"🔍 [模型初始化] 模型配置: {model}")
|
||||
logger.debug(f"🔍 [模型初始化] 额外参数: {kwargs}")
|
||||
|
||||
try:
|
||||
# print(f"model['provider']: {model['provider']}")
|
||||
self.api_key = os.environ[f"{model['provider']}_KEY"]
|
||||
self.base_url = os.environ[f"{model['provider']}_BASE_URL"]
|
||||
logger.debug(f"🔍 [模型初始化] 成功获取环境变量: {model['provider']}_KEY 和 {model['provider']}_BASE_URL")
|
||||
except AttributeError as e:
|
||||
logger.error(f"原始 model dict 信息:{model}")
|
||||
logger.error(f"配置错误:找不到对应的配置项 - {str(e)}")
|
||||
@@ -124,6 +129,10 @@ class LLMRequest:
|
||||
self.model_name: str = model["name"]
|
||||
self.params = kwargs
|
||||
|
||||
# 记录配置文件中声明了哪些参数(不管值是什么)
|
||||
self.has_enable_thinking = "enable_thinking" in model
|
||||
self.has_thinking_budget = "thinking_budget" in model
|
||||
|
||||
self.enable_thinking = model.get("enable_thinking", False)
|
||||
self.temp = model.get("temp", 0.7)
|
||||
self.thinking_budget = model.get("thinking_budget", 4096)
|
||||
@@ -132,12 +141,24 @@ class LLMRequest:
|
||||
self.pri_out = model.get("pri_out", 0)
|
||||
self.max_tokens = model.get("max_tokens", global_config.model.model_max_output_length)
|
||||
# print(f"max_tokens: {self.max_tokens}")
|
||||
|
||||
logger.debug(f"🔍 [模型初始化] 模型参数设置完成:")
|
||||
logger.debug(f" - model_name: {self.model_name}")
|
||||
logger.debug(f" - has_enable_thinking: {self.has_enable_thinking}")
|
||||
logger.debug(f" - enable_thinking: {self.enable_thinking}")
|
||||
logger.debug(f" - has_thinking_budget: {self.has_thinking_budget}")
|
||||
logger.debug(f" - thinking_budget: {self.thinking_budget}")
|
||||
logger.debug(f" - temp: {self.temp}")
|
||||
logger.debug(f" - stream: {self.stream}")
|
||||
logger.debug(f" - max_tokens: {self.max_tokens}")
|
||||
logger.debug(f" - base_url: {self.base_url}")
|
||||
|
||||
# 获取数据库实例
|
||||
self._init_database()
|
||||
|
||||
# 从 kwargs 中提取 request_type,如果没有提供则默认为 "default"
|
||||
self.request_type = kwargs.pop("request_type", "default")
|
||||
logger.debug(f"🔍 [模型初始化] 初始化完成,request_type: {self.request_type}")
|
||||
|
||||
@staticmethod
|
||||
def _init_database():
|
||||
@@ -262,11 +283,12 @@ class LLMRequest:
|
||||
if self.temp != 0.7:
|
||||
payload["temperature"] = self.temp
|
||||
|
||||
# 添加enable_thinking参数(如果不是默认值False)
|
||||
if not self.enable_thinking:
|
||||
payload["enable_thinking"] = False
|
||||
# 添加enable_thinking参数(只有配置文件中声明了才添加,不管值是true还是false)
|
||||
if self.has_enable_thinking:
|
||||
payload["enable_thinking"] = self.enable_thinking
|
||||
|
||||
if self.thinking_budget != 4096:
|
||||
# 添加thinking_budget参数(只有配置文件中声明了才添加)
|
||||
if self.has_thinking_budget:
|
||||
payload["thinking_budget"] = self.thinking_budget
|
||||
|
||||
if self.max_tokens:
|
||||
@@ -334,6 +356,19 @@ class LLMRequest:
|
||||
# 似乎是openai流式必须要的东西,不过阿里云的qwq-plus加了这个没有影响
|
||||
if request_content["stream_mode"]:
|
||||
headers["Accept"] = "text/event-stream"
|
||||
|
||||
# 添加请求发送前的调试信息
|
||||
logger.debug(f"🔍 [请求调试] 模型 {self.model_name} 准备发送请求")
|
||||
logger.debug(f"🔍 [请求调试] API URL: {request_content['api_url']}")
|
||||
logger.debug(f"🔍 [请求调试] 请求头: {await self._build_headers(no_key=True, is_formdata=file_bytes is not None)}")
|
||||
|
||||
if not file_bytes:
|
||||
# 安全地记录请求体(隐藏敏感信息)
|
||||
safe_payload = await _safely_record(request_content, request_content["payload"])
|
||||
logger.debug(f"🔍 [请求调试] 请求体: {json.dumps(safe_payload, indent=2, ensure_ascii=False)}")
|
||||
else:
|
||||
logger.debug(f"🔍 [请求调试] 文件上传请求,文件格式: {request_content['file_format']}")
|
||||
|
||||
async with aiohttp.ClientSession(connector=await get_tcp_connector()) as session:
|
||||
post_kwargs = {"headers": headers}
|
||||
# form-data数据上传方式不同
|
||||
@@ -491,7 +526,36 @@ class LLMRequest:
|
||||
logger.warning(f"模型 {self.model_name} 请求限制(429),等待{wait_time}秒后重试...")
|
||||
raise RuntimeError("请求限制(429)")
|
||||
elif response.status in policy["abort_codes"]:
|
||||
if response.status != 403:
|
||||
# 特别处理400错误,添加详细调试信息
|
||||
if response.status == 400:
|
||||
logger.error(f"🔍 [调试信息] 模型 {self.model_name} 参数错误 (400) - 开始详细诊断")
|
||||
logger.error(f"🔍 [调试信息] 模型名称: {self.model_name}")
|
||||
logger.error(f"🔍 [调试信息] API地址: {self.base_url}")
|
||||
logger.error(f"🔍 [调试信息] 模型配置参数:")
|
||||
logger.error(f" - enable_thinking: {self.enable_thinking}")
|
||||
logger.error(f" - temp: {self.temp}")
|
||||
logger.error(f" - thinking_budget: {self.thinking_budget}")
|
||||
logger.error(f" - stream: {self.stream}")
|
||||
logger.error(f" - max_tokens: {self.max_tokens}")
|
||||
logger.error(f" - pri_in: {self.pri_in}")
|
||||
logger.error(f" - pri_out: {self.pri_out}")
|
||||
logger.error(f"🔍 [调试信息] 原始params: {self.params}")
|
||||
|
||||
# 尝试获取服务器返回的详细错误信息
|
||||
try:
|
||||
error_text = await response.text()
|
||||
logger.error(f"🔍 [调试信息] 服务器返回的原始错误内容: {error_text}")
|
||||
|
||||
try:
|
||||
error_json = json.loads(error_text)
|
||||
logger.error(f"🔍 [调试信息] 解析后的错误JSON: {json.dumps(error_json, indent=2, ensure_ascii=False)}")
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"🔍 [调试信息] 错误响应不是有效的JSON格式")
|
||||
except Exception as e:
|
||||
logger.error(f"🔍 [调试信息] 无法读取错误响应内容: {str(e)}")
|
||||
|
||||
raise RequestAbortException("参数错误,请检查调试信息", response)
|
||||
elif response.status != 403:
|
||||
raise RequestAbortException("请求出现错误,中断处理", response)
|
||||
else:
|
||||
raise PermissionDeniedException("模型禁止访问")
|
||||
@@ -510,6 +574,19 @@ class LLMRequest:
|
||||
logger.error(
|
||||
f"模型 {self.model_name} 错误码: {response.status} - {error_code_mapping.get(response.status)}"
|
||||
)
|
||||
|
||||
# 如果是400错误,额外输出请求体信息用于调试
|
||||
if response.status == 400:
|
||||
logger.error(f"🔍 [异常调试] 400错误 - 请求体调试信息:")
|
||||
try:
|
||||
safe_payload = await _safely_record(request_content, payload)
|
||||
logger.error(f"🔍 [异常调试] 发送的请求体: {json.dumps(safe_payload, indent=2, ensure_ascii=False)}")
|
||||
except Exception as debug_error:
|
||||
logger.error(f"🔍 [异常调试] 无法安全记录请求体: {str(debug_error)}")
|
||||
logger.error(f"🔍 [异常调试] 原始payload类型: {type(payload)}")
|
||||
if isinstance(payload, dict):
|
||||
logger.error(f"🔍 [异常调试] 原始payload键: {list(payload.keys())}")
|
||||
|
||||
# print(request_content)
|
||||
# print(response)
|
||||
# 尝试获取并记录服务器返回的详细错误信息
|
||||
@@ -654,14 +731,27 @@ class LLMRequest:
|
||||
"""
|
||||
# 复制一份参数,避免直接修改原始数据
|
||||
new_params = dict(params)
|
||||
|
||||
logger.debug(f"🔍 [参数转换] 模型 {self.model_name} 开始参数转换")
|
||||
logger.debug(f"🔍 [参数转换] 是否为CoT模型: {self.model_name.lower() in self.MODELS_NEEDING_TRANSFORMATION}")
|
||||
logger.debug(f"🔍 [参数转换] CoT模型列表: {self.MODELS_NEEDING_TRANSFORMATION}")
|
||||
|
||||
if self.model_name.lower() in self.MODELS_NEEDING_TRANSFORMATION:
|
||||
logger.debug(f"🔍 [参数转换] 检测到CoT模型,开始参数转换")
|
||||
# 删除 'temperature' 参数(如果存在),但避免删除我们在_build_payload中添加的自定义温度
|
||||
if "temperature" in new_params and new_params["temperature"] == 0.7:
|
||||
new_params.pop("temperature")
|
||||
removed_temp = new_params.pop("temperature")
|
||||
logger.debug(f"🔍 [参数转换] 移除默认temperature参数: {removed_temp}")
|
||||
# 如果存在 'max_tokens',则重命名为 'max_completion_tokens'
|
||||
if "max_tokens" in new_params:
|
||||
old_value = new_params["max_tokens"]
|
||||
new_params["max_completion_tokens"] = new_params.pop("max_tokens")
|
||||
logger.debug(f"🔍 [参数转换] 参数重命名: max_tokens({old_value}) -> max_completion_tokens({new_params['max_completion_tokens']})")
|
||||
else:
|
||||
logger.debug(f"🔍 [参数转换] 非CoT模型,无需参数转换")
|
||||
|
||||
logger.debug(f"🔍 [参数转换] 转换前参数: {params}")
|
||||
logger.debug(f"🔍 [参数转换] 转换后参数: {new_params}")
|
||||
return new_params
|
||||
|
||||
async def _build_formdata_payload(self, file_bytes: bytes, file_format: str) -> aiohttp.FormData:
|
||||
@@ -693,7 +783,12 @@ class LLMRequest:
|
||||
async def _build_payload(self, prompt: str, image_base64: str = None, image_format: str = None) -> dict:
|
||||
"""构建请求体"""
|
||||
# 复制一份参数,避免直接修改 self.params
|
||||
logger.debug(f"🔍 [参数构建] 模型 {self.model_name} 开始构建请求体")
|
||||
logger.debug(f"🔍 [参数构建] 原始self.params: {self.params}")
|
||||
|
||||
params_copy = await self._transform_parameters(self.params)
|
||||
logger.debug(f"🔍 [参数构建] 转换后的params_copy: {params_copy}")
|
||||
|
||||
if image_base64:
|
||||
messages = [
|
||||
{
|
||||
@@ -715,26 +810,37 @@ class LLMRequest:
|
||||
"messages": messages,
|
||||
**params_copy,
|
||||
}
|
||||
|
||||
logger.debug(f"🔍 [参数构建] 基础payload构建完成: {list(payload.keys())}")
|
||||
|
||||
# 添加temp参数(如果不是默认值0.7)
|
||||
if self.temp != 0.7:
|
||||
payload["temperature"] = self.temp
|
||||
logger.debug(f"🔍 [参数构建] 添加temperature参数: {self.temp}")
|
||||
|
||||
# 添加enable_thinking参数(如果不是默认值False)
|
||||
if not self.enable_thinking:
|
||||
payload["enable_thinking"] = False
|
||||
# 添加enable_thinking参数(只有配置文件中声明了才添加,不管值是true还是false)
|
||||
if self.has_enable_thinking:
|
||||
payload["enable_thinking"] = self.enable_thinking
|
||||
logger.debug(f"🔍 [参数构建] 添加enable_thinking参数: {self.enable_thinking}")
|
||||
|
||||
if self.thinking_budget != 4096:
|
||||
# 添加thinking_budget参数(只有配置文件中声明了才添加)
|
||||
if self.has_thinking_budget:
|
||||
payload["thinking_budget"] = self.thinking_budget
|
||||
logger.debug(f"🔍 [参数构建] 添加thinking_budget参数: {self.thinking_budget}")
|
||||
|
||||
if self.max_tokens:
|
||||
payload["max_tokens"] = self.max_tokens
|
||||
logger.debug(f"🔍 [参数构建] 添加max_tokens参数: {self.max_tokens}")
|
||||
|
||||
# if "max_tokens" not in payload and "max_completion_tokens" not in payload:
|
||||
# payload["max_tokens"] = global_config.model.model_max_output_length
|
||||
# 如果 payload 中依然存在 max_tokens 且需要转换,在这里进行再次检查
|
||||
if self.model_name.lower() in self.MODELS_NEEDING_TRANSFORMATION and "max_tokens" in payload:
|
||||
old_value = payload["max_tokens"]
|
||||
payload["max_completion_tokens"] = payload.pop("max_tokens")
|
||||
logger.debug(f"🔍 [参数构建] CoT模型参数转换: max_tokens({old_value}) -> max_completion_tokens({payload['max_completion_tokens']})")
|
||||
|
||||
logger.debug(f"🔍 [参数构建] 最终payload键列表: {list(payload.keys())}")
|
||||
return payload
|
||||
|
||||
def _default_response_handler(
|
||||
|
||||
@@ -83,7 +83,7 @@ class ChatMood:
|
||||
logger.debug(
|
||||
f"base_probability: {base_probability}, time_multiplier: {time_multiplier}, interest_multiplier: {interest_multiplier}"
|
||||
)
|
||||
update_probability = min(1.0, base_probability * time_multiplier * interest_multiplier)
|
||||
update_probability = global_config.mood.mood_update_threshold * min(1.0, base_probability * time_multiplier * interest_multiplier)
|
||||
|
||||
if random.random() > update_probability:
|
||||
return
|
||||
|
||||
@@ -32,13 +32,13 @@ class ReplyAction(BaseAction):
|
||||
|
||||
# 动作基本信息
|
||||
action_name = "reply"
|
||||
action_description = "参与聊天回复,发送文本进行表达"
|
||||
action_description = ""
|
||||
|
||||
# 动作参数定义
|
||||
action_parameters = {}
|
||||
|
||||
# 动作使用场景
|
||||
action_require = ["你想要闲聊或者随便附和", "有人提到你", "如果你刚刚进行了回复,不要对同一个话题重复回应"]
|
||||
action_require = [""]
|
||||
|
||||
# 关联类型
|
||||
associated_types = ["text"]
|
||||
@@ -46,6 +46,9 @@ class ReplyAction(BaseAction):
|
||||
def _parse_reply_target(self, target_message: str) -> tuple:
|
||||
sender = ""
|
||||
target = ""
|
||||
# 添加None检查,防止NoneType错误
|
||||
if target_message is None:
|
||||
return sender, target
|
||||
if ":" in target_message or ":" in target_message:
|
||||
# 使用正则表达式匹配中文或英文冒号
|
||||
parts = re.split(pattern=r"[::]", string=target_message, maxsplit=1)
|
||||
|
||||
@@ -59,6 +59,9 @@ max_context_size = 25 # 上下文长度
|
||||
thinking_timeout = 20 # 麦麦一次回复最长思考规划时间,超过这个时间的思考会放弃(往往是api反应太慢)
|
||||
replyer_random_probability = 0.5 # 首要replyer模型被选择的概率
|
||||
|
||||
mentioned_bot_inevitable_reply = true # 提及 bot 大概率回复
|
||||
at_bot_inevitable_reply = true # @bot 或 提及bot 大概率回复
|
||||
|
||||
use_s4u_prompt_mode = true # 是否使用 s4u 对话构建模式,该模式会更好的把握当前对话对象的对话内容,但是对群聊整理理解能力较差(测试功能!!可能有未知问题!!)
|
||||
|
||||
|
||||
@@ -101,11 +104,8 @@ ban_msgs_regex = [
|
||||
]
|
||||
|
||||
[normal_chat] #普通聊天
|
||||
#一般回复参数
|
||||
willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,mxp模式:mxp,自定义模式:custom(需要你自己实现)
|
||||
response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数
|
||||
mentioned_bot_inevitable_reply = true # 提及 bot 必然回复
|
||||
at_bot_inevitable_reply = true # @bot 必然回复(包含提及)
|
||||
|
||||
[tool]
|
||||
enable_in_normal_chat = false # 是否在普通聊天中启用工具
|
||||
@@ -148,9 +148,7 @@ enable_asr = false # 是否启用语音识别,启用后麦麦可以识别语
|
||||
|
||||
[mood]
|
||||
enable_mood = true # 是否启用情绪系统
|
||||
mood_update_interval = 1.0 # 情绪更新间隔 单位秒
|
||||
mood_decay_rate = 0.95 # 情绪衰减率
|
||||
mood_intensity_factor = 1.0 # 情绪强度因子
|
||||
mood_update_threshold = 1 # 情绪更新阈值,越高,更新越慢
|
||||
|
||||
[lpmm_knowledge] # lpmm知识库配置
|
||||
enable = false # 是否启用lpmm知识库
|
||||
|
||||
Reference in New Issue
Block a user