Merge branch 'dev' into 'plugin'

This commit is contained in:
UnCLAS-Prommer
2025-07-11 01:04:20 +08:00
3 changed files with 114 additions and 29 deletions

2
.gitignore vendored
View File

@@ -317,4 +317,4 @@ run_pet.bat
!/plugins/take_picture_plugin
config.toml
备忘录.txt
interested_rates.txt

View File

@@ -8,7 +8,7 @@
count = emoji_api.get_count()
"""
from typing import Optional, Tuple
from typing import Optional, Tuple, List
from src.common.logger import get_logger
from src.chat.emoji_system.emoji_manager import get_emoji_manager
from src.chat.utils.utils_image import image_path_to_base64
@@ -55,14 +55,20 @@ async def get_by_description(description: str) -> Optional[Tuple[str, str, str]]
return None
async def get_random() -> Optional[Tuple[str, str, str]]:
"""随机获取表情包
async def get_random(count: int = 1) -> Optional[List[Tuple[str, str, str]]]:
"""随机获取指定数量的表情包
Args:
count: 要获取的表情包数量默认为1
Returns:
Optional[Tuple[str, str, str]]: (base64编码, 表情包描述, 随机情感标签)None
Optional[List[Tuple[str, str, str]]]: 包含(base64编码, 表情包描述, 随机情感标签)的元组列表,如果失败则为None
"""
if count <= 0:
return []
try:
logger.info("[EmojiAPI] 随机获取表情包")
logger.info(f"[EmojiAPI] 随机获取 {count}表情包")
emoji_manager = get_emoji_manager()
all_emojis = emoji_manager.emoji_objects
@@ -77,23 +83,37 @@ async def get_random() -> Optional[Tuple[str, str, str]]:
logger.warning("[EmojiAPI] 没有有效的表情包")
return None
if len(valid_emojis) < count:
logger.warning(
f"[EmojiAPI] 有效表情包数量 ({len(valid_emojis)}) 少于请求的数量 ({count}),将返回所有有效表情包"
)
count = len(valid_emojis)
# 随机选择
import random
selected_emoji = random.choice(valid_emojis)
selected_emojis = random.sample(valid_emojis, count)
results = []
for selected_emoji in selected_emojis:
emoji_base64 = image_path_to_base64(selected_emoji.full_path)
if not emoji_base64:
logger.error(f"[EmojiAPI] 无法转换表情包为base64: {selected_emoji.full_path}")
return None
continue
matched_emotion = random.choice(selected_emoji.emotion) if selected_emoji.emotion else "随机表情"
# 记录使用次数
emoji_manager.record_usage(selected_emoji.hash)
results.append((emoji_base64, selected_emoji.description, matched_emotion))
logger.info(f"[EmojiAPI] 成功获取随机表情包: {selected_emoji.description}")
return emoji_base64, selected_emoji.description, matched_emotion
if not results and count > 0:
logger.warning("[EmojiAPI] 随机获取表情包失败,没有一个可以成功处理")
return None
logger.info(f"[EmojiAPI] 成功获取 {len(results)} 个随机表情包")
return results
except Exception as e:
logger.error(f"[EmojiAPI] 获取随机表情包失败: {e}")

View File

@@ -1,3 +1,4 @@
import random
from typing import Tuple
# 导入新插件系统
@@ -7,7 +8,7 @@ from src.plugin_system import BaseAction, ActionActivationType, ChatMode
from src.common.logger import get_logger
# 导入API模块 - 标准Python包方式
from src.plugin_system.apis import emoji_api
from src.plugin_system.apis import emoji_api, llm_api, message_api
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
@@ -39,7 +40,7 @@ class EmojiAction(BaseAction):
"""
# 动作参数定义
action_parameters = {"description": "文字描述你想要发送的表情包内容"}
action_parameters = {"reason": "文字描述你想要发送的表情包原因"}
# 动作使用场景
action_require = [
@@ -56,18 +57,82 @@ class EmojiAction(BaseAction):
logger.info(f"{self.log_prefix} 决定发送表情")
try:
# 1. 根据描述选择表情包
description = self.action_data.get("description", "")
emoji_result = await emoji_api.get_by_description(description)
# 1. 获取发送表情的原因
reason = self.action_data.get("reason", "表达当前情绪")
logger.info(f"{self.log_prefix} 发送表情原因: {reason}")
if not emoji_result:
logger.warning(f"{self.log_prefix} 未找到匹配描述 '{description}' 的表情包")
return False, f"未找到匹配 '{description}' 的表情包"
# 2. 随机获取20个表情包
sampled_emojis = await emoji_api.get_random(30)
if not sampled_emojis:
logger.warning(f"{self.log_prefix} 无法获取随机表情包")
return False, "无法获取随机表情包"
emoji_base64, emoji_description, matched_emotion = emoji_result
logger.info(f"{self.log_prefix} 找到表达{matched_emotion}的表情包")
# 3. 准备情感数据
emotion_map = {}
for b64, desc, emo in sampled_emojis:
if emo not in emotion_map:
emotion_map[emo] = []
emotion_map[emo].append((b64, desc))
# 使用BaseAction的便捷方法发送表情包
available_emotions = list(emotion_map.keys())
if not available_emotions:
logger.warning(f"{self.log_prefix} 获取到的表情包均无情感标签, 将随机发送")
emoji_base64, emoji_description, _ = random.choice(sampled_emojis)
else:
# 获取最近的5条消息内容用于判断
recent_messages = message_api.get_recent_messages(chat_id=self.chat_id, limit=5)
messages_text = ""
if recent_messages:
# 使用message_api构建可读的消息字符串
messages_text = message_api.build_readable_messages(
messages=recent_messages,
timestamp_mode="normal_no_YMD",
truncate=False,
show_actions=False,
)
# 4. 构建prompt让LLM选择情感
prompt = f"""
你是一个正在进行聊天的网友,你需要根据一个理由和最近的聊天记录,从一个情感标签列表中选择最匹配的一个。
这是最近的聊天记录:
{messages_text}
这是理由:“{reason}
这里是可用的情感标签:{available_emotions}
请直接返回最匹配的那个情感标签,不要进行任何解释或添加其他多余的文字。
"""
logger.info(f"{self.log_prefix} 生成的LLM Prompt: {prompt}")
# 5. 调用LLM
models = llm_api.get_available_models()
chat_model_config = getattr(models, "utils_small", None) # 默认使用chat模型
if not chat_model_config:
logger.error(f"{self.log_prefix} 未找到'chat'模型配置无法调用LLM")
return False, "未找到'chat'模型配置"
success, chosen_emotion, _, _ = await llm_api.generate_with_model(
prompt, model_config=chat_model_config, request_type="emoji"
)
if not success:
logger.error(f"{self.log_prefix} LLM调用失败: {chosen_emotion}")
return False, f"LLM调用失败: {chosen_emotion}"
chosen_emotion = chosen_emotion.strip().replace('"', "").replace("'", "")
logger.info(f"{self.log_prefix} LLM选择的情感: {chosen_emotion}")
# 6. 根据选择的情感匹配表情包
if chosen_emotion in emotion_map:
emoji_base64, emoji_description = random.choice(emotion_map[chosen_emotion])
logger.info(f"{self.log_prefix} 找到匹配情感 '{chosen_emotion}' 的表情包: {emoji_description}")
else:
logger.warning(
f"{self.log_prefix} LLM选择的情感 '{chosen_emotion}' 不在可用列表中, 将随机选择一个表情包"
)
emoji_base64, emoji_description, _ = random.choice(sampled_emojis)
# 7. 发送表情包
success = await self.send_emoji(emoji_base64)
if not success:
@@ -80,5 +145,5 @@ class EmojiAction(BaseAction):
return True, f"发送表情包: {emoji_description}"
except Exception as e:
logger.error(f"{self.log_prefix} 表情动作执行失败: {e}")
logger.error(f"{self.log_prefix} 表情动作执行失败: {e}", exc_info=True)
return False, f"表情发送失败: {str(e)}"