fix:更新s4u表情系统
This commit is contained in:
@@ -1,211 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
import time
|
|
||||||
from typing import Optional
|
|
||||||
from src.config.config import global_config
|
|
||||||
from src.common.logger import get_logger
|
|
||||||
from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager
|
|
||||||
from src.chat.planner_actions.action_manager import ActionManager
|
|
||||||
from src.person_info.relationship_builder_manager import relationship_builder_manager
|
|
||||||
from .priority_manager import PriorityManager
|
|
||||||
import traceback
|
|
||||||
from src.chat.planner_actions.planner import ActionPlanner
|
|
||||||
from src.chat.planner_actions.action_modifier import ActionModifier
|
|
||||||
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat_inclusive
|
|
||||||
|
|
||||||
from src.chat.utils.utils import get_chat_type_and_target_info
|
|
||||||
|
|
||||||
logger = get_logger("normal_chat")
|
|
||||||
|
|
||||||
LOOP_INTERVAL = 0.3
|
|
||||||
|
|
||||||
class NormalChat:
|
|
||||||
"""
|
|
||||||
普通聊天处理类,负责处理非核心对话的聊天逻辑。
|
|
||||||
每个聊天(私聊或群聊)都会有一个独立的NormalChat实例。
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
chat_stream: ChatStream,
|
|
||||||
on_switch_to_focus_callback=None,
|
|
||||||
get_cooldown_progress_callback=None,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
初始化NormalChat实例。
|
|
||||||
|
|
||||||
Args:
|
|
||||||
chat_stream (ChatStream): 聊天流对象,包含与特定聊天相关的所有信息。
|
|
||||||
"""
|
|
||||||
self.chat_stream = chat_stream
|
|
||||||
self.stream_id = chat_stream.stream_id
|
|
||||||
self.last_read_time = time.time()-1
|
|
||||||
|
|
||||||
self.stream_name = get_chat_manager().get_stream_name(self.stream_id) or self.stream_id
|
|
||||||
|
|
||||||
self.relationship_builder = relationship_builder_manager.get_or_create_builder(self.stream_id)
|
|
||||||
|
|
||||||
self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.stream_id)
|
|
||||||
|
|
||||||
self.start_time = time.time()
|
|
||||||
|
|
||||||
# self.mood_manager = mood_manager
|
|
||||||
self.start_time = time.time()
|
|
||||||
|
|
||||||
self.running = False
|
|
||||||
|
|
||||||
self._initialized = False # Track initialization status
|
|
||||||
|
|
||||||
# Planner相关初始化
|
|
||||||
self.action_manager = ActionManager()
|
|
||||||
self.planner = ActionPlanner(self.stream_id, self.action_manager, mode="normal")
|
|
||||||
self.action_modifier = ActionModifier(self.action_manager, self.stream_id)
|
|
||||||
self.enable_planner = global_config.normal_chat.enable_planner # 从配置中读取是否启用planner
|
|
||||||
|
|
||||||
# 记录最近的回复内容,每项包含: {time, user_message, response, is_mentioned, is_reference_reply}
|
|
||||||
self.recent_replies = []
|
|
||||||
self.max_replies_history = 20 # 最多保存最近20条回复记录
|
|
||||||
|
|
||||||
# 添加回调函数,用于在满足条件时通知切换到focus_chat模式
|
|
||||||
self.on_switch_to_focus_callback = on_switch_to_focus_callback
|
|
||||||
|
|
||||||
# 添加回调函数,用于获取冷却进度
|
|
||||||
self.get_cooldown_progress_callback = get_cooldown_progress_callback
|
|
||||||
|
|
||||||
self._disabled = False # 增加停用标志
|
|
||||||
|
|
||||||
self.timeout_count = 0
|
|
||||||
|
|
||||||
self.action_type: Optional[str] = None # 当前动作类型
|
|
||||||
self.is_parallel_action: bool = False # 是否是可并行动作
|
|
||||||
|
|
||||||
# 任务管理
|
|
||||||
self._chat_task: Optional[asyncio.Task] = None
|
|
||||||
self._priority_chat_task: Optional[asyncio.Task] = None # for priority mode consumer
|
|
||||||
self._disabled = False # 停用标志
|
|
||||||
|
|
||||||
# 新增:回复模式和优先级管理器
|
|
||||||
self.reply_mode = self.chat_stream.context.get_priority_mode()
|
|
||||||
if self.reply_mode == "priority":
|
|
||||||
self.priority_manager = PriorityManager(
|
|
||||||
normal_queue_max_size=5,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.priority_manager = None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# async def _interest_mode_loopbody(self):
|
|
||||||
# try:
|
|
||||||
# await asyncio.sleep(LOOP_INTERVAL)
|
|
||||||
|
|
||||||
# if self._disabled:
|
|
||||||
# return False
|
|
||||||
|
|
||||||
# now = time.time()
|
|
||||||
# new_messages_data = get_raw_msg_by_timestamp_with_chat_inclusive(
|
|
||||||
# chat_id=self.stream_id, timestamp_start=self.last_read_time, timestamp_end=now, limit_mode="earliest"
|
|
||||||
# )
|
|
||||||
|
|
||||||
# if new_messages_data:
|
|
||||||
# self.last_read_time = now
|
|
||||||
|
|
||||||
# for msg_data in new_messages_data:
|
|
||||||
# try:
|
|
||||||
# self.adjust_reply_frequency()
|
|
||||||
# await self.normal_response(
|
|
||||||
# message_data=msg_data,
|
|
||||||
# is_mentioned=msg_data.get("is_mentioned", False),
|
|
||||||
# interested_rate=msg_data.get("interest_rate", 0.0) * self.willing_amplifier,
|
|
||||||
# )
|
|
||||||
# return True
|
|
||||||
# except Exception as e:
|
|
||||||
# logger.error(f"[{self.stream_name}] 处理消息时出错: {e} {traceback.format_exc()}")
|
|
||||||
|
|
||||||
|
|
||||||
# except asyncio.CancelledError:
|
|
||||||
# logger.info(f"[{self.stream_name}] 兴趣模式轮询任务被取消")
|
|
||||||
# return False
|
|
||||||
# except Exception:
|
|
||||||
# logger.error(f"[{self.stream_name}] 兴趣模式轮询循环出现错误: {traceback.format_exc()}", exc_info=True)
|
|
||||||
# await asyncio.sleep(10)
|
|
||||||
|
|
||||||
async def _priority_mode_loopbody(self):
|
|
||||||
try:
|
|
||||||
await asyncio.sleep(LOOP_INTERVAL)
|
|
||||||
|
|
||||||
if self._disabled:
|
|
||||||
return False
|
|
||||||
|
|
||||||
now = time.time()
|
|
||||||
new_messages_data = get_raw_msg_by_timestamp_with_chat_inclusive(
|
|
||||||
chat_id=self.stream_id, timestamp_start=self.last_read_time, timestamp_end=now, limit_mode="earliest"
|
|
||||||
)
|
|
||||||
|
|
||||||
if new_messages_data:
|
|
||||||
self.last_read_time = now
|
|
||||||
|
|
||||||
for msg_data in new_messages_data:
|
|
||||||
try:
|
|
||||||
if self.priority_manager:
|
|
||||||
self.priority_manager.add_message(msg_data, msg_data.get("interest_rate", 0.0))
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[{self.stream_name}] 添加消息到优先级队列时出错: {e} {traceback.format_exc()}")
|
|
||||||
|
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
|
||||||
logger.info(f"[{self.stream_name}] 优先级消息生产者任务被取消")
|
|
||||||
return False
|
|
||||||
except Exception:
|
|
||||||
logger.error(f"[{self.stream_name}] 优先级消息生产者循环出现错误: {traceback.format_exc()}", exc_info=True)
|
|
||||||
await asyncio.sleep(10)
|
|
||||||
|
|
||||||
# async def _interest_message_polling_loop(self):
|
|
||||||
# """
|
|
||||||
# [Interest Mode] 通过轮询数据库获取新消息并直接处理。
|
|
||||||
# """
|
|
||||||
# logger.info(f"[{self.stream_name}] 兴趣模式消息轮询任务开始")
|
|
||||||
# try:
|
|
||||||
# while not self._disabled:
|
|
||||||
# success = await self._interest_mode_loopbody()
|
|
||||||
|
|
||||||
# if not success:
|
|
||||||
# break
|
|
||||||
|
|
||||||
# except asyncio.CancelledError:
|
|
||||||
# logger.info(f"[{self.stream_name}] 兴趣模式消息轮询任务被优雅地取消了")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def _priority_chat_loop(self):
|
|
||||||
"""
|
|
||||||
使用优先级队列的消息处理循环。
|
|
||||||
"""
|
|
||||||
while not self._disabled:
|
|
||||||
try:
|
|
||||||
if self.priority_manager and not self.priority_manager.is_empty():
|
|
||||||
# 获取最高优先级的消息,现在是字典
|
|
||||||
message_data = self.priority_manager.get_highest_priority_message()
|
|
||||||
|
|
||||||
if message_data:
|
|
||||||
logger.info(
|
|
||||||
f"[{self.stream_name}] 从队列中取出消息进行处理: User {message_data.get('user_id')}, Time: {time.strftime('%H:%M:%S', time.localtime(message_data.get('time')))}"
|
|
||||||
)
|
|
||||||
|
|
||||||
do_reply = await self.reply_one_message(message_data)
|
|
||||||
response_set = do_reply if do_reply else []
|
|
||||||
factor = 0.5
|
|
||||||
cnt = sum([len(r) for r in response_set])
|
|
||||||
await asyncio.sleep(max(1, factor * cnt - 3)) # 等待tts
|
|
||||||
|
|
||||||
# 等待一段时间再检查队列
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
|
||||||
logger.info(f"[{self.stream_name}] 优先级聊天循环被取消。")
|
|
||||||
break
|
|
||||||
except Exception:
|
|
||||||
logger.error(f"[{self.stream_name}] 优先级聊天循环出现错误: {traceback.format_exc()}", exc_info=True)
|
|
||||||
# 出现错误时,等待更长时间避免频繁报错
|
|
||||||
await asyncio.sleep(10)
|
|
||||||
@@ -171,7 +171,7 @@ class S4UChat:
|
|||||||
|
|
||||||
def _get_priority_info(self, message: MessageRecv) -> dict:
|
def _get_priority_info(self, message: MessageRecv) -> dict:
|
||||||
"""安全地从消息中提取和解析 priority_info"""
|
"""安全地从消息中提取和解析 priority_info"""
|
||||||
priority_info_raw = message.raw.get("priority_info")
|
priority_info_raw = message.priority_info
|
||||||
priority_info = {}
|
priority_info = {}
|
||||||
if isinstance(priority_info_raw, str):
|
if isinstance(priority_info_raw, str):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -11,21 +11,50 @@ from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
|||||||
from src.manager.async_task_manager import AsyncTask, async_task_manager
|
from src.manager.async_task_manager import AsyncTask, async_task_manager
|
||||||
from src.plugin_system.apis import send_api
|
from src.plugin_system.apis import send_api
|
||||||
|
|
||||||
|
"""
|
||||||
|
面部表情系统使用说明:
|
||||||
|
|
||||||
|
1. 预定义的面部表情:
|
||||||
|
- happy: 高兴表情(眼睛微笑 + 眉毛微笑 + 嘴巴大笑)
|
||||||
|
- very_happy: 非常高兴(高兴表情 + 脸红)
|
||||||
|
- sad: 悲伤表情(眼睛哭泣 + 眉毛忧伤 + 嘴巴悲伤)
|
||||||
|
- angry: 生气表情(眉毛生气 + 嘴巴生气)
|
||||||
|
- fear: 恐惧表情(眼睛闭上)
|
||||||
|
- shy: 害羞表情(嘴巴嘟起 + 脸红)
|
||||||
|
- neutral: 中性表情(无表情)
|
||||||
|
|
||||||
|
2. 使用方法:
|
||||||
|
# 获取面部表情管理器
|
||||||
|
facial_expression = mood_manager.get_facial_expression_by_chat_id(chat_id)
|
||||||
|
|
||||||
|
# 发送指定表情
|
||||||
|
await facial_expression.send_expression("happy")
|
||||||
|
|
||||||
|
# 根据情绪值自动选择表情
|
||||||
|
await facial_expression.send_expression_by_mood(mood_values)
|
||||||
|
|
||||||
|
# 重置为中性表情
|
||||||
|
await facial_expression.reset_expression()
|
||||||
|
|
||||||
|
3. 自动表情系统:
|
||||||
|
- 当情绪值更新时,系统会自动根据mood_values选择合适的面部表情
|
||||||
|
- 只有当新表情与当前表情不同时才会发送,避免重复发送
|
||||||
|
- 支持joy >= 8时显示very_happy,joy >= 6时显示happy等梯度表情
|
||||||
|
|
||||||
|
4. amadus表情更新系统:
|
||||||
|
- 每1秒检查一次表情是否有变化,如有变化则发送到amadus
|
||||||
|
- 每次mood更新后立即发送表情更新
|
||||||
|
- 发送消息类型为"amadus_expression_update",格式为{"action": "表情名", "data": 1.0}
|
||||||
|
|
||||||
|
5. 表情选择逻辑:
|
||||||
|
- 系统会找出最强的情绪(joy, anger, sorrow, fear)
|
||||||
|
- 根据情绪强度选择相应的表情组合
|
||||||
|
- 默认情况下返回neutral表情
|
||||||
|
"""
|
||||||
|
|
||||||
logger = get_logger("mood")
|
logger = get_logger("mood")
|
||||||
|
|
||||||
|
|
||||||
async def send_joy_action(chat_id: str):
|
|
||||||
action_content = {"action": "Joy_eye", "data": 1.0}
|
|
||||||
await send_api.custom_to_stream(message_type="face_emotion", content=action_content, stream_id=chat_id)
|
|
||||||
logger.info(f"[{chat_id}] 已发送 Joy 动作: {action_content}")
|
|
||||||
|
|
||||||
await asyncio.sleep(5.0)
|
|
||||||
|
|
||||||
end_action_content = {"action": "Joy_eye", "data": 0.0}
|
|
||||||
await send_api.custom_to_stream(message_type="face_emotion", content=end_action_content, stream_id=chat_id)
|
|
||||||
logger.info(f"[{chat_id}] 已发送 Joy 结束动作: {end_action_content}")
|
|
||||||
|
|
||||||
|
|
||||||
def init_prompt():
|
def init_prompt():
|
||||||
Prompt(
|
Prompt(
|
||||||
"""
|
"""
|
||||||
@@ -64,13 +93,12 @@ def init_prompt():
|
|||||||
喜(Joy): {joy}
|
喜(Joy): {joy}
|
||||||
怒(Anger): {anger}
|
怒(Anger): {anger}
|
||||||
哀(Sorrow): {sorrow}
|
哀(Sorrow): {sorrow}
|
||||||
乐(Pleasure): {pleasure}
|
|
||||||
惧(Fear): {fear}
|
惧(Fear): {fear}
|
||||||
|
|
||||||
现在,发送了消息,引起了你的注意,你对其进行了阅读和思考。请基于对话内容,评估你新的情绪状态。
|
现在,发送了消息,引起了你的注意,你对其进行了阅读和思考。请基于对话内容,评估你新的情绪状态。
|
||||||
请以JSON格式输出你新的情绪状态,包含“喜怒哀乐惧”五个维度,每个维度的取值范围为1-10。
|
请以JSON格式输出你新的情绪状态,包含"喜怒哀惧"四个维度,每个维度的取值范围为1-10。
|
||||||
键值请使用英文: "joy", "anger", "sorrow", "pleasure", "fear".
|
键值请使用英文: "joy", "anger", "sorrow", "fear".
|
||||||
例如: {{"joy": 5, "anger": 1, "sorrow": 1, "pleasure": 5, "fear": 1}}
|
例如: {{"joy": 5, "anger": 1, "sorrow": 1, "fear": 1}}
|
||||||
不要输出任何其他内容,只输出JSON。
|
不要输出任何其他内容,只输出JSON。
|
||||||
""",
|
""",
|
||||||
"change_mood_numerical_prompt",
|
"change_mood_numerical_prompt",
|
||||||
@@ -86,24 +114,175 @@ def init_prompt():
|
|||||||
喜(Joy): {joy}
|
喜(Joy): {joy}
|
||||||
怒(Anger): {anger}
|
怒(Anger): {anger}
|
||||||
哀(Sorrow): {sorrow}
|
哀(Sorrow): {sorrow}
|
||||||
乐(Pleasure): {pleasure}
|
|
||||||
惧(Fear): {fear}
|
惧(Fear): {fear}
|
||||||
|
|
||||||
距离你上次关注直播间消息已经过去了一段时间,你冷静了下来。请基于此,评估你现在的情绪状态。
|
距离你上次关注直播间消息已经过去了一段时间,你冷静了下来。请基于此,评估你现在的情绪状态。
|
||||||
请以JSON格式输出你新的情绪状态,包含“喜怒哀乐惧”五个维度,每个维度的取值范围为1-10。
|
请以JSON格式输出你新的情绪状态,包含"喜怒哀惧"四个维度,每个维度的取值范围为1-10。
|
||||||
键值请使用英文: "joy", "anger", "sorrow", "pleasure", "fear".
|
键值请使用英文: "joy", "anger", "sorrow", "fear".
|
||||||
例如: {{"joy": 5, "anger": 1, "sorrow": 1, "pleasure": 5, "fear": 1}}
|
例如: {{"joy": 5, "anger": 1, "sorrow": 1, "fear": 1}}
|
||||||
不要输出任何其他内容,只输出JSON。
|
不要输出任何其他内容,只输出JSON。
|
||||||
""",
|
""",
|
||||||
"regress_mood_numerical_prompt",
|
"regress_mood_numerical_prompt",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FacialExpression:
|
||||||
|
def __init__(self, chat_id: str):
|
||||||
|
self.chat_id: str = chat_id
|
||||||
|
|
||||||
|
# 预定义面部表情动作
|
||||||
|
self.expressions = {
|
||||||
|
# 眼睛表情
|
||||||
|
"eye_smile": {"action": "eye_smile", "data": 1.0},
|
||||||
|
"eye_cry": {"action": "eye_cry", "data": 1.0},
|
||||||
|
"eye_close": {"action": "eye_close", "data": 1.0},
|
||||||
|
"eye_normal": {"action": "eye_normal", "data": 1.0},
|
||||||
|
|
||||||
|
# 眉毛表情
|
||||||
|
"eyebrow_smile": {"action": "eyebrow_smile", "data": 1.0},
|
||||||
|
"eyebrow_angry": {"action": "eyebrow_angry", "data": 1.0},
|
||||||
|
"eyebrow_sad": {"action": "eyebrow_sad", "data": 1.0},
|
||||||
|
"eyebrow_normal": {"action": "eyebrow_normal", "data": 1.0},
|
||||||
|
|
||||||
|
# 嘴巴表情
|
||||||
|
"mouth_sad": {"action": "mouth_sad", "data": 1.0},
|
||||||
|
"mouth_angry": {"action": "mouth_angry", "data": 1.0},
|
||||||
|
"mouth_laugh": {"action": "mouth_laugh", "data": 1.0},
|
||||||
|
"mouth_pout": {"action": "mouth_pout", "data": 1.0},
|
||||||
|
"mouth_normal": {"action": "mouth_normal", "data": 1.0},
|
||||||
|
|
||||||
|
# 脸部表情
|
||||||
|
"face_blush": {"action": "face_blush", "data": 1.0},
|
||||||
|
"face_normal": {"action": "face_normal", "data": 1.0},
|
||||||
|
}
|
||||||
|
|
||||||
|
# 表情组合模板
|
||||||
|
self.expression_combinations = {
|
||||||
|
"happy": {
|
||||||
|
"eye": "eye_smile",
|
||||||
|
"eyebrow": "eyebrow_smile",
|
||||||
|
"mouth": "mouth_laugh",
|
||||||
|
"face": "face_normal"
|
||||||
|
},
|
||||||
|
"very_happy": {
|
||||||
|
"eye": "eye_smile",
|
||||||
|
"eyebrow": "eyebrow_smile",
|
||||||
|
"mouth": "mouth_laugh",
|
||||||
|
"face": "face_blush"
|
||||||
|
},
|
||||||
|
"sad": {
|
||||||
|
"eye": "eye_cry",
|
||||||
|
"eyebrow": "eyebrow_sad",
|
||||||
|
"mouth": "mouth_sad",
|
||||||
|
"face": "face_normal"
|
||||||
|
},
|
||||||
|
"angry": {
|
||||||
|
"eye": "eye_normal",
|
||||||
|
"eyebrow": "eyebrow_angry",
|
||||||
|
"mouth": "mouth_angry",
|
||||||
|
"face": "face_normal"
|
||||||
|
},
|
||||||
|
"fear": {
|
||||||
|
"eye": "eye_close",
|
||||||
|
"eyebrow": "eyebrow_normal",
|
||||||
|
"mouth": "mouth_normal",
|
||||||
|
"face": "face_normal"
|
||||||
|
},
|
||||||
|
"shy": {
|
||||||
|
"eye": "eye_normal",
|
||||||
|
"eyebrow": "eyebrow_normal",
|
||||||
|
"mouth": "mouth_pout",
|
||||||
|
"face": "face_blush"
|
||||||
|
},
|
||||||
|
"neutral": {
|
||||||
|
"eye": "eye_normal",
|
||||||
|
"eyebrow": "eyebrow_normal",
|
||||||
|
"mouth": "mouth_normal",
|
||||||
|
"face": "face_normal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def select_expression_by_mood(self, mood_values: dict[str, int]) -> str:
|
||||||
|
"""根据情绪值选择合适的表情组合"""
|
||||||
|
joy = mood_values.get("joy", 5)
|
||||||
|
anger = mood_values.get("anger", 1)
|
||||||
|
sorrow = mood_values.get("sorrow", 1)
|
||||||
|
fear = mood_values.get("fear", 1)
|
||||||
|
|
||||||
|
# 找出最强的情绪
|
||||||
|
emotions = {
|
||||||
|
"joy": joy,
|
||||||
|
"anger": anger,
|
||||||
|
"sorrow": sorrow,
|
||||||
|
"fear": fear
|
||||||
|
}
|
||||||
|
|
||||||
|
# 获取最强情绪
|
||||||
|
dominant_emotion = max(emotions, key=emotions.get)
|
||||||
|
dominant_value = emotions[dominant_emotion]
|
||||||
|
|
||||||
|
# 根据情绪强度和类型选择表情
|
||||||
|
if dominant_emotion == "joy":
|
||||||
|
if joy >= 8:
|
||||||
|
return "very_happy"
|
||||||
|
elif joy >= 6:
|
||||||
|
return "happy"
|
||||||
|
elif joy >= 4:
|
||||||
|
return "shy"
|
||||||
|
else:
|
||||||
|
return "neutral"
|
||||||
|
elif dominant_emotion == "anger" and anger >= 6:
|
||||||
|
return "angry"
|
||||||
|
elif dominant_emotion == "sorrow" and sorrow >= 6:
|
||||||
|
return "sad"
|
||||||
|
elif dominant_emotion == "fear" and fear >= 6:
|
||||||
|
return "fear"
|
||||||
|
else:
|
||||||
|
return "neutral"
|
||||||
|
|
||||||
|
async def send_expression(self, expression_name: str):
|
||||||
|
"""发送表情组合"""
|
||||||
|
if expression_name not in self.expression_combinations:
|
||||||
|
logger.warning(f"[{self.chat_id}] 未知表情: {expression_name}")
|
||||||
|
return
|
||||||
|
|
||||||
|
combination = self.expression_combinations[expression_name]
|
||||||
|
|
||||||
|
# 依次发送各部位表情
|
||||||
|
for part, expression_key in combination.items():
|
||||||
|
if expression_key in self.expressions:
|
||||||
|
expression_data = self.expressions[expression_key]
|
||||||
|
await send_api.custom_to_stream(
|
||||||
|
message_type="facial_expression",
|
||||||
|
content=expression_data,
|
||||||
|
stream_id=self.chat_id
|
||||||
|
)
|
||||||
|
logger.info(f"[{self.chat_id}] 发送面部表情 {part}: {expression_data}")
|
||||||
|
await asyncio.sleep(0.1) # 短暂延迟避免同时发送过多消息
|
||||||
|
|
||||||
|
# 通知ChatMood需要更新amadus
|
||||||
|
# 这里需要从mood_manager获取ChatMood实例并标记
|
||||||
|
chat_mood = mood_manager.get_mood_by_chat_id(self.chat_id)
|
||||||
|
if chat_mood.last_expression != expression_name:
|
||||||
|
chat_mood.last_expression = expression_name
|
||||||
|
chat_mood.expression_needs_update = True
|
||||||
|
|
||||||
|
async def send_expression_by_mood(self, mood_values: dict[str, int]):
|
||||||
|
"""根据情绪值发送相应的面部表情"""
|
||||||
|
expression_name = self.select_expression_by_mood(mood_values)
|
||||||
|
logger.info(f"[{self.chat_id}] 根据情绪值选择表情: {expression_name}, 情绪值: {mood_values}")
|
||||||
|
await self.send_expression(expression_name)
|
||||||
|
|
||||||
|
async def reset_expression(self):
|
||||||
|
"""重置为中性表情"""
|
||||||
|
await self.send_expression("neutral")
|
||||||
|
|
||||||
|
|
||||||
class ChatMood:
|
class ChatMood:
|
||||||
def __init__(self, chat_id: str):
|
def __init__(self, chat_id: str):
|
||||||
self.chat_id: str = chat_id
|
self.chat_id: str = chat_id
|
||||||
self.mood_state: str = "感觉很平静"
|
self.mood_state: str = "感觉很平静"
|
||||||
self.mood_values: dict[str, int] = {"joy": 5, "anger": 1, "sorrow": 1, "pleasure": 5, "fear": 1}
|
self.mood_values: dict[str, int] = {"joy": 5, "anger": 1, "sorrow": 1, "fear": 1}
|
||||||
|
|
||||||
self.regression_count: int = 0
|
self.regression_count: int = 0
|
||||||
|
|
||||||
@@ -119,6 +298,15 @@ class ChatMood:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.last_change_time = 0
|
self.last_change_time = 0
|
||||||
|
|
||||||
|
# 添加面部表情系统
|
||||||
|
self.facial_expression = FacialExpression(chat_id)
|
||||||
|
self.last_expression = "neutral" # 记录上一次的表情
|
||||||
|
self.expression_needs_update = False # 标记表情是否需要更新
|
||||||
|
|
||||||
|
# 设置初始中性表情
|
||||||
|
asyncio.create_task(self.facial_expression.reset_expression())
|
||||||
|
self.expression_needs_update = True # 初始化时也标记需要更新
|
||||||
|
|
||||||
def _parse_numerical_mood(self, response: str) -> dict[str, int] | None:
|
def _parse_numerical_mood(self, response: str) -> dict[str, int] | None:
|
||||||
try:
|
try:
|
||||||
@@ -131,7 +319,7 @@ class ChatMood:
|
|||||||
data = json.loads(response)
|
data = json.loads(response)
|
||||||
|
|
||||||
# Validate
|
# Validate
|
||||||
required_keys = {"joy", "anger", "sorrow", "pleasure", "fear"}
|
required_keys = {"joy", "anger", "sorrow", "fear"}
|
||||||
if not required_keys.issubset(data.keys()):
|
if not required_keys.issubset(data.keys()):
|
||||||
logger.warning(f"Numerical mood response missing keys: {response}")
|
logger.warning(f"Numerical mood response missing keys: {response}")
|
||||||
return None
|
return None
|
||||||
@@ -203,7 +391,6 @@ class ChatMood:
|
|||||||
joy=self.mood_values["joy"],
|
joy=self.mood_values["joy"],
|
||||||
anger=self.mood_values["anger"],
|
anger=self.mood_values["anger"],
|
||||||
sorrow=self.mood_values["sorrow"],
|
sorrow=self.mood_values["sorrow"],
|
||||||
pleasure=self.mood_values["pleasure"],
|
|
||||||
fear=self.mood_values["fear"],
|
fear=self.mood_values["fear"],
|
||||||
)
|
)
|
||||||
logger.info(f"numerical mood prompt: {prompt}")
|
logger.info(f"numerical mood prompt: {prompt}")
|
||||||
@@ -221,9 +408,16 @@ class ChatMood:
|
|||||||
self.mood_state = text_mood_response
|
self.mood_state = text_mood_response
|
||||||
|
|
||||||
if numerical_mood_response:
|
if numerical_mood_response:
|
||||||
|
old_mood_values = self.mood_values.copy()
|
||||||
self.mood_values = numerical_mood_response
|
self.mood_values = numerical_mood_response
|
||||||
if self.mood_values.get("joy", 0) > 5:
|
|
||||||
asyncio.create_task(send_joy_action(self.chat_id))
|
# 发送面部表情
|
||||||
|
new_expression = self.facial_expression.select_expression_by_mood(self.mood_values)
|
||||||
|
if new_expression != self.last_expression:
|
||||||
|
# 立即发送表情
|
||||||
|
asyncio.create_task(self.facial_expression.send_expression(new_expression))
|
||||||
|
self.last_expression = new_expression
|
||||||
|
self.expression_needs_update = True # 标记表情已更新
|
||||||
|
|
||||||
self.last_change_time = message_time
|
self.last_change_time = message_time
|
||||||
|
|
||||||
@@ -277,7 +471,6 @@ class ChatMood:
|
|||||||
joy=self.mood_values["joy"],
|
joy=self.mood_values["joy"],
|
||||||
anger=self.mood_values["anger"],
|
anger=self.mood_values["anger"],
|
||||||
sorrow=self.mood_values["sorrow"],
|
sorrow=self.mood_values["sorrow"],
|
||||||
pleasure=self.mood_values["pleasure"],
|
|
||||||
fear=self.mood_values["fear"],
|
fear=self.mood_values["fear"],
|
||||||
)
|
)
|
||||||
logger.debug(f"numerical regress prompt: {prompt}")
|
logger.debug(f"numerical regress prompt: {prompt}")
|
||||||
@@ -295,12 +488,37 @@ class ChatMood:
|
|||||||
self.mood_state = text_mood_response
|
self.mood_state = text_mood_response
|
||||||
|
|
||||||
if numerical_mood_response:
|
if numerical_mood_response:
|
||||||
|
old_mood_values = self.mood_values.copy()
|
||||||
self.mood_values = numerical_mood_response
|
self.mood_values = numerical_mood_response
|
||||||
if self.mood_values.get("joy", 0) > 5:
|
|
||||||
asyncio.create_task(send_joy_action(self.chat_id))
|
# 发送面部表情
|
||||||
|
new_expression = self.facial_expression.select_expression_by_mood(self.mood_values)
|
||||||
|
if new_expression != self.last_expression:
|
||||||
|
# 立即发送表情
|
||||||
|
asyncio.create_task(self.facial_expression.send_expression(new_expression))
|
||||||
|
self.last_expression = new_expression
|
||||||
|
self.expression_needs_update = True # 标记表情已更新
|
||||||
|
|
||||||
self.regression_count += 1
|
self.regression_count += 1
|
||||||
|
|
||||||
|
async def send_expression_update_if_needed(self):
|
||||||
|
"""如果表情有变化,发送更新到amadus"""
|
||||||
|
if self.expression_needs_update:
|
||||||
|
# 发送当前表情状态到amadus,使用简洁的action/data格式
|
||||||
|
expression_data = {
|
||||||
|
"action": self.last_expression,
|
||||||
|
"data": 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
await send_api.custom_to_stream(
|
||||||
|
message_type="amadus_expression_update",
|
||||||
|
content=expression_data,
|
||||||
|
stream_id=self.chat_id
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"[{self.chat_id}] 发送表情更新到amadus: {expression_data}")
|
||||||
|
self.expression_needs_update = False # 重置标记
|
||||||
|
|
||||||
|
|
||||||
class MoodRegressionTask(AsyncTask):
|
class MoodRegressionTask(AsyncTask):
|
||||||
def __init__(self, mood_manager: "MoodManager"):
|
def __init__(self, mood_manager: "MoodManager"):
|
||||||
@@ -322,6 +540,17 @@ class MoodRegressionTask(AsyncTask):
|
|||||||
await mood.regress_mood()
|
await mood.regress_mood()
|
||||||
|
|
||||||
|
|
||||||
|
class ExpressionUpdateTask(AsyncTask):
|
||||||
|
def __init__(self, mood_manager: "MoodManager"):
|
||||||
|
super().__init__(task_name="ExpressionUpdateTask", run_interval=1)
|
||||||
|
self.mood_manager = mood_manager
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
logger.debug("Running expression update task...")
|
||||||
|
for mood in self.mood_manager.mood_list:
|
||||||
|
await mood.send_expression_update_if_needed()
|
||||||
|
|
||||||
|
|
||||||
class MoodManager:
|
class MoodManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mood_list: list[ChatMood] = []
|
self.mood_list: list[ChatMood] = []
|
||||||
@@ -333,11 +562,18 @@ class MoodManager:
|
|||||||
if self.task_started:
|
if self.task_started:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info("启动情绪回归任务...")
|
logger.info("启动情绪管理任务...")
|
||||||
task = MoodRegressionTask(self)
|
|
||||||
await async_task_manager.add_task(task)
|
# 启动情绪回归任务
|
||||||
|
regression_task = MoodRegressionTask(self)
|
||||||
|
await async_task_manager.add_task(regression_task)
|
||||||
|
|
||||||
|
# 启动表情更新任务
|
||||||
|
expression_task = ExpressionUpdateTask(self)
|
||||||
|
await async_task_manager.add_task(expression_task)
|
||||||
|
|
||||||
self.task_started = True
|
self.task_started = True
|
||||||
logger.info("情绪回归任务已启动")
|
logger.info("情绪管理任务已启动(包含情绪回归和表情更新)")
|
||||||
|
|
||||||
def get_mood_by_chat_id(self, chat_id: str) -> ChatMood:
|
def get_mood_by_chat_id(self, chat_id: str) -> ChatMood:
|
||||||
for mood in self.mood_list:
|
for mood in self.mood_list:
|
||||||
@@ -352,9 +588,30 @@ class MoodManager:
|
|||||||
for mood in self.mood_list:
|
for mood in self.mood_list:
|
||||||
if mood.chat_id == chat_id:
|
if mood.chat_id == chat_id:
|
||||||
mood.mood_state = "感觉很平静"
|
mood.mood_state = "感觉很平静"
|
||||||
|
mood.mood_values = {"joy": 5, "anger": 1, "sorrow": 1, "fear": 1}
|
||||||
mood.regression_count = 0
|
mood.regression_count = 0
|
||||||
|
# 重置面部表情为中性
|
||||||
|
asyncio.create_task(mood.facial_expression.reset_expression())
|
||||||
|
mood.last_expression = "neutral"
|
||||||
|
mood.expression_needs_update = True # 标记表情需要更新
|
||||||
return
|
return
|
||||||
self.mood_list.append(ChatMood(chat_id))
|
|
||||||
|
# 如果没有找到现有的mood,创建新的
|
||||||
|
new_mood = ChatMood(chat_id)
|
||||||
|
self.mood_list.append(new_mood)
|
||||||
|
asyncio.create_task(new_mood.facial_expression.reset_expression())
|
||||||
|
new_mood.expression_needs_update = True # 标记表情需要更新
|
||||||
|
|
||||||
|
def get_facial_expression_by_chat_id(self, chat_id: str) -> FacialExpression:
|
||||||
|
"""获取聊天对应的面部表情管理器"""
|
||||||
|
for mood in self.mood_list:
|
||||||
|
if mood.chat_id == chat_id:
|
||||||
|
return mood.facial_expression
|
||||||
|
|
||||||
|
# 如果没有找到,创建新的
|
||||||
|
new_mood = ChatMood(chat_id)
|
||||||
|
self.mood_list.append(new_mood)
|
||||||
|
return new_mood.facial_expression
|
||||||
|
|
||||||
|
|
||||||
init_prompt()
|
init_prompt()
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ def init_prompt():
|
|||||||
|
|
||||||
Prompt(
|
Prompt(
|
||||||
"""{identity_block}
|
"""{identity_block}
|
||||||
|
你有一头法式橙色卷发,你很可爱,穿戴英式侦探风格学院风裙子和帽子,你正在一个教室场景里进行虚拟线上直播。
|
||||||
|
|
||||||
{relation_info_block}
|
{relation_info_block}
|
||||||
{memory_block}
|
{memory_block}
|
||||||
|
|||||||
Reference in New Issue
Block a user