before merge commit

pull/1195/head
UnCLAS-Prommer 2025-08-18 15:33:06 +08:00
parent ce74df51db
commit 5427e2448c
No known key found for this signature in database
5 changed files with 118 additions and 145 deletions

View File

@ -1,6 +1,7 @@
import asyncio import asyncio
import time import time
import traceback import traceback
import math
import random import random
from typing import List, Optional, Dict, Any, Tuple from typing import List, Optional, Dict, Any, Tuple
from rich.traceback import install from rich.traceback import install
@ -8,6 +9,7 @@ from collections import deque
from src.config.config import global_config from src.config.config import global_config
from src.common.logger import get_logger from src.common.logger import get_logger
from src.common.data_models.database_data_model import DatabaseMessages
from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager
from src.chat.utils.prompt_builder import global_prompt_manager from src.chat.utils.prompt_builder import global_prompt_manager
from src.chat.utils.timer_calculator import Timer from src.chat.utils.timer_calculator import Timer
@ -15,21 +17,19 @@ from src.chat.planner_actions.planner import ActionPlanner
from src.chat.planner_actions.action_modifier import ActionModifier from src.chat.planner_actions.action_modifier import ActionModifier
from src.chat.planner_actions.action_manager import ActionManager from src.chat.planner_actions.action_manager import ActionManager
from src.chat.chat_loop.hfc_utils import CycleDetail from src.chat.chat_loop.hfc_utils import CycleDetail
from src.person_info.relationship_builder_manager import relationship_builder_manager from src.chat.chat_loop.hfc_utils import send_typing, stop_typing
from src.chat.memory_system.Hippocampus import hippocampus_manager
from src.chat.frequency_control.talk_frequency_control import talk_frequency_control
from src.chat.frequency_control.focus_value_control import focus_value_control
from src.chat.express.expression_learner import expression_learner_manager from src.chat.express.expression_learner import expression_learner_manager
from src.person_info.relationship_builder_manager import relationship_builder_manager
from src.person_info.person_info import Person from src.person_info.person_info import Person
from src.plugin_system.base.component_types import ChatMode, EventType from src.plugin_system.base.component_types import ChatMode, EventType
from src.plugin_system.core import events_manager from src.plugin_system.core import events_manager
from src.plugin_system.apis import generator_api, send_api, message_api, database_api from src.plugin_system.apis import generator_api, send_api, message_api, database_api
from src.mais4u.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
import math
from src.mais4u.s4u_config import s4u_config from src.mais4u.s4u_config import s4u_config
# no_action逻辑已集成到heartFC_chat.py中不再需要导入
from src.chat.chat_loop.hfc_utils import send_typing, stop_typing
# 导入记忆系统
from src.chat.memory_system.Hippocampus import hippocampus_manager
from src.chat.frequency_control.talk_frequency_control import talk_frequency_control
from src.chat.frequency_control.focus_value_control import focus_value_control
ERROR_LOOP_INFO = { ERROR_LOOP_INFO = {
"loop_plan_info": { "loop_plan_info": {
@ -62,10 +62,7 @@ class HeartFChatting:
其生命周期现在由其关联的 SubHeartflow FOCUSED 状态控制 其生命周期现在由其关联的 SubHeartflow FOCUSED 状态控制
""" """
def __init__( def __init__(self, chat_id: str):
self,
chat_id: str,
):
""" """
HeartFChatting 初始化函数 HeartFChatting 初始化函数
@ -83,7 +80,7 @@ class HeartFChatting:
self.relationship_builder = relationship_builder_manager.get_or_create_builder(self.stream_id) self.relationship_builder = relationship_builder_manager.get_or_create_builder(self.stream_id)
self.expression_learner = expression_learner_manager.get_expression_learner(self.stream_id) self.expression_learner = expression_learner_manager.get_expression_learner(self.stream_id)
self.talk_frequency_control = talk_frequency_control.get_talk_frequency_control(self.stream_id) self.talk_frequency_control = talk_frequency_control.get_talk_frequency_control(self.stream_id)
self.focus_value_control = focus_value_control.get_focus_value_control(self.stream_id) self.focus_value_control = focus_value_control.get_focus_value_control(self.stream_id)
@ -104,7 +101,7 @@ class HeartFChatting:
self.plan_timeout_count = 0 self.plan_timeout_count = 0
self.last_read_time = time.time() - 1 self.last_read_time = time.time() - 1
self.focus_energy = 1 self.focus_energy = 1
self.no_action_consecutive = 0 self.no_action_consecutive = 0
# 最近三次no_action的新消息兴趣度记录 # 最近三次no_action的新消息兴趣度记录
@ -166,27 +163,26 @@ class HeartFChatting:
# 获取动作类型,兼容新旧格式 # 获取动作类型,兼容新旧格式
action_type = "未知动作" action_type = "未知动作"
if hasattr(self, '_current_cycle_detail') and self._current_cycle_detail: if hasattr(self, "_current_cycle_detail") and self._current_cycle_detail:
loop_plan_info = self._current_cycle_detail.loop_plan_info loop_plan_info = self._current_cycle_detail.loop_plan_info
if isinstance(loop_plan_info, dict): if isinstance(loop_plan_info, dict):
action_result = loop_plan_info.get('action_result', {}) action_result = loop_plan_info.get("action_result", {})
if isinstance(action_result, dict): if isinstance(action_result, dict):
# 旧格式action_result是字典 # 旧格式action_result是字典
action_type = action_result.get('action_type', '未知动作') action_type = action_result.get("action_type", "未知动作")
elif isinstance(action_result, list) and action_result: elif isinstance(action_result, list) and action_result:
# 新格式action_result是actions列表 # 新格式action_result是actions列表
action_type = action_result[0].get('action_type', '未知动作') action_type = action_result[0].get("action_type", "未知动作")
elif isinstance(loop_plan_info, list) and loop_plan_info: elif isinstance(loop_plan_info, list) and loop_plan_info:
# 直接是actions列表的情况 # 直接是actions列表的情况
action_type = loop_plan_info[0].get('action_type', '未知动作') action_type = loop_plan_info[0].get("action_type", "未知动作")
logger.info( logger.info(
f"{self.log_prefix}{self._current_cycle_detail.cycle_id}次思考," f"{self.log_prefix}{self._current_cycle_detail.cycle_id}次思考,"
f"耗时: {self._current_cycle_detail.end_time - self._current_cycle_detail.start_time:.1f}秒, " # type: ignore f"耗时: {self._current_cycle_detail.end_time - self._current_cycle_detail.start_time:.1f}秒, " # type: ignore
f"选择动作: {action_type}" f"选择动作: {action_type}" + (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
) )
def _determine_form_type(self) -> None: def _determine_form_type(self) -> None:
"""判断使用哪种形式的no_action""" """判断使用哪种形式的no_action"""
# 如果连续no_action次数少于3次使用waiting形式 # 如果连续no_action次数少于3次使用waiting形式
@ -195,42 +191,44 @@ class HeartFChatting:
else: else:
# 计算最近三次记录的兴趣度总和 # 计算最近三次记录的兴趣度总和
total_recent_interest = sum(self.recent_interest_records) total_recent_interest = sum(self.recent_interest_records)
# 计算调整后的阈值 # 计算调整后的阈值
adjusted_threshold = 1 / self.talk_frequency_control.get_current_talk_frequency() adjusted_threshold = 1 / self.talk_frequency_control.get_current_talk_frequency()
logger.info(f"{self.log_prefix} 最近三次兴趣度总和: {total_recent_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}") logger.info(
f"{self.log_prefix} 最近三次兴趣度总和: {total_recent_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}"
)
# 如果兴趣度总和小于阈值进入breaking形式 # 如果兴趣度总和小于阈值进入breaking形式
if total_recent_interest < adjusted_threshold: if total_recent_interest < adjusted_threshold:
logger.info(f"{self.log_prefix} 兴趣度不足,进入休息") logger.info(f"{self.log_prefix} 兴趣度不足,进入休息")
self.focus_energy = random.randint(3, 6) self.focus_energy = random.randint(3, 6)
else: else:
logger.info(f"{self.log_prefix} 兴趣度充足,等待新消息") logger.info(f"{self.log_prefix} 兴趣度充足,等待新消息")
self.focus_energy = 1 self.focus_energy = 1
async def _should_process_messages(self, new_message: List[Dict[str, Any]]) -> tuple[bool,float]: async def _should_process_messages(self, new_message: List[DatabaseMessages]) -> tuple[bool, float]:
""" """
判断是否应该处理消息 判断是否应该处理消息
Args: Args:
new_message: 新消息列表 new_message: 新消息列表
mode: 当前聊天模式 mode: 当前聊天模式
Returns: Returns:
bool: 是否应该处理消息 bool: 是否应该处理消息
""" """
new_message_count = len(new_message) new_message_count = len(new_message)
talk_frequency = self.talk_frequency_control.get_current_talk_frequency() talk_frequency = self.talk_frequency_control.get_current_talk_frequency()
modified_exit_count_threshold = self.focus_energy * 0.5 / talk_frequency modified_exit_count_threshold = self.focus_energy * 0.5 / talk_frequency
modified_exit_interest_threshold = 1.5 / talk_frequency modified_exit_interest_threshold = 1.5 / talk_frequency
total_interest = 0.0 total_interest = 0.0
for msg_dict in new_message: for msg in new_message:
interest_value = msg_dict.get("interest_value") interest_value = msg.interest_value
if interest_value is not None and msg_dict.get("processed_plain_text", ""): if interest_value is not None and msg.processed_plain_text:
total_interest += float(interest_value) total_interest += float(interest_value)
if new_message_count >= modified_exit_count_threshold: if new_message_count >= modified_exit_count_threshold:
self.recent_interest_records.append(total_interest) self.recent_interest_records.append(total_interest)
logger.info( logger.info(
@ -244,9 +242,11 @@ class HeartFChatting:
if new_message_count > 0: if new_message_count > 0:
# 只在兴趣值变化时输出log # 只在兴趣值变化时输出log
if not hasattr(self, "_last_accumulated_interest") or total_interest != self._last_accumulated_interest: if not hasattr(self, "_last_accumulated_interest") or total_interest != self._last_accumulated_interest:
logger.info(f"{self.log_prefix} 休息中,新消息:{new_message_count}条,累计兴趣值: {total_interest:.2f}, 活跃度: {talk_frequency:.1f}") logger.info(
f"{self.log_prefix} 休息中,新消息:{new_message_count}条,累计兴趣值: {total_interest:.2f}, 活跃度: {talk_frequency:.1f}"
)
self._last_accumulated_interest = total_interest self._last_accumulated_interest = total_interest
if total_interest >= modified_exit_interest_threshold: if total_interest >= modified_exit_interest_threshold:
# 记录兴趣度到列表 # 记录兴趣度到列表
self.recent_interest_records.append(total_interest) self.recent_interest_records.append(total_interest)
@ -261,29 +261,25 @@ class HeartFChatting:
f"{self.log_prefix} 已等待{time.time() - self.last_read_time:.0f}秒,累计{new_message_count}条消息,累计兴趣{total_interest:.1f},继续等待..." f"{self.log_prefix} 已等待{time.time() - self.last_read_time:.0f}秒,累计{new_message_count}条消息,累计兴趣{total_interest:.1f},继续等待..."
) )
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
return False,0.0
return False, 0.0
async def _loopbody(self): async def _loopbody(self):
recent_messages_dict = message_api.get_messages_by_time_in_chat( recent_messages_dict = message_api.get_messages_by_time_in_chat(
chat_id=self.stream_id, chat_id=self.stream_id,
start_time=self.last_read_time, start_time=self.last_read_time,
end_time=time.time(), end_time=time.time(),
limit = 10, limit=10,
limit_mode="latest", limit_mode="latest",
filter_mai=True, filter_mai=True,
filter_command=True, filter_command=True,
) )
# TODO: 修复!
from src.common.data_models import temporarily_transform_class_to_dict
temp_recent_messages_dict = [temporarily_transform_class_to_dict(msg) for msg in recent_messages_dict]
# 统一的消息处理逻辑 # 统一的消息处理逻辑
should_process,interest_value = await self._should_process_messages(temp_recent_messages_dict) should_process, interest_value = await self._should_process_messages(recent_messages_dict)
if should_process: if should_process:
self.last_read_time = time.time() self.last_read_time = time.time()
await self._observe(interest_value = interest_value) await self._observe(interest_value=interest_value)
else: else:
# Normal模式消息数量不足等待 # Normal模式消息数量不足等待
@ -298,22 +294,21 @@ class HeartFChatting:
cycle_timers: Dict[str, float], cycle_timers: Dict[str, float],
thinking_id, thinking_id,
actions, actions,
selected_expressions:List[int] = None, selected_expressions: List[int] = None,
) -> Tuple[Dict[str, Any], str, Dict[str, float]]: ) -> Tuple[Dict[str, Any], str, Dict[str, float]]:
with Timer("回复发送", cycle_timers): with Timer("回复发送", cycle_timers):
reply_text = await self._send_response( reply_text = await self._send_response(
reply_set=response_set, reply_set=response_set,
message_data=action_message, message_data=action_message,
selected_expressions=selected_expressions, selected_expressions=selected_expressions,
) )
# 获取 platform如果不存在则从 chat_stream 获取,如果还是 None 则使用默认值 # 获取 platform如果不存在则从 chat_stream 获取,如果还是 None 则使用默认值
platform = action_message.get("chat_info_platform") platform = action_message.get("chat_info_platform")
if platform is None: if platform is None:
platform = getattr(self.chat_stream, "platform", "unknown") platform = getattr(self.chat_stream, "platform", "unknown")
person = Person(platform = platform ,user_id = action_message.get("user_id", "")) person = Person(platform=platform, user_id=action_message.get("user_id", ""))
person_name = person.person_name person_name = person.person_name
action_prompt_display = f"你对{person_name}进行了回复:{reply_text}" action_prompt_display = f"你对{person_name}进行了回复:{reply_text}"
@ -342,12 +337,10 @@ class HeartFChatting:
return loop_info, reply_text, cycle_timers return loop_info, reply_text, cycle_timers
async def _observe(self,interest_value:float = 0.0) -> bool: async def _observe(self, interest_value: float = 0.0) -> bool:
action_type = "no_action" action_type = "no_action"
reply_text = "" # 初始化reply_text变量避免UnboundLocalError reply_text = "" # 初始化reply_text变量避免UnboundLocalError
# 使用sigmoid函数将interest_value转换为概率 # 使用sigmoid函数将interest_value转换为概率
# 当interest_value为0时概率接近0使用Focus模式 # 当interest_value为0时概率接近0使用Focus模式
# 当interest_value很高时概率接近1使用Normal模式 # 当interest_value很高时概率接近1使用Normal模式
@ -360,13 +353,19 @@ class HeartFChatting:
k = 2.0 # 控制曲线陡峭程度 k = 2.0 # 控制曲线陡峭程度
x0 = 1.0 # 控制曲线中心点 x0 = 1.0 # 控制曲线中心点
return 1.0 / (1.0 + math.exp(-k * (interest_val - x0))) return 1.0 / (1.0 + math.exp(-k * (interest_val - x0)))
normal_mode_probability = calculate_normal_mode_probability(interest_value) * 0.5 / self.talk_frequency_control.get_current_talk_frequency() normal_mode_probability = (
calculate_normal_mode_probability(interest_value)
* 0.5
/ self.talk_frequency_control.get_current_talk_frequency()
)
# 根据概率决定使用哪种模式 # 根据概率决定使用哪种模式
if random.random() < normal_mode_probability: if random.random() < normal_mode_probability:
mode = ChatMode.NORMAL mode = ChatMode.NORMAL
logger.info(f"{self.log_prefix} 有兴趣({interest_value:.2f}),在{normal_mode_probability*100:.0f}%概率下选择回复") logger.info(
f"{self.log_prefix} 有兴趣({interest_value:.2f}),在{normal_mode_probability * 100:.0f}%概率下选择回复"
)
else: else:
mode = ChatMode.FOCUS mode = ChatMode.FOCUS
@ -387,10 +386,9 @@ class HeartFChatting:
await hippocampus_manager.build_memory_for_chat(self.stream_id) await hippocampus_manager.build_memory_for_chat(self.stream_id)
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 记忆构建失败: {e}") logger.error(f"{self.log_prefix} 记忆构建失败: {e}")
if random.random() > self.focus_value_control.get_current_focus_value() and mode == ChatMode.FOCUS: if random.random() > self.focus_value_control.get_current_focus_value() and mode == ChatMode.FOCUS:
#如果激活度没有激活并且聊天活跃度低有可能不进行plan相当于不在电脑前不进行认真思考 # 如果激活度没有激活并且聊天活跃度低有可能不进行plan相当于不在电脑前不进行认真思考
actions = [ actions = [
{ {
"action_type": "no_action", "action_type": "no_action",
@ -420,23 +418,21 @@ class HeartFChatting:
): ):
return False return False
with Timer("规划器", cycle_timers): with Timer("规划器", cycle_timers):
actions, _= await self.action_planner.plan( actions, _ = await self.action_planner.plan(
mode=mode, mode=mode,
loop_start_time=self.last_read_time, loop_start_time=self.last_read_time,
available_actions=available_actions, available_actions=available_actions,
) )
# 3. 并行执行所有动作 # 3. 并行执行所有动作
async def execute_action(action_info,actions): async def execute_action(action_info, actions):
"""执行单个动作的通用函数""" """执行单个动作的通用函数"""
try: try:
if action_info["action_type"] == "no_action": if action_info["action_type"] == "no_action":
# 直接处理no_action逻辑不再通过动作系统 # 直接处理no_action逻辑不再通过动作系统
reason = action_info.get("reasoning", "选择不回复") reason = action_info.get("reasoning", "选择不回复")
logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}") logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}")
# 存储no_action信息到数据库 # 存储no_action信息到数据库
await database_api.store_action_info( await database_api.store_action_info(
chat_stream=self.chat_stream, chat_stream=self.chat_stream,
@ -447,13 +443,8 @@ class HeartFChatting:
action_data={"reason": reason}, action_data={"reason": reason},
action_name="no_action", action_name="no_action",
) )
return { return {"action_type": "no_action", "success": True, "reply_text": "", "command": ""}
"action_type": "no_action",
"success": True,
"reply_text": "",
"command": ""
}
elif action_info["action_type"] != "reply": elif action_info["action_type"] != "reply":
# 执行普通动作 # 执行普通动作
with Timer("动作执行", cycle_timers): with Timer("动作执行", cycle_timers):
@ -463,20 +454,19 @@ class HeartFChatting:
action_info["action_data"], action_info["action_data"],
cycle_timers, cycle_timers,
thinking_id, thinking_id,
action_info["action_message"] action_info["action_message"],
) )
return { return {
"action_type": action_info["action_type"], "action_type": action_info["action_type"],
"success": success, "success": success,
"reply_text": reply_text, "reply_text": reply_text,
"command": command "command": command,
} }
else: else:
try: try:
success, response_set, prompt_selected_expressions = await generator_api.generate_reply( success, response_set, prompt_selected_expressions = await generator_api.generate_reply(
chat_stream=self.chat_stream, chat_stream=self.chat_stream,
reply_message = action_info["action_message"], reply_message=action_info["action_message"],
available_actions=available_actions, available_actions=available_actions,
choosen_actions=actions, choosen_actions=actions,
reply_reason=action_info.get("reasoning", ""), reply_reason=action_info.get("reasoning", ""),
@ -485,29 +475,21 @@ class HeartFChatting:
from_plugin=False, from_plugin=False,
return_expressions=True, return_expressions=True,
) )
if prompt_selected_expressions and len(prompt_selected_expressions) > 1: if prompt_selected_expressions and len(prompt_selected_expressions) > 1:
_,selected_expressions = prompt_selected_expressions _, selected_expressions = prompt_selected_expressions
else: else:
selected_expressions = [] selected_expressions = []
if not success or not response_set: if not success or not response_set:
logger.info(f"{action_info['action_message'].get('processed_plain_text')} 的回复生成失败") logger.info(
return { f"{action_info['action_message'].get('processed_plain_text')} 的回复生成失败"
"action_type": "reply", )
"success": False, return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
"reply_text": "",
"loop_info": None
}
except asyncio.CancelledError: except asyncio.CancelledError:
logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消") logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消")
return { return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
"action_type": "reply",
"success": False,
"reply_text": "",
"loop_info": None
}
loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply( loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply(
response_set=response_set, response_set=response_set,
@ -521,7 +503,7 @@ class HeartFChatting:
"action_type": "reply", "action_type": "reply",
"success": True, "success": True,
"reply_text": reply_text, "reply_text": reply_text,
"loop_info": loop_info "loop_info": loop_info,
} }
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 执行动作时出错: {e}") logger.error(f"{self.log_prefix} 执行动作时出错: {e}")
@ -531,26 +513,26 @@ class HeartFChatting:
"success": False, "success": False,
"reply_text": "", "reply_text": "",
"loop_info": None, "loop_info": None,
"error": str(e) "error": str(e),
} }
action_tasks = [asyncio.create_task(execute_action(action,actions)) for action in actions] action_tasks = [asyncio.create_task(execute_action(action, actions)) for action in actions]
# 并行执行所有任务 # 并行执行所有任务
results = await asyncio.gather(*action_tasks, return_exceptions=True) results = await asyncio.gather(*action_tasks, return_exceptions=True)
# 处理执行结果 # 处理执行结果
reply_loop_info = None reply_loop_info = None
reply_text_from_reply = "" reply_text_from_reply = ""
action_success = False action_success = False
action_reply_text = "" action_reply_text = ""
action_command = "" action_command = ""
for i, result in enumerate(results): for i, result in enumerate(results):
if isinstance(result, BaseException): if isinstance(result, BaseException):
logger.error(f"{self.log_prefix} 动作执行异常: {result}") logger.error(f"{self.log_prefix} 动作执行异常: {result}")
continue continue
_cur_action = actions[i] _cur_action = actions[i]
if result["action_type"] != "reply": if result["action_type"] != "reply":
action_success = result["success"] action_success = result["success"]
@ -590,7 +572,6 @@ class HeartFChatting:
}, },
} }
reply_text = action_reply_text reply_text = action_reply_text
if s4u_config.enable_s4u: if s4u_config.enable_s4u:
await stop_typing() await stop_typing()
@ -602,7 +583,7 @@ class HeartFChatting:
# await self.willing_manager.after_generate_reply_handle(message_data.get("message_id", "")) # await self.willing_manager.after_generate_reply_handle(message_data.get("message_id", ""))
action_type = actions[0]["action_type"] if actions else "no_action" action_type = actions[0]["action_type"] if actions else "no_action"
# 管理no_action计数器当执行了非no_action动作时重置计数器 # 管理no_action计数器当执行了非no_action动作时重置计数器
if action_type != "no_action": if action_type != "no_action":
# no_action逻辑已集成到heartFC_chat.py中直接重置计数器 # no_action逻辑已集成到heartFC_chat.py中直接重置计数器
@ -610,7 +591,7 @@ class HeartFChatting:
self.no_action_consecutive = 0 self.no_action_consecutive = 0
logger.debug(f"{self.log_prefix} 执行了{action_type}动作重置no_action计数器") logger.debug(f"{self.log_prefix} 执行了{action_type}动作重置no_action计数器")
return True return True
if action_type == "no_action": if action_type == "no_action":
self.no_action_consecutive += 1 self.no_action_consecutive += 1
self._determine_form_type() self._determine_form_type()
@ -692,11 +673,12 @@ class HeartFChatting:
traceback.print_exc() traceback.print_exc()
return False, "", "" return False, "", ""
async def _send_response(self, async def _send_response(
reply_set, self,
message_data, reply_set,
selected_expressions:List[int] = None, message_data,
) -> str: selected_expressions: List[int] = None,
) -> str:
new_message_count = message_api.count_new_messages( new_message_count = message_api.count_new_messages(
chat_id=self.chat_stream.stream_id, start_time=self.last_read_time, end_time=time.time() chat_id=self.chat_stream.stream_id, start_time=self.last_read_time, end_time=time.time()
) )
@ -714,7 +696,7 @@ class HeartFChatting:
await send_api.text_to_stream( await send_api.text_to_stream(
text=data, text=data,
stream_id=self.chat_stream.stream_id, stream_id=self.chat_stream.stream_id,
reply_message = message_data, reply_message=message_data,
set_reply=need_reply, set_reply=need_reply,
typing=False, typing=False,
selected_expressions=selected_expressions, selected_expressions=selected_expressions,
@ -724,7 +706,7 @@ class HeartFChatting:
await send_api.text_to_stream( await send_api.text_to_stream(
text=data, text=data,
stream_id=self.chat_stream.stream_id, stream_id=self.chat_stream.stream_id,
reply_message = message_data, reply_message=message_data,
set_reply=False, set_reply=False,
typing=True, typing=True,
selected_expressions=selected_expressions, selected_expressions=selected_expressions,

View File

@ -8,6 +8,7 @@ from typing import List, Dict, Optional, Any, Tuple
from src.common.logger import get_logger from src.common.logger import get_logger
from src.common.database.database_model import Expression from src.common.database.database_model import Expression
from src.common.data_models.database_data_model import DatabaseMessages
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.config.config import model_config, global_config from src.config.config import model_config, global_config
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat_inclusive, build_anonymous_messages from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat_inclusive, build_anonymous_messages
@ -346,21 +347,17 @@ class ExpressionLearner:
current_time = time.time() current_time = time.time()
# 获取上次学习时间 # 获取上次学习时间
random_msg_temp = get_raw_msg_by_timestamp_with_chat_inclusive( random_msg = get_raw_msg_by_timestamp_with_chat_inclusive(
chat_id=self.chat_id, chat_id=self.chat_id,
timestamp_start=self.last_learning_time, timestamp_start=self.last_learning_time,
timestamp_end=current_time, timestamp_end=current_time,
limit=num, limit=num,
) )
# TODO: 修复!
from src.common.data_models import temporarily_transform_class_to_dict
random_msg: Optional[List[Dict[str, Any]]] = [temporarily_transform_class_to_dict(msg) for msg in random_msg_temp] if random_msg_temp else None
# print(random_msg) # print(random_msg)
if not random_msg or random_msg == []: if not random_msg or random_msg == []:
return None return None
# 转化成str # 转化成str
chat_id: str = random_msg[0]["chat_id"] chat_id: str = random_msg[0].chat_id
# random_msg_str: str = build_readable_messages(random_msg, timestamp_mode="normal") # random_msg_str: str = build_readable_messages(random_msg, timestamp_mode="normal")
random_msg_str: str = await build_anonymous_messages(random_msg) random_msg_str: str = await build_anonymous_messages(random_msg)
# print(f"random_msg_str:{random_msg_str}") # print(f"random_msg_str:{random_msg_str}")

View File

@ -70,11 +70,9 @@ class ActionModifier:
timestamp=time.time(), timestamp=time.time(),
limit=min(int(global_config.chat.max_context_size * 0.33), 10), limit=min(int(global_config.chat.max_context_size * 0.33), 10),
) )
# TODO: 修复!
from src.common.data_models import temporarily_transform_class_to_dict
temp_msg_list_before_now_half = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now_half]
chat_content = build_readable_messages( chat_content = build_readable_messages(
temp_msg_list_before_now_half, message_list_before_now_half,
replace_bot_name=True, replace_bot_name=True,
merge_messages=False, merge_messages=False,
timestamp_mode="relative", timestamp_mode="relative",

View File

@ -280,11 +280,8 @@ class ActionPlanner:
timestamp=time.time(), timestamp=time.time(),
limit=int(global_config.chat.max_context_size * 0.6), limit=int(global_config.chat.max_context_size * 0.6),
) )
# TODO: 修复!
from src.common.data_models import temporarily_transform_class_to_dict
temp_msg_list_before_now = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now]
chat_content_block, message_id_list = build_readable_messages_with_id( chat_content_block, message_id_list = build_readable_messages_with_id(
messages=temp_msg_list_before_now, messages=message_list_before_now,
timestamp_mode="normal_no_YMD", timestamp_mode="normal_no_YMD",
read_mark=self.last_obs_time_mark, read_mark=self.last_obs_time_mark,
truncate=True, truncate=True,

View File

@ -6,6 +6,7 @@ from typing import List, Dict, Any, Tuple, Optional, Callable
from rich.traceback import install from rich.traceback import install
from src.config.config import global_config from src.config.config import global_config
from src.common.logger import get_logger
from src.common.message_repository import find_messages, count_messages from src.common.message_repository import find_messages, count_messages
from src.common.data_models.database_data_model import DatabaseMessages from src.common.data_models.database_data_model import DatabaseMessages
from src.common.database.database_model import ActionRecords from src.common.database.database_model import ActionRecords
@ -14,6 +15,7 @@ from src.person_info.person_info import Person, get_person_id
from src.chat.utils.utils import translate_timestamp_to_human_readable, assign_message_ids from src.chat.utils.utils import translate_timestamp_to_human_readable, assign_message_ids
install(extra_lines=3) install(extra_lines=3)
logger = get_logger("chat_message_builder")
def replace_user_references_sync( def replace_user_references_sync(
@ -349,7 +351,9 @@ def get_raw_msg_before_timestamp_with_chat(chat_id: str, timestamp: float, limit
return find_messages(message_filter=filter_query, sort=sort_order, limit=limit) return find_messages(message_filter=filter_query, sort=sort_order, limit=limit)
def get_raw_msg_before_timestamp_with_users(timestamp: float, person_ids: list, limit: int = 0) -> List[DatabaseMessages]: def get_raw_msg_before_timestamp_with_users(
timestamp: float, person_ids: list, limit: int = 0
) -> List[DatabaseMessages]:
"""获取指定时间戳之前的消息,按时间升序排序,返回消息列表 """获取指定时间戳之前的消息,按时间升序排序,返回消息列表
limit: 限制返回的消息数量0为不限制 limit: 限制返回的消息数量0为不限制
""" """
@ -776,7 +780,7 @@ async def build_readable_messages_with_list(
def build_readable_messages_with_id( def build_readable_messages_with_id(
messages: List[Dict[str, Any]], messages: List[DatabaseMessages],
replace_bot_name: bool = True, replace_bot_name: bool = True,
merge_messages: bool = False, merge_messages: bool = False,
timestamp_mode: str = "relative", timestamp_mode: str = "relative",
@ -807,7 +811,7 @@ def build_readable_messages_with_id(
def build_readable_messages( def build_readable_messages(
messages: List[Dict[str, Any]], messages: List[DatabaseMessages],
replace_bot_name: bool = True, replace_bot_name: bool = True,
merge_messages: bool = False, merge_messages: bool = False,
timestamp_mode: str = "relative", timestamp_mode: str = "relative",
@ -835,15 +839,15 @@ def build_readable_messages(
if not messages: if not messages:
return "" return ""
copy_messages = [msg.copy() for msg in messages] copy_messages = list(messages)
if show_actions and copy_messages: if show_actions and copy_messages:
# 获取所有消息的时间范围 # 获取所有消息的时间范围
min_time = min(msg.get("time", 0) for msg in copy_messages) min_time = min(msg.time or 0 for msg in copy_messages)
max_time = max(msg.get("time", 0) for msg in copy_messages) max_time = max(msg.time or 0 for msg in copy_messages)
# 从第一条消息中获取chat_id # 从第一条消息中获取chat_id
chat_id = copy_messages[0].get("chat_id") if copy_messages else None chat_id = copy_messages[0].chat_id if copy_messages else None
# 获取这个时间范围内的动作记录并匹配chat_id # 获取这个时间范围内的动作记录并匹配chat_id
actions_in_range = ( actions_in_range = (
@ -883,7 +887,7 @@ def build_readable_messages(
copy_messages.append(action_msg) copy_messages.append(action_msg)
# 重新按时间排序 # 重新按时间排序
copy_messages.sort(key=lambda x: x.get("time", 0)) copy_messages.sort(key=lambda x: x.time or 0)
if read_mark <= 0: if read_mark <= 0:
# 没有有效的 read_mark直接格式化所有消息 # 没有有效的 read_mark直接格式化所有消息
@ -905,8 +909,8 @@ def build_readable_messages(
return formatted_string return formatted_string
else: else:
# 按 read_mark 分割消息 # 按 read_mark 分割消息
messages_before_mark = [msg for msg in copy_messages if msg.get("time", 0) <= read_mark] messages_before_mark = [msg for msg in copy_messages if (msg.time or 0) <= read_mark]
messages_after_mark = [msg for msg in copy_messages if msg.get("time", 0) > read_mark] messages_after_mark = [msg for msg in copy_messages if (msg.time or 0) > read_mark]
# 共享的图片映射字典和计数器 # 共享的图片映射字典和计数器
pic_id_mapping = {} pic_id_mapping = {}
@ -960,13 +964,13 @@ def build_readable_messages(
return "".join(result_parts) return "".join(result_parts)
async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str: async def build_anonymous_messages(messages: List[DatabaseMessages]) -> str:
""" """
构建匿名可读消息将不同人的名称转为唯一占位符ABC...bot自己用SELF 构建匿名可读消息将不同人的名称转为唯一占位符ABC...bot自己用SELF
处理 回复<aaa:bbb> @<aaa:bbb> 字段将bbb映射为匿名占位符 处理 回复<aaa:bbb> @<aaa:bbb> 字段将bbb映射为匿名占位符
""" """
if not messages: if not messages:
print("111111111111没有消息,无法构建匿名消息") logger.warning("没有消息,无法构建匿名消息")
return "" return ""
person_map = {} person_map = {}
@ -1017,14 +1021,9 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str:
for msg in messages: for msg in messages:
try: try:
platform: str = msg.get("chat_info_platform") # type: ignore platform = msg.chat_info.platform
user_id = msg.get("user_id") user_id = msg.user_info.user_id
_timestamp = msg.get("time") content = msg.display_message or msg.processed_plain_text or ""
content: str = ""
if msg.get("display_message"):
content = msg.get("display_message", "")
else:
content = msg.get("processed_plain_text", "")
if "" in content: if "" in content:
content = content.replace("", "") content = content.replace("", "")