diff --git a/src/chat/focus_chat/expressors/default_expressor.py b/src/chat/focus_chat/expressors/default_expressor.py index 2d8cf1231..306add243 100644 --- a/src/chat/focus_chat/expressors/default_expressor.py +++ b/src/chat/focus_chat/expressors/default_expressor.py @@ -395,7 +395,7 @@ class DefaultExpressor: thinking_start_time = time.time() if thinking_start_time is None: - logger.error(f"[{stream_name}]思考过程未找到或已结束,无法发送回复。") + logger.error(f"[{stream_name}]expressor思考过程未找到或已结束,无法发送回复。") return None mark_head = False diff --git a/src/chat/focus_chat/planners/actions/__init__.py b/src/chat/focus_chat/planners/actions/__init__.py index 6fc139d74..537090dc1 100644 --- a/src/chat/focus_chat/planners/actions/__init__.py +++ b/src/chat/focus_chat/planners/actions/__init__.py @@ -2,5 +2,6 @@ from . import reply_action # noqa from . import no_reply_action # noqa from . import exit_focus_chat_action # noqa +from . import emoji_action # noqa # 在此处添加更多动作模块导入 diff --git a/src/chat/focus_chat/planners/actions/emoji_action.py b/src/chat/focus_chat/planners/actions/emoji_action.py new file mode 100644 index 000000000..cf7673228 --- /dev/null +++ b/src/chat/focus_chat/planners/actions/emoji_action.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from src.common.logger_manager import get_logger +from src.chat.focus_chat.planners.actions.base_action import BaseAction, register_action +from typing import Tuple, List +from src.chat.heart_flow.observation.observation import Observation +from src.chat.focus_chat.replyer.default_replyer import DefaultReplyer +from src.chat.message_receive.chat_stream import ChatStream +from src.chat.heart_flow.observation.chatting_observation import ChattingObservation +from src.chat.focus_chat.hfc_utils import create_empty_anchor_message + +logger = get_logger("action_taken") + + +@register_action +class EmojiAction(BaseAction): + """表情动作处理类 + + 处理构建和发送消息表情的动作。 + """ + + action_name: str = "emoji" + action_description: str = "当你想发送一个表情" + action_parameters: dict[str:str] = { + "description": "描述你想要发送的表情", + } + action_require: list[str] = [ + "你想要发送一个表情", + "表达情绪时可以选择使用", + ] + + associated_types: list[str] = ["emoji"] + + default = True + + def __init__( + self, + action_data: dict, + reasoning: str, + cycle_timers: dict, + thinking_id: str, + observations: List[Observation], + chat_stream: ChatStream, + log_prefix: str, + replyer: DefaultReplyer, + **kwargs, + ): + """初始化回复动作处理器 + + Args: + action_name: 动作名称 + action_data: 动作数据,包含 message, emojis, target 等 + reasoning: 执行该动作的理由 + cycle_timers: 计时器字典 + thinking_id: 思考ID + observations: 观察列表 + replyer: 回复器 + chat_stream: 聊天流 + log_prefix: 日志前缀 + """ + super().__init__(action_data, reasoning, cycle_timers, thinking_id) + self.observations = observations + self.replyer = replyer + self.chat_stream = chat_stream + self.log_prefix = log_prefix + + async def handle_action(self) -> Tuple[bool, str]: + """ + 处理回复动作 + + Returns: + Tuple[bool, str]: (是否执行成功, 回复文本) + """ + # 注意: 此处可能会使用不同的expressor实现根据任务类型切换不同的回复策略 + return await self._handle_reply( + reasoning=self.reasoning, + reply_data=self.action_data, + cycle_timers=self.cycle_timers, + thinking_id=self.thinking_id, + ) + + async def _handle_reply( + self, reasoning: str, reply_data: dict, cycle_timers: dict, thinking_id: str + ) -> tuple[bool, str]: + """ + 处理统一的回复动作 - 可包含文本和表情,顺序任意 + + reply_data格式: + { + "description": "描述你想要发送的表情" + } + """ + logger.info(f"{self.log_prefix} 决定发送表情: {self.reasoning}") + + # 从聊天观察获取锚定消息 + # chatting_observation: ChattingObservation = next( + # obs for obs in self.observations if isinstance(obs, ChattingObservation) + # ) + # if reply_data.get("target"): + # anchor_message = chatting_observation.search_message_by_text(reply_data["target"]) + # else: + # anchor_message = None + + # 如果没有找到锚点消息,创建一个占位符 + # if not anchor_message: + # logger.info(f"{self.log_prefix} 未找到锚点消息,创建占位符") + # anchor_message = await create_empty_anchor_message( + # self.chat_stream.platform, self.chat_stream.group_info, self.chat_stream + # ) + # else: + # anchor_message.update_chat_stream(self.chat_stream) + + logger.info(f"{self.log_prefix} 为了表情包创建占位符") + anchor_message = await create_empty_anchor_message( + self.chat_stream.platform, self.chat_stream.group_info, self.chat_stream + ) + + success, reply_set = await self.replyer.deal_emoji( + cycle_timers=cycle_timers, + action_data=reply_data, + anchor_message=anchor_message, + # reasoning=reasoning, + thinking_id=thinking_id, + ) + + reply_text = "" + for reply in reply_set: + type = reply[0] + data = reply[1] + if type == "text": + reply_text += data + elif type == "emoji": + reply_text += data + + return success, reply_text diff --git a/src/chat/focus_chat/replyer/default_replyer.py b/src/chat/focus_chat/replyer/default_replyer.py index 02adc6869..c3aa9bf3d 100644 --- a/src/chat/focus_chat/replyer/default_replyer.py +++ b/src/chat/focus_chat/replyer/default_replyer.py @@ -177,6 +177,52 @@ class DefaultReplyer: return False, None # --- 回复器 (Replier) 的定义 --- # + + async def deal_emoji( + self, + anchor_message: MessageRecv, + thinking_id: str, + action_data: Dict[str, Any], + cycle_timers: dict, + ) -> Optional[List[str]]: + """ + 表情动作处理类 + """ + + await self._create_thinking_message(anchor_message, thinking_id) + + + try: + reply = [] + with Timer("选择表情", cycle_timers): + emoji_keyword = action_data.get("description", []) + emoji_base64 = await self._choose_emoji(emoji_keyword) + if emoji_base64: + reply.append(("emoji", emoji_base64)) + + + if reply: + with Timer("发送表情", cycle_timers): + sent_msg_list = await self.send_response_messages( + anchor_message=anchor_message, + thinking_id=thinking_id, + response_set=reply, + ) + has_sent_something = True + else: + logger.warning(f"{self.log_prefix} 表情发送失败") + + if not has_sent_something: + logger.warning(f"{self.log_prefix} 表情发送失败") + + return has_sent_something, sent_msg_list + + except Exception as e: + logger.error(f"回复失败: {e}") + traceback.print_exc() + return False, None + + async def reply( self, @@ -426,13 +472,15 @@ class DefaultReplyer: # 检查思考过程是否仍在进行,并获取开始时间 if thinking_id: + # print(f"thinking_id: {thinking_id}") thinking_start_time = await self.heart_fc_sender.get_thinking_start_time(chat_id, thinking_id) else: - thinking_id = "ds" + str(round(time.time(), 2)) + print("thinking_id is None") + # thinking_id = "ds" + str(round(time.time(), 2)) thinking_start_time = time.time() if thinking_start_time is None: - logger.error(f"[{stream_name}]思考过程未找到或已结束,无法发送回复。") + logger.error(f"[{stream_name}]replyer思考过程未找到或已结束,无法发送回复。") return None mark_head = False