diff --git a/src/chat/focus_chat/heartFC_Cycleinfo.py b/src/chat/focus_chat/heartFC_Cycleinfo.py deleted file mode 100644 index f9a90780d..000000000 --- a/src/chat/focus_chat/heartFC_Cycleinfo.py +++ /dev/null @@ -1,135 +0,0 @@ -import time -import os -from typing import Optional, Dict, Any -from src.common.logger import get_logger -import json - -logger = get_logger("hfc") # Logger Name Changed - -log_dir = "log/log_cycle_debug/" - - -class CycleDetail: - """循环信息记录类""" - - def __init__(self, cycle_id: int): - self.cycle_id = cycle_id - self.prefix = "" - self.thinking_id = "" - self.start_time = time.time() - self.end_time: Optional[float] = None - self.timers: Dict[str, float] = {} - - # 新字段 - self.loop_observation_info: Dict[str, Any] = {} - self.loop_processor_info: Dict[str, Any] = {} # 前处理器信息 - self.loop_plan_info: Dict[str, Any] = {} - self.loop_action_info: Dict[str, Any] = {} - - def to_dict(self) -> Dict[str, Any]: - """将循环信息转换为字典格式""" - - def convert_to_serializable(obj, depth=0, seen=None): - if seen is None: - seen = set() - - # 防止递归过深 - if depth > 5: # 降低递归深度限制 - return str(obj) - - # 防止循环引用 - obj_id = id(obj) - if obj_id in seen: - return str(obj) - seen.add(obj_id) - - try: - if hasattr(obj, "to_dict"): - # 对于有to_dict方法的对象,直接调用其to_dict方法 - return obj.to_dict() - elif isinstance(obj, dict): - # 对于字典,只保留基本类型和可序列化的值 - return { - k: convert_to_serializable(v, depth + 1, seen) - for k, v in obj.items() - if isinstance(k, (str, int, float, bool)) - } - elif isinstance(obj, (list, tuple)): - # 对于列表和元组,只保留可序列化的元素 - return [ - convert_to_serializable(item, depth + 1, seen) - for item in obj - if not isinstance(item, (dict, list, tuple)) - or isinstance(item, (str, int, float, bool, type(None))) - ] - elif isinstance(obj, (str, int, float, bool, type(None))): - return obj - else: - return str(obj) - finally: - seen.remove(obj_id) - - return { - "cycle_id": self.cycle_id, - "start_time": self.start_time, - "end_time": self.end_time, - "timers": self.timers, - "thinking_id": self.thinking_id, - "loop_observation_info": convert_to_serializable(self.loop_observation_info), - "loop_processor_info": convert_to_serializable(self.loop_processor_info), - "loop_plan_info": convert_to_serializable(self.loop_plan_info), - "loop_action_info": convert_to_serializable(self.loop_action_info), - } - - def complete_cycle(self): - """完成循环,记录结束时间""" - self.end_time = time.time() - - # 处理 prefix,只保留中英文字符和基本标点 - if not self.prefix: - self.prefix = "group" - else: - # 只保留中文、英文字母、数字和基本标点 - allowed_chars = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_") - self.prefix = ( - "".join(char for char in self.prefix if "\u4e00" <= char <= "\u9fff" or char in allowed_chars) - or "group" - ) - - # current_time_minute = time.strftime("%Y%m%d_%H%M", time.localtime()) - - # try: - # self.log_cycle_to_file( - # log_dir + self.prefix + f"/{current_time_minute}_cycle_" + str(self.cycle_id) + ".json" - # ) - # except Exception as e: - # logger.warning(f"写入文件日志,可能是群名称包含非法字符: {e}") - - def log_cycle_to_file(self, file_path: str): - """将循环信息写入文件""" - # 如果目录不存在,则创建目 - dir_name = os.path.dirname(file_path) - # 去除特殊字符,保留字母、数字、下划线、中划线和中文 - dir_name = "".join( - char for char in dir_name if char.isalnum() or char in ["_", "-", "/"] or "\u4e00" <= char <= "\u9fff" - ) - # print("dir_name:", dir_name) - if dir_name and not os.path.exists(dir_name): - os.makedirs(dir_name, exist_ok=True) - # 写入文件 - - file_path = os.path.join(dir_name, os.path.basename(file_path)) - # print("file_path:", file_path) - with open(file_path, "a", encoding="utf-8") as f: - f.write(json.dumps(self.to_dict(), ensure_ascii=False) + "\n") - - def set_thinking_id(self, thinking_id: str): - """设置思考消息ID""" - self.thinking_id = thinking_id - - def set_loop_info(self, loop_info: Dict[str, Any]): - """设置循环信息""" - self.loop_observation_info = loop_info["loop_observation_info"] - self.loop_processor_info = loop_info["loop_processor_info"] - self.loop_plan_info = loop_info["loop_plan_info"] - self.loop_action_info = loop_info["loop_action_info"] diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index a6d12b821..9e10da366 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -9,17 +9,14 @@ from rich.traceback import install from src.chat.utils.prompt_builder import global_prompt_manager from src.common.logger import get_logger from src.chat.utils.timer_calculator import Timer -from src.chat.heart_flow.observation.observation import Observation -from src.chat.focus_chat.heartFC_Cycleinfo import CycleDetail +from src.chat.focus_chat.observation.observation import Observation from src.chat.focus_chat.info.info_base import InfoBase from src.chat.focus_chat.info_processors.chattinginfo_processor import ChattingInfoProcessor from src.chat.focus_chat.info_processors.working_memory_processor import WorkingMemoryProcessor -from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation -from src.chat.heart_flow.observation.working_observation import WorkingMemoryObservation -from src.chat.heart_flow.observation.chatting_observation import ChattingObservation -from src.chat.heart_flow.observation.actions_observation import ActionObservation - -from src.chat.focus_chat.memory_activator import MemoryActivator +from src.chat.focus_chat.observation.hfcloop_observation import HFCloopObservation +from src.chat.focus_chat.observation.working_observation import WorkingMemoryObservation +from src.chat.focus_chat.observation.chatting_observation import ChattingObservation +from src.chat.focus_chat.observation.actions_observation import ActionObservation from src.chat.focus_chat.info_processors.base_processor import BaseProcessor from src.chat.planner_actions.planner_focus import ActionPlanner from src.chat.planner_actions.action_modifier import ActionModifier @@ -28,6 +25,7 @@ from src.config.config import global_config from src.chat.focus_chat.hfc_performance_logger import HFCPerformanceLogger from src.chat.focus_chat.hfc_version_manager import get_hfc_version from src.person_info.relationship_builder_manager import relationship_builder_manager +from src.chat.focus_chat.hfc_utils import CycleDetail install(extra_lines=3) @@ -76,8 +74,6 @@ class HeartFChatting: self.chat_stream = get_chat_manager().get_stream(self.stream_id) self.log_prefix = f"[{get_chat_manager().get_stream_name(self.stream_id) or self.stream_id}]" - self.memory_activator = MemoryActivator() - self.relationship_builder = relationship_builder_manager.get_or_create_builder(self.stream_id) # 新增:消息计数器和疲惫阈值 diff --git a/src/chat/focus_chat/hfc_utils.py b/src/chat/focus_chat/hfc_utils.py index faec67eb8..496239851 100644 --- a/src/chat/focus_chat/hfc_utils.py +++ b/src/chat/focus_chat/hfc_utils.py @@ -5,9 +5,143 @@ from src.chat.message_receive.chat_stream import ChatStream from src.chat.message_receive.message import UserInfo from src.common.logger import get_logger import json +import time +import os +from typing import Optional, Dict, Any +from src.common.logger import get_logger +import json logger = get_logger(__name__) +log_dir = "log/log_cycle_debug/" + + +class CycleDetail: + """循环信息记录类""" + + def __init__(self, cycle_id: int): + self.cycle_id = cycle_id + self.prefix = "" + self.thinking_id = "" + self.start_time = time.time() + self.end_time: Optional[float] = None + self.timers: Dict[str, float] = {} + + # 新字段 + self.loop_observation_info: Dict[str, Any] = {} + self.loop_processor_info: Dict[str, Any] = {} # 前处理器信息 + self.loop_plan_info: Dict[str, Any] = {} + self.loop_action_info: Dict[str, Any] = {} + + def to_dict(self) -> Dict[str, Any]: + """将循环信息转换为字典格式""" + + def convert_to_serializable(obj, depth=0, seen=None): + if seen is None: + seen = set() + + # 防止递归过深 + if depth > 5: # 降低递归深度限制 + return str(obj) + + # 防止循环引用 + obj_id = id(obj) + if obj_id in seen: + return str(obj) + seen.add(obj_id) + + try: + if hasattr(obj, "to_dict"): + # 对于有to_dict方法的对象,直接调用其to_dict方法 + return obj.to_dict() + elif isinstance(obj, dict): + # 对于字典,只保留基本类型和可序列化的值 + return { + k: convert_to_serializable(v, depth + 1, seen) + for k, v in obj.items() + if isinstance(k, (str, int, float, bool)) + } + elif isinstance(obj, (list, tuple)): + # 对于列表和元组,只保留可序列化的元素 + return [ + convert_to_serializable(item, depth + 1, seen) + for item in obj + if not isinstance(item, (dict, list, tuple)) + or isinstance(item, (str, int, float, bool, type(None))) + ] + elif isinstance(obj, (str, int, float, bool, type(None))): + return obj + else: + return str(obj) + finally: + seen.remove(obj_id) + + return { + "cycle_id": self.cycle_id, + "start_time": self.start_time, + "end_time": self.end_time, + "timers": self.timers, + "thinking_id": self.thinking_id, + "loop_observation_info": convert_to_serializable(self.loop_observation_info), + "loop_processor_info": convert_to_serializable(self.loop_processor_info), + "loop_plan_info": convert_to_serializable(self.loop_plan_info), + "loop_action_info": convert_to_serializable(self.loop_action_info), + } + + def complete_cycle(self): + """完成循环,记录结束时间""" + self.end_time = time.time() + + # 处理 prefix,只保留中英文字符和基本标点 + if not self.prefix: + self.prefix = "group" + else: + # 只保留中文、英文字母、数字和基本标点 + allowed_chars = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_") + self.prefix = ( + "".join(char for char in self.prefix if "\u4e00" <= char <= "\u9fff" or char in allowed_chars) + or "group" + ) + + # current_time_minute = time.strftime("%Y%m%d_%H%M", time.localtime()) + + # try: + # self.log_cycle_to_file( + # log_dir + self.prefix + f"/{current_time_minute}_cycle_" + str(self.cycle_id) + ".json" + # ) + # except Exception as e: + # logger.warning(f"写入文件日志,可能是群名称包含非法字符: {e}") + + def log_cycle_to_file(self, file_path: str): + """将循环信息写入文件""" + # 如果目录不存在,则创建目 + dir_name = os.path.dirname(file_path) + # 去除特殊字符,保留字母、数字、下划线、中划线和中文 + dir_name = "".join( + char for char in dir_name if char.isalnum() or char in ["_", "-", "/"] or "\u4e00" <= char <= "\u9fff" + ) + # print("dir_name:", dir_name) + if dir_name and not os.path.exists(dir_name): + os.makedirs(dir_name, exist_ok=True) + # 写入文件 + + file_path = os.path.join(dir_name, os.path.basename(file_path)) + # print("file_path:", file_path) + with open(file_path, "a", encoding="utf-8") as f: + f.write(json.dumps(self.to_dict(), ensure_ascii=False) + "\n") + + def set_thinking_id(self, thinking_id: str): + """设置思考消息ID""" + self.thinking_id = thinking_id + + def set_loop_info(self, loop_info: Dict[str, Any]): + """设置循环信息""" + self.loop_observation_info = loop_info["loop_observation_info"] + self.loop_processor_info = loop_info["loop_processor_info"] + self.loop_plan_info = loop_info["loop_plan_info"] + self.loop_action_info = loop_info["loop_action_info"] + + async def create_empty_anchor_message( platform: str, group_info: dict, chat_stream: ChatStream diff --git a/src/chat/focus_chat/info/chat_info.py b/src/chat/focus_chat/info/chat_info.py deleted file mode 100644 index 445529318..000000000 --- a/src/chat/focus_chat/info/chat_info.py +++ /dev/null @@ -1,97 +0,0 @@ -from typing import Dict, Optional -from dataclasses import dataclass -from .info_base import InfoBase - - -@dataclass -class ChatInfo(InfoBase): - """聊天信息类 - - 用于记录和管理聊天相关的信息,包括聊天ID、名称和类型等。 - 继承自 InfoBase 类,使用字典存储具体数据。 - - Attributes: - type (str): 信息类型标识符,固定为 "chat" - - Data Fields: - chat_id (str): 聊天的唯一标识符 - chat_name (str): 聊天的名称 - chat_type (str): 聊天的类型 - """ - - type: str = "chat" - - def set_chat_id(self, chat_id: str) -> None: - """设置聊天ID - - Args: - chat_id (str): 聊天的唯一标识符 - """ - self.data["chat_id"] = chat_id - - def set_chat_name(self, chat_name: str) -> None: - """设置聊天名称 - - Args: - chat_name (str): 聊天的名称 - """ - self.data["chat_name"] = chat_name - - def set_chat_type(self, chat_type: str) -> None: - """设置聊天类型 - - Args: - chat_type (str): 聊天的类型 - """ - self.data["chat_type"] = chat_type - - def get_chat_id(self) -> Optional[str]: - """获取聊天ID - - Returns: - Optional[str]: 聊天的唯一标识符,如果未设置则返回 None - """ - return self.get_info("chat_id") - - def get_chat_name(self) -> Optional[str]: - """获取聊天名称 - - Returns: - Optional[str]: 聊天的名称,如果未设置则返回 None - """ - return self.get_info("chat_name") - - def get_chat_type(self) -> Optional[str]: - """获取聊天类型 - - Returns: - Optional[str]: 聊天的类型,如果未设置则返回 None - """ - return self.get_info("chat_type") - - def get_type(self) -> str: - """获取信息类型 - - Returns: - str: 当前信息对象的类型标识符 - """ - return self.type - - def get_data(self) -> Dict[str, str]: - """获取所有信息数据 - - Returns: - Dict[str, str]: 包含所有信息数据的字典 - """ - return self.data - - def get_info(self, key: str) -> Optional[str]: - """获取特定属性的信息 - - Args: - key: 要获取的属性键名 - - Returns: - Optional[str]: 属性值,如果键不存在则返回 None - """ - return self.data.get(key) diff --git a/src/chat/focus_chat/info_processors/base_processor.py b/src/chat/focus_chat/info_processors/base_processor.py index 3b88eb841..26396580c 100644 --- a/src/chat/focus_chat/info_processors/base_processor.py +++ b/src/chat/focus_chat/info_processors/base_processor.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from typing import List, Any from src.chat.focus_chat.info.info_base import InfoBase -from src.chat.heart_flow.observation.observation import Observation +from src.chat.focus_chat.observation.observation import Observation from src.common.logger import get_logger logger = get_logger("base_processor") diff --git a/src/chat/focus_chat/info_processors/chattinginfo_processor.py b/src/chat/focus_chat/info_processors/chattinginfo_processor.py index 6443982e1..a4aea17c4 100644 --- a/src/chat/focus_chat/info_processors/chattinginfo_processor.py +++ b/src/chat/focus_chat/info_processors/chattinginfo_processor.py @@ -1,10 +1,10 @@ from typing import List, Any from src.chat.focus_chat.info.obs_info import ObsInfo -from src.chat.heart_flow.observation.observation import Observation +from src.chat.focus_chat.observation.observation import Observation from src.chat.focus_chat.info.info_base import InfoBase from .base_processor import BaseProcessor from src.common.logger import get_logger -from src.chat.heart_flow.observation.chatting_observation import ChattingObservation +from src.chat.focus_chat.observation.chatting_observation import ChattingObservation from datetime import datetime from src.llm_models.utils_model import LLMRequest from src.config.config import global_config diff --git a/src/chat/focus_chat/info_processors/working_memory_processor.py b/src/chat/focus_chat/info_processors/working_memory_processor.py index abe9786d4..ad2c88876 100644 --- a/src/chat/focus_chat/info_processors/working_memory_processor.py +++ b/src/chat/focus_chat/info_processors/working_memory_processor.py @@ -1,5 +1,5 @@ -from src.chat.heart_flow.observation.chatting_observation import ChattingObservation -from src.chat.heart_flow.observation.observation import Observation +from src.chat.focus_chat.observation.chatting_observation import ChattingObservation +from src.chat.focus_chat.observation.observation import Observation from src.llm_models.utils_model import LLMRequest from src.config.config import global_config import time @@ -9,7 +9,7 @@ from src.chat.utils.prompt_builder import Prompt, global_prompt_manager from src.chat.message_receive.chat_stream import get_chat_manager from .base_processor import BaseProcessor from typing import List -from src.chat.heart_flow.observation.working_observation import WorkingMemoryObservation +from src.chat.focus_chat.observation.working_observation import WorkingMemoryObservation from src.chat.focus_chat.working_memory.working_memory import WorkingMemory from src.chat.focus_chat.info.info_base import InfoBase from json_repair import repair_json diff --git a/src/chat/heart_flow/observation/actions_observation.py b/src/chat/focus_chat/observation/actions_observation.py similarity index 100% rename from src/chat/heart_flow/observation/actions_observation.py rename to src/chat/focus_chat/observation/actions_observation.py diff --git a/src/chat/heart_flow/observation/chatting_observation.py b/src/chat/focus_chat/observation/chatting_observation.py similarity index 98% rename from src/chat/heart_flow/observation/chatting_observation.py rename to src/chat/focus_chat/observation/chatting_observation.py index 2a4a42856..201e313fa 100644 --- a/src/chat/heart_flow/observation/chatting_observation.py +++ b/src/chat/focus_chat/observation/chatting_observation.py @@ -8,9 +8,9 @@ from src.chat.utils.chat_message_builder import ( get_person_id_list, ) from src.chat.utils.prompt_builder import global_prompt_manager, Prompt -from src.chat.heart_flow.observation.observation import Observation +from src.chat.focus_chat.observation.observation import Observation from src.common.logger import get_logger -from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info +from src.chat.utils.utils import get_chat_type_and_target_info logger = get_logger("observation") diff --git a/src/chat/heart_flow/observation/hfcloop_observation.py b/src/chat/focus_chat/observation/hfcloop_observation.py similarity index 98% rename from src/chat/heart_flow/observation/hfcloop_observation.py rename to src/chat/focus_chat/observation/hfcloop_observation.py index c2834257b..ad7245f8a 100644 --- a/src/chat/heart_flow/observation/hfcloop_observation.py +++ b/src/chat/focus_chat/observation/hfcloop_observation.py @@ -2,7 +2,7 @@ # 外部世界可以是某个聊天 不同平台的聊天 也可以是任意媒体 from datetime import datetime from src.common.logger import get_logger -from src.chat.focus_chat.heartFC_Cycleinfo import CycleDetail +from src.chat.focus_chat.hfc_utils import CycleDetail from typing import List # Import the new utility function diff --git a/src/chat/heart_flow/observation/observation.py b/src/chat/focus_chat/observation/observation.py similarity index 100% rename from src/chat/heart_flow/observation/observation.py rename to src/chat/focus_chat/observation/observation.py diff --git a/src/chat/heart_flow/observation/working_observation.py b/src/chat/focus_chat/observation/working_observation.py similarity index 100% rename from src/chat/heart_flow/observation/working_observation.py rename to src/chat/focus_chat/observation/working_observation.py diff --git a/src/chat/heart_flow/chat_state_info.py b/src/chat/heart_flow/chat_state_info.py index db4c2d5c7..320093533 100644 --- a/src/chat/heart_flow/chat_state_info.py +++ b/src/chat/heart_flow/chat_state_info.py @@ -7,7 +7,6 @@ class ChatState(enum.Enum): NORMAL = "随便水群" FOCUSED = "认真水群" - class ChatStateInfo: def __init__(self): self.chat_status: ChatState = ChatState.NORMAL diff --git a/src/chat/focus_chat/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py similarity index 100% rename from src/chat/focus_chat/heartflow_message_processor.py rename to src/chat/heart_flow/heartflow_message_processor.py diff --git a/src/chat/heart_flow/sub_heartflow.py b/src/chat/heart_flow/sub_heartflow.py index 206c00364..6dee805a0 100644 --- a/src/chat/heart_flow/sub_heartflow.py +++ b/src/chat/heart_flow/sub_heartflow.py @@ -1,5 +1,3 @@ -from .observation.observation import Observation -from src.chat.heart_flow.observation.chatting_observation import ChattingObservation import asyncio import time from typing import Optional, List, Dict, Tuple @@ -10,7 +8,7 @@ from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.focus_chat.heartFC_chat import HeartFChatting from src.chat.normal_chat.normal_chat import NormalChat from src.chat.heart_flow.chat_state_info import ChatState, ChatStateInfo -from .utils_chat import get_chat_type_and_target_info +from src.chat.utils.utils import get_chat_type_and_target_info from src.config.config import global_config from rich.traceback import install @@ -314,24 +312,6 @@ class SubHeartflow: f"{log_prefix} 尝试将状态从 {current_state.value} 变为 {new_state.value},但未成功或未执行更改。" ) - def add_observation(self, observation: Observation): - for existing_obs in self.observations: - if existing_obs.observe_id == observation.observe_id: - return - self.observations.append(observation) - - def remove_observation(self, observation: Observation): - if observation in self.observations: - self.observations.remove(observation) - - def get_all_observations(self) -> list[Observation]: - return self.observations - - def _get_primary_observation(self) -> Optional[ChattingObservation]: - if self.observations and isinstance(self.observations[0], ChattingObservation): - return self.observations[0] - logger.warning(f"SubHeartflow {self.subheartflow_id} 没有找到有效的 ChattingObservation") - return None def get_normal_chat_last_speak_time(self) -> float: if self.normal_chat_instance: diff --git a/src/chat/heart_flow/utils_chat.py b/src/chat/heart_flow/utils_chat.py deleted file mode 100644 index e25ee6b6e..000000000 --- a/src/chat/heart_flow/utils_chat.py +++ /dev/null @@ -1,73 +0,0 @@ -from typing import Optional, Tuple, Dict -from src.common.logger import get_logger -from src.chat.message_receive.chat_stream import get_chat_manager -from src.person_info.person_info import PersonInfoManager, get_person_info_manager - -logger = get_logger("heartflow_utils") - - -def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Dict]]: - """ - 获取聊天类型(是否群聊)和私聊对象信息。 - - Args: - chat_id: 聊天流ID - - Returns: - Tuple[bool, Optional[Dict]]: - - bool: 是否为群聊 (True 是群聊, False 是私聊或未知) - - Optional[Dict]: 如果是私聊,包含对方信息的字典;否则为 None。 - 字典包含: platform, user_id, user_nickname, person_id, person_name - """ - is_group_chat = False # Default to private/unknown - chat_target_info = None - - try: - chat_stream = get_chat_manager().get_stream(chat_id) - - if chat_stream: - if chat_stream.group_info: - is_group_chat = True - chat_target_info = None # Explicitly None for group chat - elif chat_stream.user_info: # It's a private chat - is_group_chat = False - user_info = chat_stream.user_info - platform = chat_stream.platform - user_id = user_info.user_id - - # Initialize target_info with basic info - target_info = { - "platform": platform, - "user_id": user_id, - "user_nickname": user_info.user_nickname, - "person_id": None, - "person_name": None, - } - - # Try to fetch person info - try: - # Assume get_person_id is sync (as per original code), keep using to_thread - person_id = PersonInfoManager.get_person_id(platform, user_id) - person_name = None - if person_id: - # get_value is async, so await it directly - person_info_manager = get_person_info_manager() - person_name = person_info_manager.get_value_sync(person_id, "person_name") - - target_info["person_id"] = person_id - target_info["person_name"] = person_name - except Exception as person_e: - logger.warning( - f"获取 person_id 或 person_name 时出错 for {platform}:{user_id} in utils: {person_e}" - ) - - chat_target_info = target_info - else: - logger.warning(f"无法获取 chat_stream for {chat_id} in utils") - # Keep defaults: is_group_chat=False, chat_target_info=None - - except Exception as e: - logger.error(f"获取聊天类型和目标信息时出错 for {chat_id}: {e}", exc_info=True) - # Keep defaults on error - - return is_group_chat, chat_target_info diff --git a/src/chat/focus_chat/memory_activator.py b/src/chat/memory_system/memory_activator.py similarity index 100% rename from src/chat/focus_chat/memory_activator.py rename to src/chat/memory_system/memory_activator.py diff --git a/src/chat/message_receive/bot.py b/src/chat/message_receive/bot.py index 6126fc751..0bc5bec58 100644 --- a/src/chat/message_receive/bot.py +++ b/src/chat/message_receive/bot.py @@ -9,7 +9,7 @@ from src.chat.message_receive.message import MessageRecv from src.experimental.only_message_process import MessageProcessor from src.chat.message_receive.storage import MessageStorage from src.experimental.PFC.pfc_manager import PFCManager -from src.chat.focus_chat.heartflow_message_processor import HeartFCMessageReceiver +from src.chat.heart_flow.heartflow_message_processor import HeartFCMessageReceiver from src.chat.utils.prompt_builder import Prompt, global_prompt_manager from src.config.config import global_config from src.plugin_system.core.component_registry import component_registry # 导入新插件系统 diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 6817670f0..5e6b14f63 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -21,7 +21,7 @@ import traceback from src.chat.planner_actions.planner_normal import NormalChatPlanner from src.chat.planner_actions.action_modifier import ActionModifier -from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info +from src.chat.utils.utils import get_chat_type_and_target_info from src.manager.mood_manager import mood_manager willing_manager = get_willing_manager() diff --git a/src/chat/planner_actions/action_modifier.py b/src/chat/planner_actions/action_modifier.py index c57842ae4..426c54657 100644 --- a/src/chat/planner_actions/action_modifier.py +++ b/src/chat/planner_actions/action_modifier.py @@ -1,8 +1,8 @@ from typing import List, Optional, Any, Dict -from src.chat.heart_flow.observation.observation import Observation +from src.chat.focus_chat.observation.observation import Observation from src.common.logger import get_logger -from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation -from src.chat.heart_flow.observation.chatting_observation import ChattingObservation +from src.chat.focus_chat.observation.hfcloop_observation import HFCloopObservation +from src.chat.focus_chat.observation.chatting_observation import ChattingObservation from src.chat.message_receive.chat_stream import get_chat_manager from src.config.config import global_config from src.llm_models.utils_model import LLMRequest diff --git a/src/chat/planner_actions/planner_focus.py b/src/chat/planner_actions/planner_focus.py index bb3bdcacd..c52b8b486 100644 --- a/src/chat/planner_actions/planner_focus.py +++ b/src/chat/planner_actions/planner_focus.py @@ -11,7 +11,7 @@ from src.common.logger import get_logger from src.chat.utils.prompt_builder import Prompt, global_prompt_manager from src.chat.planner_actions.action_manager import ActionManager from json_repair import repair_json -from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info +from src.chat.utils.utils import get_chat_type_and_target_info from datetime import datetime logger = get_logger("planner") diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index dd1b4e8a6..62ff926f5 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -10,7 +10,7 @@ from src.llm_models.utils_model import LLMRequest from src.config.config import global_config from src.chat.utils.timer_calculator import Timer # <--- Import Timer from src.chat.focus_chat.heartFC_sender import HeartFCSender -from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info +from src.chat.utils.utils import get_chat_type_and_target_info from src.chat.message_receive.chat_stream import ChatStream from src.chat.focus_chat.hfc_utils import parse_thinking_id_to_timestamp from src.chat.utils.prompt_builder import Prompt, global_prompt_manager @@ -26,7 +26,7 @@ from src.person_info.person_info import get_person_info_manager from datetime import datetime import re from src.chat.knowledge.knowledge_lib import qa_manager -from src.chat.focus_chat.memory_activator import MemoryActivator +from src.chat.memory_system.memory_activator import MemoryActivator from src.tools.tool_executor import ToolExecutor logger = get_logger("replyer") diff --git a/src/chat/utils/utils.py b/src/chat/utils/utils.py index a081ad9a5..d4bb5b17a 100644 --- a/src/chat/utils/utils.py +++ b/src/chat/utils/utils.py @@ -14,6 +14,9 @@ from src.llm_models.utils_model import LLMRequest from .typo_generator import ChineseTypoGenerator from ...config.config import global_config from ...common.message_repository import find_messages, count_messages +from typing import Optional, Tuple, Dict +from src.chat.message_receive.chat_stream import get_chat_manager +from src.person_info.person_info import PersonInfoManager, get_person_info_manager logger = get_logger("chat_utils") @@ -638,3 +641,69 @@ def translate_timestamp_to_human_readable(timestamp: float, mode: str = "normal" else: # mode = "lite" or unknown # 只返回时分秒格式,喵~ return time.strftime("%H:%M:%S", time.localtime(timestamp)) + +def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Dict]]: + """ + 获取聊天类型(是否群聊)和私聊对象信息。 + + Args: + chat_id: 聊天流ID + + Returns: + Tuple[bool, Optional[Dict]]: + - bool: 是否为群聊 (True 是群聊, False 是私聊或未知) + - Optional[Dict]: 如果是私聊,包含对方信息的字典;否则为 None。 + 字典包含: platform, user_id, user_nickname, person_id, person_name + """ + is_group_chat = False # Default to private/unknown + chat_target_info = None + + try: + chat_stream = get_chat_manager().get_stream(chat_id) + + if chat_stream: + if chat_stream.group_info: + is_group_chat = True + chat_target_info = None # Explicitly None for group chat + elif chat_stream.user_info: # It's a private chat + is_group_chat = False + user_info = chat_stream.user_info + platform = chat_stream.platform + user_id = user_info.user_id + + # Initialize target_info with basic info + target_info = { + "platform": platform, + "user_id": user_id, + "user_nickname": user_info.user_nickname, + "person_id": None, + "person_name": None, + } + + # Try to fetch person info + try: + # Assume get_person_id is sync (as per original code), keep using to_thread + person_id = PersonInfoManager.get_person_id(platform, user_id) + person_name = None + if person_id: + # get_value is async, so await it directly + person_info_manager = get_person_info_manager() + person_name = person_info_manager.get_value_sync(person_id, "person_name") + + target_info["person_id"] = person_id + target_info["person_name"] = person_name + except Exception as person_e: + logger.warning( + f"获取 person_id 或 person_name 时出错 for {platform}:{user_id} in utils: {person_e}" + ) + + chat_target_info = target_info + else: + logger.warning(f"无法获取 chat_stream for {chat_id} in utils") + # Keep defaults: is_group_chat=False, chat_target_info=None + + except Exception as e: + logger.error(f"获取聊天类型和目标信息时出错 for {chat_id}: {e}", exc_info=True) + # Keep defaults on error + + return is_group_chat, chat_target_info \ No newline at end of file