This commit is contained in:
SengokuCola
2025-04-24 23:45:49 +08:00
parent af08ef9b04
commit 3ab3979047
6 changed files with 117 additions and 130 deletions

View File

@@ -405,7 +405,6 @@ class BotConfig:
config.save_emoji = emoji_config.get("save_emoji", config.save_emoji) config.save_emoji = emoji_config.get("save_emoji", config.save_emoji)
config.steal_emoji = emoji_config.get("steal_emoji", config.steal_emoji) config.steal_emoji = emoji_config.get("steal_emoji", config.steal_emoji)
def bot(parent: dict): def bot(parent: dict):
# 机器人基础配置 # 机器人基础配置
bot_config = parent["bot"] bot_config = parent["bot"]

View File

@@ -12,7 +12,6 @@ import re
from ...common.database import db from ...common.database import db
from ...config.config import global_config from ...config.config import global_config
from ..chat.utils import get_embedding
from ..chat.utils_image import image_path_to_base64, image_manager from ..chat.utils_image import image_path_to_base64, image_manager
from ..models.utils_model import LLMRequest from ..models.utils_model import LLMRequest
from src.common.logger import get_module_logger, LogConfig, EMOJI_STYLE_CONFIG from src.common.logger import get_module_logger, LogConfig, EMOJI_STYLE_CONFIG
@@ -31,6 +30,7 @@ EMOJI_REGISTED_DIR = os.path.join("data", "emoji_registed") # 已注册的表
class MaiEmoji: class MaiEmoji:
"""定义一个表情包""" """定义一个表情包"""
def __init__(self, filename: str, path: str): def __init__(self, filename: str, path: str):
self.path = path # 存储目录路径 self.path = path # 存储目录路径
self.filename = filename self.filename = filename
@@ -64,7 +64,6 @@ class MaiEmoji:
logger.error(f"[错误] 无法读取图片: {file_path}") logger.error(f"[错误] 无法读取图片: {file_path}")
return None return None
# 计算哈希值 # 计算哈希值
image_bytes = base64.b64decode(image_base64) image_bytes = base64.b64decode(image_base64)
self.hash = hashlib.md5(image_bytes).hexdigest() self.hash = hashlib.md5(image_bytes).hexdigest()
@@ -72,7 +71,6 @@ class MaiEmoji:
# 获取图片格式 # 获取图片格式
self.format = Image.open(io.BytesIO(image_bytes)).format.lower() self.format = Image.open(io.BytesIO(image_bytes)).format.lower()
except Exception as e: except Exception as e:
logger.error(f"[错误] 初始化表情包失败: {str(e)}") logger.error(f"[错误] 初始化表情包失败: {str(e)}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@@ -110,30 +108,26 @@ class MaiEmoji:
self.path = EMOJI_REGISTED_DIR self.path = EMOJI_REGISTED_DIR
except Exception as move_error: except Exception as move_error:
logger.error(f"[错误] 移动文件失败: {str(move_error)}") logger.error(f"[错误] 移动文件失败: {str(move_error)}")
return False # 文件移动失败,不继续 return False # 文件移动失败,不继续
# --- 数据库操作 --- # --- 数据库操作 ---
try: try:
# 准备数据库记录 for emoji collection # 准备数据库记录 for emoji collection
emoji_record = { emoji_record = {
"filename": self.filename, "filename": self.filename,
"path": os.path.join(self.path, self.filename), # 使用更新后的路径 "path": os.path.join(self.path, self.filename), # 使用更新后的路径
"embedding": self.embedding, "embedding": self.embedding,
"description": self.description, "description": self.description,
"emotion": self.emotion, # 添加情感标签字段 "emotion": self.emotion, # 添加情感标签字段
"hash": self.hash, "hash": self.hash,
"format": self.format, "format": self.format,
"timestamp": int(self.register_time), # 使用实例的注册时间 "timestamp": int(self.register_time), # 使用实例的注册时间
"usage_count": self.usage_count, "usage_count": self.usage_count,
"last_used_time": self.last_used_time "last_used_time": self.last_used_time,
} }
# 使用upsert确保记录存在或被更新 # 使用upsert确保记录存在或被更新
db["emoji"].update_one( db["emoji"].update_one({"hash": self.hash}, {"$set": emoji_record}, upsert=True)
{"hash": self.hash},
{"$set": emoji_record},
upsert=True
)
logger.success(f"[注册] 表情包信息保存到数据库: {self.description}") logger.success(f"[注册] 表情包信息保存到数据库: {self.description}")
return True return True
@@ -188,7 +182,6 @@ class MaiEmoji:
class EmojiManager: class EmojiManager:
_instance = None _instance = None
def __new__(cls): def __new__(cls):
if cls._instance is None: if cls._instance is None:
cls._instance = super().__new__(cls) cls._instance = super().__new__(cls)
@@ -213,7 +206,6 @@ class EmojiManager:
"""确保表情存储目录存在""" """确保表情存储目录存在"""
os.makedirs(EMOJI_DIR, exist_ok=True) os.makedirs(EMOJI_DIR, exist_ok=True)
def initialize(self): def initialize(self):
"""初始化数据库连接和表情目录""" """初始化数据库连接和表情目录"""
if not self._initialized: if not self._initialized:
@@ -314,9 +306,7 @@ class EmojiManager:
# 更新使用次数 # 更新使用次数
db.emoji.update_one({"hash": selected_emoji.hash}, {"$inc": {"usage_count": 1}}) db.emoji.update_one({"hash": selected_emoji.hash}, {"$inc": {"usage_count": 1}})
logger.info( logger.info(f"[匹配] 找到表情包: {selected_emoji.description} (相似度: {similarity:.4f})")
f"[匹配] 找到表情包: {selected_emoji.description} (相似度: {similarity:.4f})"
)
time_end = time.time() time_end = time.time()
logger.info(f"[匹配] 搜索表情包用时: {time_end - time_start:.2f}") logger.info(f"[匹配] 搜索表情包用时: {time_end - time_start:.2f}")
@@ -420,11 +410,14 @@ class EmojiManager:
continue continue
# 检查是否需要处理表情包(数量超过最大值或不足) # 检查是否需要处理表情包(数量超过最大值或不足)
if (self.emoji_num > self.emoji_num_max and global_config.max_reach_deletion) or (self.emoji_num < self.emoji_num_max): if (self.emoji_num > self.emoji_num_max and global_config.max_reach_deletion) or (
self.emoji_num < self.emoji_num_max
):
try: try:
# 获取目录下所有图片文件 # 获取目录下所有图片文件
files_to_process = [ files_to_process = [
f for f in files f
for f in files
if os.path.isfile(os.path.join(EMOJI_DIR, f)) if os.path.isfile(os.path.join(EMOJI_DIR, f))
and f.lower().endswith((".jpg", ".jpeg", ".png", ".gif")) and f.lower().endswith((".jpg", ".jpeg", ".png", ".gif"))
] ]
@@ -540,8 +533,6 @@ class EmojiManager:
return emoji return emoji
return None return None
async def delete_emoji(self, emoji_hash: str) -> bool: async def delete_emoji(self, emoji_hash: str) -> bool:
"""根据哈希值删除表情包 """根据哈希值删除表情包
@@ -596,10 +587,7 @@ class EmojiManager:
time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(emoji.register_time)) time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(emoji.register_time))
# 构建每个表情包的信息字符串 # 构建每个表情包的信息字符串
emoji_info = ( emoji_info = (
f"编号: {i+1}\n" f"编号: {i + 1}\n描述: {emoji.description}\n使用次数: {emoji.usage_count}\n添加时间: {time_str}\n"
f"描述: {emoji.description}\n"
f"使用次数: {emoji.usage_count}\n"
f"添加时间: {time_str}\n"
) )
emoji_info_list.append(emoji_info) emoji_info_list.append(emoji_info)
return emoji_info_list return emoji_info_list
@@ -629,10 +617,10 @@ class EmojiManager:
f"新表情包信息:\n" f"新表情包信息:\n"
f"描述: {new_emoji.description}\n\n" f"描述: {new_emoji.description}\n\n"
f"现有表情包列表:\n" + "\n".join(emoji_info_list) + "\n\n" f"现有表情包列表:\n" + "\n".join(emoji_info_list) + "\n\n"
f"请决定:\n" "请决定:\n"
f"1. 是否要删除某个现有表情包来为新表情包腾出空间?\n" "1. 是否要删除某个现有表情包来为新表情包腾出空间?\n"
f"2. 如果要删除,应该删除哪一个(给出编号)\n" "2. 如果要删除,应该删除哪一个(给出编号)\n"
f"请只回答:'不删除''删除编号X'(X为表情包编号)。" "请只回答:'不删除''删除编号X'(X为表情包编号)。"
) )
# 调用大模型进行决策 # 调用大模型进行决策
@@ -645,7 +633,7 @@ class EmojiManager:
return False return False
# 尝试从决策中提取表情包编号 # 尝试从决策中提取表情包编号
match = re.search(r'删除编号(\d+)', decision) match = re.search(r"删除编号(\d+)", decision)
if match: if match:
emoji_index = int(match.group(1)) - 1 # 转换为0-based索引 emoji_index = int(match.group(1)) - 1 # 转换为0-based索引
@@ -669,10 +657,10 @@ class EmojiManager:
logger.error(f"[错误] 注册表情包到数据库失败: {new_emoji.filename}") logger.error(f"[错误] 注册表情包到数据库失败: {new_emoji.filename}")
return False return False
else: else:
logger.error(f"[错误] 删除表情包失败,无法完成替换") logger.error("[错误] 删除表情包失败,无法完成替换")
return False return False
else: else:
logger.error(f"[错误] 无效的表情包编号: {emoji_index+1}") logger.error(f"[错误] 无效的表情包编号: {emoji_index + 1}")
else: else:
logger.error(f"[错误] 无法从决策中提取表情包编号: {decision}") logger.error(f"[错误] 无法从决策中提取表情包编号: {decision}")
@@ -721,17 +709,17 @@ class EmojiManager:
return None, [] return None, []
# 分析情感含义 # 分析情感含义
emotion_prompt = f''' emotion_prompt = f"""
基于这个表情包的描述:'{description}'请列出1-3个可能的情感标签每个标签用一个词组表示格式如下 基于这个表情包的描述:'{description}'请列出1-3个可能的情感标签每个标签用一个词组表示格式如下
幽默的讽刺 幽默的讽刺
悲伤的无奈 悲伤的无奈
愤怒的抗议 愤怒的抗议
愤怒的讽刺 愤怒的讽刺
直接输出词组,词组检用逗号分隔。''' 直接输出词组,词组检用逗号分隔。"""
emotions_text, _ = await self.llm_emotion_judge.generate_response_async(emotion_prompt, temperature=0.7) emotions_text, _ = await self.llm_emotion_judge.generate_response_async(emotion_prompt, temperature=0.7)
# 处理情感列表 # 处理情感列表
emotions = [e.strip() for e in emotions_text.split(',') if e.strip()] emotions = [e.strip() for e in emotions_text.split(",") if e.strip()]
return f"[表情包:{description}]", emotions return f"[表情包:{description}]", emotions
@@ -739,7 +727,6 @@ class EmojiManager:
logger.error(f"获取表情包描述失败: {str(e)}") logger.error(f"获取表情包描述失败: {str(e)}")
return "", [] return "", []
async def register_emoji_by_filename(self, filename: str) -> bool: async def register_emoji_by_filename(self, filename: str) -> bool:
"""读取指定文件名的表情包图片,分析并注册到数据库 """读取指定文件名的表情包图片,分析并注册到数据库

View File

@@ -78,7 +78,7 @@ class HeartFCGenerator:
) -> str: ) -> str:
info_catcher = info_catcher_manager.get_info_catcher(thinking_id) info_catcher = info_catcher_manager.get_info_catcher(thinking_id)
with Timer() as t_build_prompt: with Timer() as _t_build_prompt:
prompt = await prompt_builder.build_prompt( prompt = await prompt_builder.build_prompt(
build_mode="focus", build_mode="focus",
reason=reason, reason=reason,

View File

@@ -122,13 +122,14 @@ class PromptBuilder:
elif build_mode == "focus": elif build_mode == "focus":
return await self._build_prompt_focus( return await self._build_prompt_focus(
reason, current_mind_info, structured_info, chat_stream, reason,
current_mind_info,
structured_info,
chat_stream,
) )
return None return None
async def _build_prompt_focus( async def _build_prompt_focus(self, reason, current_mind_info, structured_info, chat_stream) -> tuple[str, str]:
self, reason, current_mind_info, structured_info, chat_stream
) -> tuple[str, str]:
individuality = Individuality.get_instance() individuality = Individuality.get_instance()
prompt_personality = individuality.get_prompt(type="personality", x_person=2, level=1) prompt_personality = individuality.get_prompt(type="personality", x_person=2, level=1)
prompt_identity = individuality.get_prompt(type="identity", x_person=2, level=1) prompt_identity = individuality.get_prompt(type="identity", x_person=2, level=1)