frrr
This commit is contained in:
@@ -633,15 +633,12 @@ HFC_STYLE_CONFIG = {
|
|||||||
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 专注聊天 | {message}",
|
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 专注聊天 | {message}",
|
||||||
},
|
},
|
||||||
"simple": {
|
"simple": {
|
||||||
"console_format": (
|
"console_format": ("<level>{time:MM-DD HH:mm}</level> | <light-green>专注聊天 | {message}</light-green>"),
|
||||||
"<level>{time:MM-DD HH:mm}</level> | <light-green>专注聊天 | {message}</light-green>"
|
|
||||||
),
|
|
||||||
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 专注聊天 | {message}",
|
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 专注聊天 | {message}",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CONFIRM_STYLE_CONFIG = {
|
CONFIRM_STYLE_CONFIG = {
|
||||||
"console_format": "<RED>{message}</RED>", # noqa: E501
|
"console_format": "<RED>{message}</RED>", # noqa: E501
|
||||||
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | EULA与PRIVACY确认 | {message}",
|
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | EULA与PRIVACY确认 | {message}",
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ class MaiEmoji:
|
|||||||
def __init__(self, full_path: str):
|
def __init__(self, full_path: str):
|
||||||
if not full_path:
|
if not full_path:
|
||||||
raise ValueError("full_path cannot be empty")
|
raise ValueError("full_path cannot be empty")
|
||||||
self.full_path = full_path # 文件的完整路径 (包括文件名)
|
self.full_path = full_path # 文件的完整路径 (包括文件名)
|
||||||
self.path = os.path.dirname(full_path) # 文件所在的目录路径
|
self.path = os.path.dirname(full_path) # 文件所在的目录路径
|
||||||
self.filename = os.path.basename(full_path) # 文件名
|
self.filename = os.path.basename(full_path) # 文件名
|
||||||
self.embedding = []
|
self.embedding = []
|
||||||
self.hash = "" # 初始为空,在创建实例时会计算
|
self.hash = "" # 初始为空,在创建实例时会计算
|
||||||
self.description = ""
|
self.description = ""
|
||||||
@@ -92,13 +92,13 @@ class MaiEmoji:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logger.error(f"[初始化错误] 文件在处理过程中丢失: {self.full_path}")
|
logger.error(f"[初始化错误] 文件在处理过程中丢失: {self.full_path}")
|
||||||
self.is_deleted = True
|
self.is_deleted = True
|
||||||
return None
|
return None
|
||||||
except base64.binascii.Error as b64_error:
|
except base64.binascii.Error as b64_error:
|
||||||
logger.error(f"[初始化错误] Base64解码失败 ({self.filename}): {b64_error}")
|
logger.error(f"[初始化错误] Base64解码失败 ({self.filename}): {b64_error}")
|
||||||
self.is_deleted = True
|
self.is_deleted = True
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[初始化错误] 初始化表情包时发生未预期错误 ({self.filename}): {str(e)}")
|
logger.error(f"[初始化错误] 初始化表情包时发生未预期错误 ({self.filename}): {str(e)}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
@@ -146,8 +146,8 @@ class MaiEmoji:
|
|||||||
# 准备数据库记录 for emoji collection
|
# 准备数据库记录 for emoji collection
|
||||||
emoji_record = {
|
emoji_record = {
|
||||||
"filename": self.filename,
|
"filename": self.filename,
|
||||||
"path": self.path, # 存储目录路径
|
"path": self.path, # 存储目录路径
|
||||||
"full_path": self.full_path, # 存储完整文件路径
|
"full_path": self.full_path, # 存储完整文件路径
|
||||||
"embedding": self.embedding,
|
"embedding": self.embedding,
|
||||||
"description": self.description,
|
"description": self.description,
|
||||||
"emotion": self.emotion,
|
"emotion": self.emotion,
|
||||||
@@ -170,11 +170,11 @@ class MaiEmoji:
|
|||||||
# 数据库保存失败,是否需要将文件移回?为了简化,暂时只记录错误
|
# 数据库保存失败,是否需要将文件移回?为了简化,暂时只记录错误
|
||||||
# 可以考虑在这里尝试删除已移动的文件,避免残留
|
# 可以考虑在这里尝试删除已移动的文件,避免残留
|
||||||
try:
|
try:
|
||||||
if os.path.exists(self.full_path): # full_path 此时是目标路径
|
if os.path.exists(self.full_path): # full_path 此时是目标路径
|
||||||
os.remove(self.full_path)
|
os.remove(self.full_path)
|
||||||
logger.warning(f"[回滚] 已删除移动失败后残留的文件: {self.full_path}")
|
logger.warning(f"[回滚] 已删除移动失败后残留的文件: {self.full_path}")
|
||||||
except Exception as remove_error:
|
except Exception as remove_error:
|
||||||
logger.error(f"[错误] 回滚删除文件失败: {remove_error}")
|
logger.error(f"[错误] 回滚删除文件失败: {remove_error}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -213,9 +213,11 @@ class MaiEmoji:
|
|||||||
else:
|
else:
|
||||||
# 如果数据库记录删除失败,但文件可能已删除,记录一个警告
|
# 如果数据库记录删除失败,但文件可能已删除,记录一个警告
|
||||||
if not os.path.exists(file_to_delete):
|
if not os.path.exists(file_to_delete):
|
||||||
logger.warning(f"[警告] 表情包文件 {file_to_delete} 已删除,但数据库记录删除失败 (Hash: {self.hash})")
|
logger.warning(
|
||||||
|
f"[警告] 表情包文件 {file_to_delete} 已删除,但数据库记录删除失败 (Hash: {self.hash})"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(f"[错误] 删除表情包数据库记录失败: {self.hash}")
|
logger.error(f"[错误] 删除表情包数据库记录失败: {self.hash}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -323,7 +325,7 @@ class EmojiManager:
|
|||||||
# 计算每个表情包与输入文本的最大情感相似度
|
# 计算每个表情包与输入文本的最大情感相似度
|
||||||
emoji_similarities = []
|
emoji_similarities = []
|
||||||
for emoji in all_emojis:
|
for emoji in all_emojis:
|
||||||
# 跳过已标记为删除的对象
|
# 跳过已标记为删除的对象
|
||||||
if emoji.is_deleted:
|
if emoji.is_deleted:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -421,17 +423,17 @@ class EmojiManager:
|
|||||||
objects_to_remove = []
|
objects_to_remove = []
|
||||||
for emoji in self.emoji_objects:
|
for emoji in self.emoji_objects:
|
||||||
try:
|
try:
|
||||||
# 跳过已经标记为删除的,避免重复处理
|
# 跳过已经标记为删除的,避免重复处理
|
||||||
if emoji.is_deleted:
|
if emoji.is_deleted:
|
||||||
objects_to_remove.append(emoji) # 收集起来一次性移除
|
objects_to_remove.append(emoji) # 收集起来一次性移除
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 检查文件是否存在
|
# 检查文件是否存在
|
||||||
if not os.path.exists(emoji.full_path):
|
if not os.path.exists(emoji.full_path):
|
||||||
logger.warning(f"[检查] 表情包文件丢失: {emoji.full_path}")
|
logger.warning(f"[检查] 表情包文件丢失: {emoji.full_path}")
|
||||||
# 执行表情包对象的删除方法
|
# 执行表情包对象的删除方法
|
||||||
await emoji.delete() # delete 方法现在会标记 is_deleted
|
await emoji.delete() # delete 方法现在会标记 is_deleted
|
||||||
objects_to_remove.append(emoji) # 标记删除后,也收集起来移除
|
objects_to_remove.append(emoji) # 标记删除后,也收集起来移除
|
||||||
# 更新计数
|
# 更新计数
|
||||||
self.emoji_num -= 1
|
self.emoji_num -= 1
|
||||||
removed_count += 1
|
removed_count += 1
|
||||||
@@ -453,7 +455,7 @@ class EmojiManager:
|
|||||||
|
|
||||||
# 从 self.emoji_objects 中移除标记的对象
|
# 从 self.emoji_objects 中移除标记的对象
|
||||||
if objects_to_remove:
|
if objects_to_remove:
|
||||||
self.emoji_objects = [e for e in self.emoji_objects if e not in objects_to_remove]
|
self.emoji_objects = [e for e in self.emoji_objects if e not in objects_to_remove]
|
||||||
|
|
||||||
# 清理 EMOJI_REGISTED_DIR 目录中未被追踪的文件
|
# 清理 EMOJI_REGISTED_DIR 目录中未被追踪的文件
|
||||||
await self.clean_unused_emojis(EMOJI_REGISTED_DIR, self.emoji_objects)
|
await self.clean_unused_emojis(EMOJI_REGISTED_DIR, self.emoji_objects)
|
||||||
@@ -538,7 +540,7 @@ class EmojiManager:
|
|||||||
if not full_path:
|
if not full_path:
|
||||||
logger.warning(f"[加载错误] 数据库记录缺少 'full_path' 字段: {emoji_data.get('_id')}")
|
logger.warning(f"[加载错误] 数据库记录缺少 'full_path' 字段: {emoji_data.get('_id')}")
|
||||||
load_errors += 1
|
load_errors += 1
|
||||||
continue # 跳过缺少 full_path 的记录
|
continue # 跳过缺少 full_path 的记录
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 使用 full_path 初始化 MaiEmoji 对象
|
# 使用 full_path 初始化 MaiEmoji 对象
|
||||||
@@ -548,9 +550,9 @@ class EmojiManager:
|
|||||||
emoji.hash = emoji_data.get("hash", "")
|
emoji.hash = emoji_data.get("hash", "")
|
||||||
# 如果 hash 为空,也跳过?取决于业务逻辑
|
# 如果 hash 为空,也跳过?取决于业务逻辑
|
||||||
if not emoji.hash:
|
if not emoji.hash:
|
||||||
logger.warning(f"[加载错误] 数据库记录缺少 'hash' 字段: {full_path}")
|
logger.warning(f"[加载错误] 数据库记录缺少 'hash' 字段: {full_path}")
|
||||||
load_errors += 1
|
load_errors += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
emoji.description = emoji_data.get("description", "")
|
emoji.description = emoji_data.get("description", "")
|
||||||
emoji.emotion = emoji_data.get("emotion", [])
|
emoji.emotion = emoji_data.get("emotion", [])
|
||||||
@@ -558,21 +560,22 @@ class EmojiManager:
|
|||||||
# 优先使用 last_used_time,否则用 timestamp,最后用当前时间
|
# 优先使用 last_used_time,否则用 timestamp,最后用当前时间
|
||||||
last_used = emoji_data.get("last_used_time")
|
last_used = emoji_data.get("last_used_time")
|
||||||
timestamp = emoji_data.get("timestamp")
|
timestamp = emoji_data.get("timestamp")
|
||||||
emoji.last_used_time = last_used if last_used is not None else (timestamp if timestamp is not None else time.time())
|
emoji.last_used_time = (
|
||||||
|
last_used if last_used is not None else (timestamp if timestamp is not None else time.time())
|
||||||
|
)
|
||||||
emoji.register_time = timestamp if timestamp is not None else time.time()
|
emoji.register_time = timestamp if timestamp is not None else time.time()
|
||||||
emoji.format = emoji_data.get("format", "") # 加载格式
|
emoji.format = emoji_data.get("format", "") # 加载格式
|
||||||
|
|
||||||
# 不需要再手动设置 path 和 filename,__init__ 会自动处理
|
# 不需要再手动设置 path 和 filename,__init__ 会自动处理
|
||||||
|
|
||||||
emoji_objects.append(emoji)
|
emoji_objects.append(emoji)
|
||||||
|
|
||||||
except ValueError as ve: #捕获 __init__ 可能的错误
|
except ValueError as ve: # 捕获 __init__ 可能的错误
|
||||||
logger.error(f"[加载错误] 初始化 MaiEmoji 失败 ({full_path}): {ve}")
|
logger.error(f"[加载错误] 初始化 MaiEmoji 失败 ({full_path}): {ve}")
|
||||||
load_errors += 1
|
load_errors += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[加载错误] 处理数据库记录时出错 ({full_path}): {str(e)}")
|
logger.error(f"[加载错误] 处理数据库记录时出错 ({full_path}): {str(e)}")
|
||||||
load_errors += 1
|
load_errors += 1
|
||||||
|
|
||||||
|
|
||||||
# 更新内存中的列表和数量
|
# 更新内存中的列表和数量
|
||||||
self.emoji_objects = emoji_objects
|
self.emoji_objects = emoji_objects
|
||||||
@@ -580,12 +583,11 @@ class EmojiManager:
|
|||||||
|
|
||||||
logger.success(f"[数据库] 加载完成: 共加载 {self.emoji_num} 个表情包记录。")
|
logger.success(f"[数据库] 加载完成: 共加载 {self.emoji_num} 个表情包记录。")
|
||||||
if load_errors > 0:
|
if load_errors > 0:
|
||||||
logger.warning(f"[数据库] 加载过程中出现 {load_errors} 个错误。")
|
logger.warning(f"[数据库] 加载过程中出现 {load_errors} 个错误。")
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[错误] 从数据库加载所有表情包对象失败: {str(e)}")
|
logger.error(f"[错误] 从数据库加载所有表情包对象失败: {str(e)}")
|
||||||
self.emoji_objects = [] # 加载失败则清空列表
|
self.emoji_objects = [] # 加载失败则清空列表
|
||||||
self.emoji_num = 0
|
self.emoji_num = 0
|
||||||
|
|
||||||
async def get_emoji_from_db(self, hash=None):
|
async def get_emoji_from_db(self, hash=None):
|
||||||
@@ -604,8 +606,9 @@ class EmojiManager:
|
|||||||
if hash:
|
if hash:
|
||||||
query = {"hash": hash}
|
query = {"hash": hash}
|
||||||
else:
|
else:
|
||||||
logger.warning("[查询] 未提供 hash,将尝试加载所有表情包,建议使用 get_all_emoji_from_db 更新管理器状态。")
|
logger.warning(
|
||||||
|
"[查询] 未提供 hash,将尝试加载所有表情包,建议使用 get_all_emoji_from_db 更新管理器状态。"
|
||||||
|
)
|
||||||
|
|
||||||
emoji_data_list = list(db.emoji.find(query))
|
emoji_data_list = list(db.emoji.find(query))
|
||||||
emoji_objects = []
|
emoji_objects = []
|
||||||
@@ -622,29 +625,30 @@ class EmojiManager:
|
|||||||
emoji = MaiEmoji(full_path=full_path)
|
emoji = MaiEmoji(full_path=full_path)
|
||||||
emoji.hash = emoji_data.get("hash", "")
|
emoji.hash = emoji_data.get("hash", "")
|
||||||
if not emoji.hash:
|
if not emoji.hash:
|
||||||
logger.warning(f"[加载错误] 数据库记录缺少 'hash' 字段: {full_path}")
|
logger.warning(f"[加载错误] 数据库记录缺少 'hash' 字段: {full_path}")
|
||||||
load_errors += 1
|
load_errors += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
emoji.description = emoji_data.get("description", "")
|
emoji.description = emoji_data.get("description", "")
|
||||||
emoji.emotion = emoji_data.get("emotion", [])
|
emoji.emotion = emoji_data.get("emotion", [])
|
||||||
emoji.usage_count = emoji_data.get("usage_count", 0)
|
emoji.usage_count = emoji_data.get("usage_count", 0)
|
||||||
last_used = emoji_data.get("last_used_time")
|
last_used = emoji_data.get("last_used_time")
|
||||||
timestamp = emoji_data.get("timestamp")
|
timestamp = emoji_data.get("timestamp")
|
||||||
emoji.last_used_time = last_used if last_used is not None else (timestamp if timestamp is not None else time.time())
|
emoji.last_used_time = (
|
||||||
|
last_used if last_used is not None else (timestamp if timestamp is not None else time.time())
|
||||||
|
)
|
||||||
emoji.register_time = timestamp if timestamp is not None else time.time()
|
emoji.register_time = timestamp if timestamp is not None else time.time()
|
||||||
emoji.format = emoji_data.get("format", "")
|
emoji.format = emoji_data.get("format", "")
|
||||||
emoji_objects.append(emoji)
|
emoji_objects.append(emoji)
|
||||||
except ValueError as ve:
|
except ValueError as ve:
|
||||||
logger.error(f"[加载错误] 初始化 MaiEmoji 失败 ({full_path}): {ve}")
|
logger.error(f"[加载错误] 初始化 MaiEmoji 失败 ({full_path}): {ve}")
|
||||||
load_errors += 1
|
load_errors += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[加载错误] 处理数据库记录时出错 ({full_path}): {str(e)}")
|
logger.error(f"[加载错误] 处理数据库记录时出错 ({full_path}): {str(e)}")
|
||||||
load_errors += 1
|
load_errors += 1
|
||||||
|
|
||||||
|
|
||||||
if load_errors > 0:
|
if load_errors > 0:
|
||||||
logger.warning(f"[查询] 加载过程中出现 {load_errors} 个错误。")
|
logger.warning(f"[查询] 加载过程中出现 {load_errors} 个错误。")
|
||||||
|
|
||||||
return emoji_objects
|
return emoji_objects
|
||||||
|
|
||||||
@@ -661,10 +665,10 @@ class EmojiManager:
|
|||||||
MaiEmoji 或 None: 如果找到则返回 MaiEmoji 对象,否则返回 None
|
MaiEmoji 或 None: 如果找到则返回 MaiEmoji 对象,否则返回 None
|
||||||
"""
|
"""
|
||||||
for emoji in self.emoji_objects:
|
for emoji in self.emoji_objects:
|
||||||
# 确保对象未被标记为删除且哈希值匹配
|
# 确保对象未被标记为删除且哈希值匹配
|
||||||
if not emoji.is_deleted and emoji.hash == hash:
|
if not emoji.is_deleted and emoji.hash == hash:
|
||||||
return emoji
|
return emoji
|
||||||
return None # 如果循环结束还没找到,则返回 None
|
return None # 如果循环结束还没找到,则返回 None
|
||||||
|
|
||||||
async def delete_emoji(self, emoji_hash: str) -> bool:
|
async def delete_emoji(self, emoji_hash: str) -> bool:
|
||||||
"""根据哈希值删除表情包
|
"""根据哈希值删除表情包
|
||||||
@@ -886,14 +890,14 @@ class EmojiManager:
|
|||||||
"""
|
"""
|
||||||
file_full_path = os.path.join(EMOJI_DIR, filename)
|
file_full_path = os.path.join(EMOJI_DIR, filename)
|
||||||
if not os.path.exists(file_full_path):
|
if not os.path.exists(file_full_path):
|
||||||
logger.error(f"[注册失败] 文件不存在: {file_full_path}")
|
logger.error(f"[注册失败] 文件不存在: {file_full_path}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 1. 创建 MaiEmoji 实例并初始化哈希和格式
|
# 1. 创建 MaiEmoji 实例并初始化哈希和格式
|
||||||
new_emoji = MaiEmoji(full_path=file_full_path)
|
new_emoji = MaiEmoji(full_path=file_full_path)
|
||||||
init_result = await new_emoji.initialize_hash_format()
|
init_result = await new_emoji.initialize_hash_format()
|
||||||
if init_result is None or new_emoji.is_deleted: # 初始化失败或文件读取错误
|
if init_result is None or new_emoji.is_deleted: # 初始化失败或文件读取错误
|
||||||
logger.error(f"[注册失败] 初始化哈希和格式失败: {filename}")
|
logger.error(f"[注册失败] 初始化哈希和格式失败: {filename}")
|
||||||
# 是否需要删除源文件?看业务需求,暂时不删
|
# 是否需要删除源文件?看业务需求,暂时不删
|
||||||
return False
|
return False
|
||||||
@@ -901,22 +905,22 @@ class EmojiManager:
|
|||||||
# 2. 检查哈希是否已存在 (在内存中检查)
|
# 2. 检查哈希是否已存在 (在内存中检查)
|
||||||
if await self.get_emoji_from_manager(new_emoji.hash):
|
if await self.get_emoji_from_manager(new_emoji.hash):
|
||||||
logger.warning(f"[注册跳过] 表情包已存在 (Hash: {new_emoji.hash}): {filename}")
|
logger.warning(f"[注册跳过] 表情包已存在 (Hash: {new_emoji.hash}): {filename}")
|
||||||
# 删除重复的源文件
|
# 删除重复的源文件
|
||||||
try:
|
try:
|
||||||
os.remove(file_full_path)
|
os.remove(file_full_path)
|
||||||
logger.info(f"[清理] 删除重复的待注册文件: {filename}")
|
logger.info(f"[清理] 删除重复的待注册文件: {filename}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[错误] 删除重复文件失败: {str(e)}")
|
logger.error(f"[错误] 删除重复文件失败: {str(e)}")
|
||||||
return False # 返回 False 表示未注册新表情
|
return False # 返回 False 表示未注册新表情
|
||||||
|
|
||||||
# 3. 构建描述和情感
|
# 3. 构建描述和情感
|
||||||
try:
|
try:
|
||||||
emoji_base64 = image_path_to_base64(file_full_path)
|
emoji_base64 = image_path_to_base64(file_full_path)
|
||||||
if emoji_base64 is None: # 再次检查读取
|
if emoji_base64 is None: # 再次检查读取
|
||||||
logger.error(f"[注册失败] 无法读取图片以生成描述: {filename}")
|
logger.error(f"[注册失败] 无法读取图片以生成描述: {filename}")
|
||||||
return False
|
return False
|
||||||
description, emotions = await self.build_emoji_description(emoji_base64)
|
description, emotions = await self.build_emoji_description(emoji_base64)
|
||||||
if not description: # 检查描述是否成功生成或审核通过
|
if not description: # 检查描述是否成功生成或审核通过
|
||||||
logger.warning(f"[注册失败] 未能生成有效描述或审核未通过: {filename}")
|
logger.warning(f"[注册失败] 未能生成有效描述或审核未通过: {filename}")
|
||||||
# 删除未能生成描述的文件
|
# 删除未能生成描述的文件
|
||||||
try:
|
try:
|
||||||
@@ -943,9 +947,9 @@ class EmojiManager:
|
|||||||
replaced = await self.replace_a_emoji(new_emoji)
|
replaced = await self.replace_a_emoji(new_emoji)
|
||||||
if not replaced:
|
if not replaced:
|
||||||
logger.error("[注册失败] 替换表情包失败,无法完成注册")
|
logger.error("[注册失败] 替换表情包失败,无法完成注册")
|
||||||
# 替换失败,删除新表情包文件
|
# 替换失败,删除新表情包文件
|
||||||
try:
|
try:
|
||||||
os.remove(file_full_path) # new_emoji 的 full_path 此时还是源路径
|
os.remove(file_full_path) # new_emoji 的 full_path 此时还是源路径
|
||||||
logger.info(f"[清理] 删除替换失败的新表情文件: {filename}")
|
logger.info(f"[清理] 删除替换失败的新表情文件: {filename}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[错误] 删除替换失败文件时出错: {str(e)}")
|
logger.error(f"[错误] 删除替换失败文件时出错: {str(e)}")
|
||||||
@@ -954,7 +958,7 @@ class EmojiManager:
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
# 直接注册
|
# 直接注册
|
||||||
register_success = await new_emoji.register_to_db() # 此方法会移动文件并更新 DB
|
register_success = await new_emoji.register_to_db() # 此方法会移动文件并更新 DB
|
||||||
if register_success:
|
if register_success:
|
||||||
# 注册成功后,添加到内存列表
|
# 注册成功后,添加到内存列表
|
||||||
self.emoji_objects.append(new_emoji)
|
self.emoji_objects.append(new_emoji)
|
||||||
@@ -963,20 +967,20 @@ class EmojiManager:
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.error(f"[注册失败] 保存表情包到数据库/移动文件失败: {filename}")
|
logger.error(f"[注册失败] 保存表情包到数据库/移动文件失败: {filename}")
|
||||||
# register_to_db 失败时,内部会尝试清理移动后的文件,源文件可能还在
|
# register_to_db 失败时,内部会尝试清理移动后的文件,源文件可能还在
|
||||||
# 是否需要删除源文件?
|
# 是否需要删除源文件?
|
||||||
if os.path.exists(file_full_path):
|
if os.path.exists(file_full_path):
|
||||||
try:
|
try:
|
||||||
os.remove(file_full_path)
|
os.remove(file_full_path)
|
||||||
logger.info(f"[清理] 删除注册失败的源文件: {filename}")
|
logger.info(f"[清理] 删除注册失败的源文件: {filename}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[错误] 删除注册失败源文件时出错: {str(e)}")
|
logger.error(f"[错误] 删除注册失败源文件时出错: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[错误] 注册表情包时发生未预期错误 ({filename}): {str(e)}")
|
logger.error(f"[错误] 注册表情包时发生未预期错误 ({filename}): {str(e)}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
# 尝试删除源文件以避免循环处理
|
# 尝试删除源文件以避免循环处理
|
||||||
if os.path.exists(file_full_path):
|
if os.path.exists(file_full_path):
|
||||||
try:
|
try:
|
||||||
os.remove(file_full_path)
|
os.remove(file_full_path)
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ from src.plugins.models.utils_model import LLMRequest
|
|||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.plugins.chat.utils_image import image_path_to_base64 # Local import needed after move
|
from src.plugins.chat.utils_image import image_path_to_base64 # Local import needed after move
|
||||||
from src.plugins.utils.timer_calculator import Timer # <--- Import Timer
|
from src.plugins.utils.timer_calculator import Timer # <--- Import Timer
|
||||||
from src.do_tool.tool_use import ToolUser
|
|
||||||
from src.plugins.emoji_system.emoji_manager import emoji_manager
|
from src.plugins.emoji_system.emoji_manager import emoji_manager
|
||||||
from src.plugins.utils.json_utils import process_llm_tool_calls, extract_tool_call_arguments
|
|
||||||
from src.heart_flow.sub_mind import SubMind
|
from src.heart_flow.sub_mind import SubMind
|
||||||
from src.heart_flow.observation import Observation
|
from src.heart_flow.observation import Observation
|
||||||
from src.plugins.heartFC_chat.heartflow_prompt_builder import global_prompt_manager, prompt_builder
|
from src.plugins.heartFC_chat.heartflow_prompt_builder import global_prompt_manager, prompt_builder
|
||||||
@@ -788,7 +786,9 @@ class HeartFChatting:
|
|||||||
logger.info(f"{self.log_prefix}[Planner] 连续回复 2 次,80% 概率移除 text_reply 和 emoji_reply (触发)")
|
logger.info(f"{self.log_prefix}[Planner] 连续回复 2 次,80% 概率移除 text_reply 和 emoji_reply (触发)")
|
||||||
actions_to_remove_temporarily.extend(["text_reply", "emoji_reply"])
|
actions_to_remove_temporarily.extend(["text_reply", "emoji_reply"])
|
||||||
else:
|
else:
|
||||||
logger.info(f"{self.log_prefix}[Planner] 连续回复 2 次,80% 概率移除 text_reply 和 emoji_reply (未触发)")
|
logger.info(
|
||||||
|
f"{self.log_prefix}[Planner] 连续回复 2 次,80% 概率移除 text_reply 和 emoji_reply (未触发)"
|
||||||
|
)
|
||||||
elif lian_xu_wen_ben_hui_fu == 1:
|
elif lian_xu_wen_ben_hui_fu == 1:
|
||||||
if probability_roll < 0.4:
|
if probability_roll < 0.4:
|
||||||
logger.info(f"{self.log_prefix}[Planner] 连续回复 1 次,40% 概率移除 text_reply (触发)")
|
logger.info(f"{self.log_prefix}[Planner] 连续回复 1 次,40% 概率移除 text_reply (触发)")
|
||||||
@@ -805,10 +805,10 @@ class HeartFChatting:
|
|||||||
observed_messages_str = observation.talking_message_str_truncate
|
observed_messages_str = observation.talking_message_str_truncate
|
||||||
|
|
||||||
# --- 使用 LLM 进行决策 (JSON 输出模式) --- #
|
# --- 使用 LLM 进行决策 (JSON 输出模式) --- #
|
||||||
action = "no_reply" # 默认动作
|
action = "no_reply" # 默认动作
|
||||||
reasoning = "规划器初始化默认"
|
reasoning = "规划器初始化默认"
|
||||||
emoji_query = ""
|
emoji_query = ""
|
||||||
llm_error = False # LLM 请求或解析错误标志
|
llm_error = False # LLM 请求或解析错误标志
|
||||||
|
|
||||||
# 获取我们将传递给 prompt 构建器和用于验证的当前可用动作
|
# 获取我们将传递给 prompt 构建器和用于验证的当前可用动作
|
||||||
current_available_actions = self.action_manager.get_available_actions()
|
current_available_actions = self.action_manager.get_available_actions()
|
||||||
@@ -833,8 +833,8 @@ class HeartFChatting:
|
|||||||
observed_messages_str,
|
observed_messages_str,
|
||||||
current_mind,
|
current_mind,
|
||||||
self.sub_mind.structured_info,
|
self.sub_mind.structured_info,
|
||||||
"", # replan_prompt_str,
|
"", # replan_prompt_str,
|
||||||
current_available_actions # <--- 传入当前可用动作
|
current_available_actions, # <--- 传入当前可用动作
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- 调用 LLM (普通文本生成) ---
|
# --- 调用 LLM (普通文本生成) ---
|
||||||
@@ -851,8 +851,8 @@ class HeartFChatting:
|
|||||||
reasoning = f"LLM 请求失败: {req_e}"
|
reasoning = f"LLM 请求失败: {req_e}"
|
||||||
llm_error = True
|
llm_error = True
|
||||||
# 直接使用默认动作返回错误结果
|
# 直接使用默认动作返回错误结果
|
||||||
action = "no_reply" # 明确设置为默认值
|
action = "no_reply" # 明确设置为默认值
|
||||||
emoji_query = "" # 明确设置为空
|
emoji_query = "" # 明确设置为空
|
||||||
# 不再立即返回,而是继续执行 finally 块以恢复动作
|
# 不再立即返回,而是继续执行 finally 块以恢复动作
|
||||||
# return { ... }
|
# return { ... }
|
||||||
|
|
||||||
@@ -860,9 +860,11 @@ class HeartFChatting:
|
|||||||
if not llm_error and llm_content:
|
if not llm_error and llm_content:
|
||||||
try:
|
try:
|
||||||
# 尝试去除可能的 markdown 代码块标记
|
# 尝试去除可能的 markdown 代码块标记
|
||||||
cleaned_content = llm_content.strip().removeprefix("```json").removeprefix("```").removesuffix("```").strip()
|
cleaned_content = (
|
||||||
|
llm_content.strip().removeprefix("```json").removeprefix("```").removesuffix("```").strip()
|
||||||
|
)
|
||||||
if not cleaned_content:
|
if not cleaned_content:
|
||||||
raise json.JSONDecodeError("Cleaned content is empty", cleaned_content, 0)
|
raise json.JSONDecodeError("Cleaned content is empty", cleaned_content, 0)
|
||||||
parsed_json = json.loads(cleaned_content)
|
parsed_json = json.loads(cleaned_content)
|
||||||
|
|
||||||
# 提取决策,提供默认值
|
# 提取决策,提供默认值
|
||||||
@@ -881,28 +883,32 @@ class HeartFChatting:
|
|||||||
emoji_query = ""
|
emoji_query = ""
|
||||||
# 检查 no_reply 是否也恰好被移除了 (极端情况)
|
# 检查 no_reply 是否也恰好被移除了 (极端情况)
|
||||||
if "no_reply" not in current_available_actions:
|
if "no_reply" not in current_available_actions:
|
||||||
logger.error(f"{self.log_prefix}[Planner] 严重错误:'no_reply' 动作也不可用!无法执行任何动作。")
|
logger.error(
|
||||||
action = "error" # 回退到错误状态
|
f"{self.log_prefix}[Planner] 严重错误:'no_reply' 动作也不可用!无法执行任何动作。"
|
||||||
reasoning = "无法执行任何有效动作,包括 no_reply"
|
)
|
||||||
llm_error = True # 标记为严重错误
|
action = "error" # 回退到错误状态
|
||||||
|
reasoning = "无法执行任何有效动作,包括 no_reply"
|
||||||
|
llm_error = True # 标记为严重错误
|
||||||
else:
|
else:
|
||||||
llm_error = False # 视为逻辑修正而非 LLM 错误
|
llm_error = False # 视为逻辑修正而非 LLM 错误
|
||||||
else:
|
else:
|
||||||
# 动作有效且可用
|
# 动作有效且可用
|
||||||
action = extracted_action
|
action = extracted_action
|
||||||
reasoning = extracted_reasoning
|
reasoning = extracted_reasoning
|
||||||
emoji_query = extracted_emoji_query
|
emoji_query = extracted_emoji_query
|
||||||
llm_error = False # 解析成功
|
llm_error = False # 解析成功
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{self.log_prefix}[要做什么]\nPrompt:\n{prompt}\n\n决策结果 (来自JSON): {action}, 理由: {reasoning}, 表情查询: '{emoji_query}'"
|
f"{self.log_prefix}[要做什么]\nPrompt:\n{prompt}\n\n决策结果 (来自JSON): {action}, 理由: {reasoning}, 表情查询: '{emoji_query}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
except json.JSONDecodeError as json_e:
|
except json.JSONDecodeError as json_e:
|
||||||
logger.warning(f"{self.log_prefix}[Planner] 解析LLM响应JSON失败: {json_e}. LLM原始输出: '{llm_content}'")
|
logger.warning(
|
||||||
|
f"{self.log_prefix}[Planner] 解析LLM响应JSON失败: {json_e}. LLM原始输出: '{llm_content}'"
|
||||||
|
)
|
||||||
reasoning = f"解析LLM响应JSON失败: {json_e}. 将使用默认动作 'no_reply'."
|
reasoning = f"解析LLM响应JSON失败: {json_e}. 将使用默认动作 'no_reply'."
|
||||||
action = "no_reply" # 解析失败则默认不回复
|
action = "no_reply" # 解析失败则默认不回复
|
||||||
emoji_query = ""
|
emoji_query = ""
|
||||||
llm_error = True # 标记解析错误
|
llm_error = True # 标记解析错误
|
||||||
except Exception as parse_e:
|
except Exception as parse_e:
|
||||||
logger.error(f"{self.log_prefix}[Planner] 处理LLM响应时发生意外错误: {parse_e}")
|
logger.error(f"{self.log_prefix}[Planner] 处理LLM响应时发生意外错误: {parse_e}")
|
||||||
reasoning = f"处理LLM响应时发生意外错误: {parse_e}. 将使用默认动作 'no_reply'."
|
reasoning = f"处理LLM响应时发生意外错误: {parse_e}. 将使用默认动作 'no_reply'."
|
||||||
@@ -910,12 +916,12 @@ class HeartFChatting:
|
|||||||
emoji_query = ""
|
emoji_query = ""
|
||||||
llm_error = True
|
llm_error = True
|
||||||
elif not llm_error and not llm_content:
|
elif not llm_error and not llm_content:
|
||||||
# LLM 请求成功但返回空内容
|
# LLM 请求成功但返回空内容
|
||||||
logger.warning(f"{self.log_prefix}[Planner] LLM 返回了空内容。")
|
logger.warning(f"{self.log_prefix}[Planner] LLM 返回了空内容。")
|
||||||
reasoning = "LLM 返回了空内容,使用默认动作 'no_reply'."
|
reasoning = "LLM 返回了空内容,使用默认动作 'no_reply'."
|
||||||
action = "no_reply"
|
action = "no_reply"
|
||||||
emoji_query = ""
|
emoji_query = ""
|
||||||
llm_error = True # 标记为空响应错误
|
llm_error = True # 标记为空响应错误
|
||||||
|
|
||||||
# 如果 llm_error 在此阶段为 True,意味着请求成功但解析失败或返回空
|
# 如果 llm_error 在此阶段为 True,意味着请求成功但解析失败或返回空
|
||||||
# 如果 llm_error 在请求阶段就为 True,则跳过了此解析块
|
# 如果 llm_error 在请求阶段就为 True,则跳过了此解析块
|
||||||
@@ -923,7 +929,7 @@ class HeartFChatting:
|
|||||||
except Exception as outer_e:
|
except Exception as outer_e:
|
||||||
logger.error(f"{self.log_prefix}[Planner] Planner 处理过程中发生意外错误: {outer_e}")
|
logger.error(f"{self.log_prefix}[Planner] Planner 处理过程中发生意外错误: {outer_e}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
action = "error" # 发生未知错误,标记为 error 动作
|
action = "error" # 发生未知错误,标记为 error 动作
|
||||||
reasoning = f"Planner 内部处理错误: {outer_e}"
|
reasoning = f"Planner 内部处理错误: {outer_e}"
|
||||||
emoji_query = ""
|
emoji_query = ""
|
||||||
llm_error = True
|
llm_error = True
|
||||||
@@ -944,7 +950,7 @@ class HeartFChatting:
|
|||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix}[Planner] 但是麦麦这次不想加表情 ({1 - EMOJI_SEND_PRO:.0%}),忽略表情 '{emoji_query}'"
|
f"{self.log_prefix}[Planner] 但是麦麦这次不想加表情 ({1 - EMOJI_SEND_PRO:.0%}),忽略表情 '{emoji_query}'"
|
||||||
)
|
)
|
||||||
emoji_query = "" # 清空表情请求
|
emoji_query = "" # 清空表情请求
|
||||||
else:
|
else:
|
||||||
logger.info(f"{self.log_prefix}[Planner] 好吧,加上表情 '{emoji_query}'")
|
logger.info(f"{self.log_prefix}[Planner] 好吧,加上表情 '{emoji_query}'")
|
||||||
# --- 结束概率性忽略 ---
|
# --- 结束概率性忽略 ---
|
||||||
@@ -956,7 +962,7 @@ class HeartFChatting:
|
|||||||
"emoji_query": emoji_query,
|
"emoji_query": emoji_query,
|
||||||
"current_mind": current_mind,
|
"current_mind": current_mind,
|
||||||
"observed_messages": observed_messages,
|
"observed_messages": observed_messages,
|
||||||
"llm_error": llm_error, # 返回错误状态
|
"llm_error": llm_error, # 返回错误状态
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _get_anchor_message(self) -> Optional[MessageRecv]:
|
async def _get_anchor_message(self) -> Optional[MessageRecv]:
|
||||||
@@ -1178,8 +1184,8 @@ class HeartFChatting:
|
|||||||
action_options_text = "当前你可以选择的行动有:\n"
|
action_options_text = "当前你可以选择的行动有:\n"
|
||||||
action_keys = list(current_available_actions.keys())
|
action_keys = list(current_available_actions.keys())
|
||||||
for name in action_keys:
|
for name in action_keys:
|
||||||
desc = current_available_actions[name]
|
desc = current_available_actions[name]
|
||||||
action_options_text += f"- '{name}': {desc}\n"
|
action_options_text += f"- '{name}': {desc}\n"
|
||||||
|
|
||||||
# --- 选择一个示例动作键 (用于填充模板中的 {example_action}) ---
|
# --- 选择一个示例动作键 (用于填充模板中的 {example_action}) ---
|
||||||
example_action_key = action_keys[0] if action_keys else "no_reply"
|
example_action_key = action_keys[0] if action_keys else "no_reply"
|
||||||
@@ -1194,10 +1200,10 @@ class HeartFChatting:
|
|||||||
structured_info_block=structured_info_block,
|
structured_info_block=structured_info_block,
|
||||||
chat_content_block=chat_content_block,
|
chat_content_block=chat_content_block,
|
||||||
current_mind_block=current_mind_block,
|
current_mind_block=current_mind_block,
|
||||||
replan="", # 暂时留空 replan 信息
|
replan="", # 暂时留空 replan 信息
|
||||||
cycle_info_block=cycle_info_block,
|
cycle_info_block=cycle_info_block,
|
||||||
action_options_text=action_options_text, # 传入可用动作描述
|
action_options_text=action_options_text, # 传入可用动作描述
|
||||||
example_action=example_action_key # 传入示例动作键
|
example_action=example_action_key, # 传入示例动作键
|
||||||
)
|
)
|
||||||
|
|
||||||
return prompt
|
return prompt
|
||||||
@@ -1205,7 +1211,7 @@ class HeartFChatting:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.log_prefix}[Planner] 构建提示词时出错: {e}")
|
logger.error(f"{self.log_prefix}[Planner] 构建提示词时出错: {e}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
return "[构建 Planner Prompt 时出错]" # 返回错误提示,避免空字符串
|
return "[构建 Planner Prompt 时出错]" # 返回错误提示,避免空字符串
|
||||||
|
|
||||||
# --- 回复器 (Replier) 的定义 --- #
|
# --- 回复器 (Replier) 的定义 --- #
|
||||||
async def _replier_work(
|
async def _replier_work(
|
||||||
|
|||||||
@@ -7,14 +7,13 @@ from src.plugins.utils.chat_message_builder import build_readable_messages, get_
|
|||||||
from src.plugins.person_info.relationship_manager import relationship_manager
|
from src.plugins.person_info.relationship_manager import relationship_manager
|
||||||
from src.plugins.chat.utils import get_embedding
|
from src.plugins.chat.utils import get_embedding
|
||||||
import time
|
import time
|
||||||
from typing import Union, Optional, Dict, Any
|
from typing import Union, Optional
|
||||||
from ...common.database import db
|
from ...common.database import db
|
||||||
from ..chat.utils import get_recent_group_speaker
|
from ..chat.utils import get_recent_group_speaker
|
||||||
from ..moods.moods import MoodManager
|
from ..moods.moods import MoodManager
|
||||||
from ..memory_system.Hippocampus import HippocampusManager
|
from ..memory_system.Hippocampus import HippocampusManager
|
||||||
from ..schedule.schedule_generator import bot_schedule
|
from ..schedule.schedule_generator import bot_schedule
|
||||||
from ..knowledge.knowledge_lib import qa_manager
|
from ..knowledge.knowledge_lib import qa_manager
|
||||||
import traceback
|
|
||||||
|
|
||||||
logger = get_logger("prompt")
|
logger = get_logger("prompt")
|
||||||
|
|
||||||
@@ -50,7 +49,7 @@ def init_prompt():
|
|||||||
|
|
||||||
# Planner提示词 - 修改为要求 JSON 输出
|
# Planner提示词 - 修改为要求 JSON 输出
|
||||||
Prompt(
|
Prompt(
|
||||||
'''你的名字是{bot_name},{prompt_personality},你现在正在一个群聊中。需要基于以下信息决定如何参与对话:
|
"""你的名字是{bot_name},{prompt_personality},你现在正在一个群聊中。需要基于以下信息决定如何参与对话:
|
||||||
{structured_info_block}
|
{structured_info_block}
|
||||||
{chat_content_block}
|
{chat_content_block}
|
||||||
{current_mind_block}
|
{current_mind_block}
|
||||||
@@ -106,7 +105,7 @@ JSON 结构如下,包含三个字段 "action", "reasoning", "emoji_query":
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
请输出你的决策 JSON:
|
请输出你的决策 JSON:
|
||||||
''', # 使用三引号避免内部引号问题
|
""", # 使用三引号避免内部引号问题
|
||||||
"planner_prompt", # 保持名称不变,替换内容
|
"planner_prompt", # 保持名称不变,替换内容
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user