From 7438c2dd76a367a5a3ded5b2f462e3a05b10436f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 22:27:44 +0800
Subject: [PATCH 01/28] =?UTF-8?q?refactor(logger):=20=E6=9B=BF=E6=8D=A2pri?=
=?UTF-8?q?nt=E4=B8=BAlogger=E5=B9=B6=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=AE=9A?=
=?UTF-8?q?=E4=B9=89=E6=97=A5=E5=BF=97=E6=A0=B7=E5=BC=8F=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
将配置文件加载中的print语句替换为logger.info,以统一日志输出。同时新增log_decorators.py文件,提供自定义日志样式的装饰器支持,并在logger.py中实现自定义样式处理器的添加和移除功能。
---
src/common/log_decorators.py | 107 ++++++++++++++
src/common/logger.py | 189 +++++++++++++++++-------
src/plugins/knowledge/src/lpmmconfig.py | 5 +-
3 files changed, 245 insertions(+), 56 deletions(-)
create mode 100644 src/common/log_decorators.py
diff --git a/src/common/log_decorators.py b/src/common/log_decorators.py
new file mode 100644
index 00000000..60d9c6cc
--- /dev/null
+++ b/src/common/log_decorators.py
@@ -0,0 +1,107 @@
+import functools
+import inspect
+from typing import Callable, Any
+from .logger import logger, add_custom_style_handler
+
+
+def use_log_style(
+ style_name: str,
+ console_format: str,
+ console_level: str = "INFO",
+ # file_format: Optional[str] = None, # 暂未支持文件输出
+ # file_level: str = "DEBUG",
+) -> Callable:
+ """装饰器:为函数内的日志启用特定的自定义样式。
+
+ Args:
+ style_name (str): 自定义样式的唯一名称。
+ console_format (str): 控制台输出的格式字符串。
+ console_level (str, optional): 控制台日志级别. Defaults to "INFO".
+ # file_format (Optional[str], optional): 文件输出格式 (暂未支持). Defaults to None.
+ # file_level (str, optional): 文件日志级别 (暂未支持). Defaults to "DEBUG".
+
+ Returns:
+ Callable: 返回装饰器本身。
+ """
+
+ def decorator(func: Callable) -> Callable:
+ # 获取被装饰函数所在的模块名
+ module = inspect.getmodule(func)
+ if module is None:
+ # 如果无法获取模块(例如,在交互式解释器中定义函数),则使用默认名称
+ module_name = "unknown_module"
+ logger.warning(f"无法确定函数 {func.__name__} 的模块,将使用 '{module_name}'")
+ else:
+ module_name = module.__name__
+
+ # 在函数首次被调用(或模块加载时)确保自定义处理器已添加
+ # 注意:这会在模块加载时执行,而不是每次函数调用时
+ # print(f"Setting up custom style '{style_name}' for module '{module_name}' in decorator definition")
+ add_custom_style_handler(
+ module_name=module_name,
+ style_name=style_name,
+ console_format=console_format,
+ console_level=console_level,
+ # file_format=file_format,
+ # file_level=file_level,
+ )
+
+ @functools.wraps(func)
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
+ # 创建绑定了模块名和自定义样式标记的 logger 实例
+ custom_logger = logger.bind(module=module_name, custom_style=style_name)
+ # print(f"Executing {func.__name__} with custom logger for style '{style_name}'")
+ # 将自定义 logger 作为第一个参数传递给原函数
+ # 注意:这要求被装饰的函数第一个参数用于接收 logger
+ try:
+ return func(custom_logger, *args, **kwargs)
+ except TypeError as e:
+ # 捕获可能的类型错误,比如原函数不接受 logger 参数
+ logger.error(
+ f"调用 {func.__name__} 时出错:请确保该函数接受一个 logger 实例作为其第一个参数。错误:{e}"
+ )
+ # 可以选择重新抛出异常或返回特定值
+ raise e
+
+ return wrapper
+
+ return decorator
+
+
+# --- 示例用法 (可以在其他模块中这样使用) ---
+
+# # 假设这是你的模块 my_module.py
+# from src.common.log_decorators import use_log_style
+# from src.common.logger import get_module_logger, LoguruLogger
+
+# # 获取模块的标准 logger
+# standard_logger = get_module_logger(__name__)
+
+# # 定义一个自定义样式
+# MY_SPECIAL_STYLE = "special"
+# MY_SPECIAL_FORMAT = " SPECIAL [{time:HH:mm:ss}] | {message}"
+
+# @use_log_style(style_name=MY_SPECIAL_STYLE, console_format=MY_SPECIAL_FORMAT)
+# def my_function_with_special_logs(custom_logger: LoguruLogger, x: int, y: int):
+# standard_logger.info("这是一条使用标准格式的日志")
+# custom_logger.info(f"开始执行特殊操作,参数: x={x}, y={y}")
+# result = x + y
+# custom_logger.success(f"特殊操作完成,结果: {result}")
+# standard_logger.info("标准格式日志:函数即将结束")
+# return result
+
+# @use_log_style(style_name="another_style", console_format="任务: {message}")
+# def another_task(task_logger: LoguruLogger, task_name: str):
+# standard_logger.debug("准备执行另一个任务")
+# task_logger.info(f"正在处理任务 '{task_name}'")
+# # ... 执行任务 ...
+# task_logger.warning("任务处理中遇到一个警告")
+# standard_logger.info("另一个任务的标准日志")
+
+# if __name__ == "__main__":
+# print("\n--- 调用 my_function_with_special_logs ---")
+# my_function_with_special_logs(10, 5)
+# print("\n--- 调用 another_task ---")
+# another_task("数据清理")
+# print("\n--- 单独使用标准 logger ---")
+# standard_logger.info("这是一条完全独立的标准日志")
\ No newline at end of file
diff --git a/src/common/logger.py b/src/common/logger.py
index 2b220841..cbc6c149 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -1,5 +1,5 @@
from loguru import logger
-from typing import Dict, Optional, Union, List
+from typing import Dict, Optional, Union, List, Tuple
import sys
import os
from types import ModuleType
@@ -26,6 +26,7 @@ LoguruLogger = logger.__class__
# 全局注册表:记录模块与处理器ID的映射
_handler_registry: Dict[str, List[int]] = {}
+_custom_style_handlers: Dict[Tuple[str, str], List[int]] = {} # 记录自定义样式处理器ID
# 获取日志存储根地址
current_file_path = Path(__file__).resolve()
@@ -42,8 +43,7 @@ if not SIMPLE_OUTPUT:
"file_level": "DEBUG",
# 格式配置
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
"{extra[module]: <12} | "
"{message}"
),
@@ -59,7 +59,7 @@ else:
"console_level": "INFO",
"file_level": "DEBUG",
# 格式配置
- "console_format": "{time:MM-DD HH:mm} | {extra[module]} | {message}",
+ "console_format": "{time:MM-DD HH:mm} | {extra[module]} | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | {message}",
"log_dir": LOG_ROOT,
"rotation": "00:00",
@@ -72,8 +72,8 @@ else:
MEMORY_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"海马体 | "
"{message}"
@@ -82,7 +82,7 @@ MEMORY_STYLE_CONFIG = {
},
"simple": {
"console_format": (
- "{time:MM-DD HH:mm} | 海马体 | {message}"
+ "{time:MM-DD HH:mm} | 海马体 | {message}"
),
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 海马体 | {message}",
},
@@ -92,8 +92,8 @@ MEMORY_STYLE_CONFIG = {
PFC_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"PFC | "
"{message}"
@@ -102,7 +102,7 @@ PFC_STYLE_CONFIG = {
},
"simple": {
"console_format": (
- "{time:MM-DD HH:mm} | PFC | {message}"
+ "{time:MM-DD HH:mm} | PFC | {message}"
),
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | PFC | {message}",
},
@@ -112,8 +112,8 @@ PFC_STYLE_CONFIG = {
MOOD_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"心情 | "
"{message}"
@@ -121,7 +121,7 @@ MOOD_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 心情 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 心情 | {message}",
+ "console_format": "{time:MM-DD HH:mm} | 心情 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 心情 | {message}",
},
}
@@ -129,8 +129,8 @@ MOOD_STYLE_CONFIG = {
TOOL_USE_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"工具使用 | "
"{message}"
@@ -138,7 +138,7 @@ TOOL_USE_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 工具使用 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 工具使用 | {message}",
+ "console_format": "{time:MM-DD HH:mm} | 工具使用 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 工具使用 | {message}",
},
}
@@ -148,8 +148,8 @@ TOOL_USE_STYLE_CONFIG = {
RELATION_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"关系 | "
"{message}"
@@ -157,7 +157,7 @@ RELATION_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 关系 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 关系 | {message}",
+ "console_format": "{time:MM-DD HH:mm} | 关系 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 关系 | {message}",
},
}
@@ -166,8 +166,8 @@ RELATION_STYLE_CONFIG = {
CONFIG_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"配置 | "
"{message}"
@@ -175,7 +175,7 @@ CONFIG_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 配置 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 配置 | {message}",
+ "console_format": "{time:MM-DD HH:mm} | 配置 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 配置 | {message}",
},
}
@@ -183,8 +183,8 @@ CONFIG_STYLE_CONFIG = {
SENDER_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"消息发送 | "
"{message}"
@@ -192,7 +192,7 @@ SENDER_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 消息发送 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 消息发送 | {message}",
+ "console_format": "{time:MM-DD HH:mm} | 消息发送 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 消息发送 | {message}",
},
}
@@ -200,8 +200,8 @@ SENDER_STYLE_CONFIG = {
HEARTFLOW_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"麦麦大脑袋 | "
"{message}"
@@ -210,7 +210,7 @@ HEARTFLOW_STYLE_CONFIG = {
},
"simple": {
"console_format": (
- "{time:MM-DD HH:mm} | 麦麦大脑袋 | {message}"
+ "{time:MM-DD HH:mm} | 麦麦大脑袋 | {message}"
), # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦大脑袋 | {message}",
},
@@ -219,8 +219,8 @@ HEARTFLOW_STYLE_CONFIG = {
SCHEDULE_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"在干嘛 | "
"{message}"
@@ -228,7 +228,7 @@ SCHEDULE_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 在干嘛 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 在干嘛 | {message}",
+ "console_format": "{time:MM-DD HH:mm} | 在干嘛 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 在干嘛 | {message}",
},
}
@@ -236,8 +236,8 @@ SCHEDULE_STYLE_CONFIG = {
LLM_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"麦麦组织语言 | "
"{message}"
@@ -245,7 +245,7 @@ LLM_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦组织语言 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 麦麦组织语言 | {message}",
+ "console_format": "{time:MM-DD HH:mm} | 麦麦组织语言 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦组织语言 | {message}",
},
}
@@ -255,8 +255,8 @@ LLM_STYLE_CONFIG = {
TOPIC_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"话题 | "
"{message}"
@@ -264,7 +264,7 @@ TOPIC_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 话题 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 主题 | {message}",
+ "console_format": "{time:MM-DD HH:mm} | 主题 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 话题 | {message}",
},
}
@@ -273,8 +273,8 @@ TOPIC_STYLE_CONFIG = {
CHAT_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"见闻 | "
"{message}"
@@ -283,7 +283,7 @@ CHAT_STYLE_CONFIG = {
},
"simple": {
"console_format": (
- "{time:MM-DD HH:mm} | 见闻 | {message}"
+ "{time:MM-DD HH:mm} | 见闻 | {message}"
), # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 见闻 | {message}",
},
@@ -292,8 +292,8 @@ CHAT_STYLE_CONFIG = {
SUB_HEARTFLOW_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"麦麦小脑袋 | "
"{message}"
@@ -302,7 +302,7 @@ SUB_HEARTFLOW_STYLE_CONFIG = {
},
"simple": {
"console_format": (
- "{time:MM-DD HH:mm} | 麦麦小脑袋 | {message}"
+ "{time:MM-DD HH:mm} | 麦麦小脑袋 | {message}"
), # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦小脑袋 | {message}",
},
@@ -311,8 +311,8 @@ SUB_HEARTFLOW_STYLE_CONFIG = {
WILLING_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"意愿 | "
"{message}"
@@ -320,7 +320,7 @@ WILLING_STYLE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 意愿 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 意愿 | {message} ", # noqa: E501
+ "console_format": "{time:MM-DD HH:mm} | 意愿 | {message} ", # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 意愿 | {message}",
},
}
@@ -329,8 +329,8 @@ WILLING_STYLE_CONFIG = {
MAI_STATE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"麦麦状态 | "
"{message}"
@@ -338,7 +338,7 @@ MAI_STATE_CONFIG = {
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦状态 | {message}",
},
"simple": {
- "console_format": "{time:MM-DD HH:mm} | 麦麦状态 | {message} ", # noqa: E501
+ "console_format": "{time:MM-DD HH:mm} | 麦麦状态 | {message} ", # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦状态 | {message}",
},
}
@@ -347,8 +347,8 @@ MAI_STATE_CONFIG = {
LPMM_STYLE_CONFIG = {
"advanced": {
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
"{extra[module]: <12} | "
"LPMM | "
"{message}"
@@ -357,7 +357,7 @@ LPMM_STYLE_CONFIG = {
},
"simple": {
"console_format": (
- "{time:MM-DD HH:mm} | LPMM | {message}"
+ "{time:MM-DD HH:mm} | LPMM | {message}"
),
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | LPMM | {message}",
},
@@ -451,7 +451,7 @@ def get_module_logger(
sink=sys.stderr,
level=os.getenv("CONSOLE_LOG_LEVEL", console_level or current_config["console_level"]),
format=current_config["console_format"],
- filter=lambda record: record["extra"].get("module") == module_name,
+ filter=lambda record: record["extra"].get("module") == module_name and 'custom_style' not in record['extra'],
enqueue=True,
)
handler_ids.append(console_id)
@@ -470,7 +470,7 @@ def get_module_logger(
retention=current_config["retention"],
compression=current_config["compression"],
encoding="utf-8",
- filter=lambda record: record["extra"].get("module") == module_name,
+ filter=lambda record: record["extra"].get("module") == module_name and 'custom_style' not in record['extra'],
enqueue=True,
)
handler_ids.append(file_id)
@@ -487,6 +487,87 @@ def get_module_logger(
return logger.bind(module=module_name)
+def add_custom_style_handler(
+ module_name: str,
+ style_name: str,
+ console_format: str,
+ console_level: str = "INFO",
+ # file_format: Optional[str] = None, # 暂时只支持控制台
+ # file_level: str = "DEBUG",
+ # config: Optional[LogConfig] = None, # 暂时不使用全局配置
+) -> None:
+ """为指定模块和样式名添加自定义日志处理器(目前仅支持控制台)."""
+ handler_key = (module_name, style_name)
+
+ # 如果已存在该模块和样式的处理器,则不重复添加
+ if handler_key in _custom_style_handlers:
+ # print(f"Custom handler for {handler_key} already exists.")
+ return
+
+ handler_ids = []
+
+ # 添加自定义控制台处理器
+ try:
+ custom_console_id = logger.add(
+ sink=sys.stderr,
+ level=os.getenv(f"{module_name.upper()}_{style_name.upper()}_CONSOLE_LEVEL", console_level),
+ format=console_format,
+ filter=lambda record: record["extra"].get("module") == module_name
+ and record["extra"].get("custom_style") == style_name,
+ enqueue=True,
+ )
+ handler_ids.append(custom_console_id)
+ # print(f"Added custom console handler {custom_console_id} for {handler_key}")
+ except Exception as e:
+ logger.error(f"Failed to add custom console handler for {handler_key}: {e}")
+ # 如果添加失败,确保列表为空,避免记录不存在的ID
+ handler_ids = []
+
+ # # 文件处理器 (可选,按需启用)
+ # if file_format:
+ # current_config = config.config if config else DEFAULT_CONFIG
+ # log_dir = Path(current_config["log_dir"])
+ # log_dir.mkdir(parents=True, exist_ok=True)
+ # # 可以考虑将自定义样式的日志写入单独文件或模块主文件
+ # log_file = log_dir / module_name / f"{style_name}_{{time:YYYY-MM-DD}}.log"
+ # log_file.parent.mkdir(parents=True, exist_ok=True)
+ # try:
+ # custom_file_id = logger.add(
+ # sink=str(log_file),
+ # level=os.getenv(f"{module_name.upper()}_{style_name.upper()}_FILE_LEVEL", file_level),
+ # format=file_format,
+ # rotation=current_config["rotation"],
+ # retention=current_config["retention"],
+ # compression=current_config["compression"],
+ # encoding="utf-8",
+ # filter=lambda record: record["extra"].get("module") == module_name
+ # and record["extra"].get("custom_style") == style_name,
+ # enqueue=True,
+ # )
+ # handler_ids.append(custom_file_id)
+ # except Exception as e:
+ # logger.error(f"Failed to add custom file handler for {handler_key}: {e}")
+
+ # 更新自定义处理器注册表
+ if handler_ids:
+ _custom_style_handlers[handler_key] = handler_ids
+
+
+def remove_custom_style_handler(module_name: str, style_name: str) -> None:
+ """移除指定模块和样式名的自定义日志处理器."""
+ handler_key = (module_name, style_name)
+ if handler_key in _custom_style_handlers:
+ for handler_id in _custom_style_handlers[handler_key]:
+ try:
+ logger.remove(handler_id)
+ # print(f"Removed custom handler {handler_id} for {handler_key}")
+ except ValueError:
+ # 可能已经被移除或不存在
+ # print(f"Handler {handler_id} for {handler_key} already removed or invalid.")
+ pass
+ del _custom_style_handlers[handler_key]
+
+
def remove_module_logger(module_name: str) -> None:
"""清理指定模块的日志处理器"""
if module_name in _handler_registry:
diff --git a/src/plugins/knowledge/src/lpmmconfig.py b/src/plugins/knowledge/src/lpmmconfig.py
index 7f59bc89..753562f4 100644
--- a/src/plugins/knowledge/src/lpmmconfig.py
+++ b/src/plugins/knowledge/src/lpmmconfig.py
@@ -2,6 +2,7 @@ import os
import toml
import sys
import argparse
+from .global_logger import logger
PG_NAMESPACE = "paragraph"
ENT_NAMESPACE = "entity"
@@ -63,8 +64,8 @@ def _load_config(config, config_file_path):
if "persistence" in file_config:
config["persistence"] = file_config["persistence"]
- print(config)
- print("Configurations loaded from file: ", config_file_path)
+ # print(config)
+ logger.info(f"从文件中读取配置: {config_file_path}")
parser = argparse.ArgumentParser(description="Configurations for the pipeline")
From b0b05d532bf3553a5f641526fd6147248bb11f76 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 14:27:57 +0000
Subject: [PATCH 02/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/common/log_decorators.py | 2 +-
src/common/logger.py | 10 ++++------
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/common/log_decorators.py b/src/common/log_decorators.py
index 60d9c6cc..9838717f 100644
--- a/src/common/log_decorators.py
+++ b/src/common/log_decorators.py
@@ -104,4 +104,4 @@ def use_log_style(
# print("\n--- 调用 another_task ---")
# another_task("数据清理")
# print("\n--- 单独使用标准 logger ---")
-# standard_logger.info("这是一条完全独立的标准日志")
\ No newline at end of file
+# standard_logger.info("这是一条完全独立的标准日志")
diff --git a/src/common/logger.py b/src/common/logger.py
index cbc6c149..df075d47 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -26,7 +26,7 @@ LoguruLogger = logger.__class__
# 全局注册表:记录模块与处理器ID的映射
_handler_registry: Dict[str, List[int]] = {}
-_custom_style_handlers: Dict[Tuple[str, str], List[int]] = {} # 记录自定义样式处理器ID
+_custom_style_handlers: Dict[Tuple[str, str], List[int]] = {} # 记录自定义样式处理器ID
# 获取日志存储根地址
current_file_path = Path(__file__).resolve()
@@ -43,9 +43,7 @@ if not SIMPLE_OUTPUT:
"file_level": "DEBUG",
# 格式配置
"console_format": (
- "{time:YYYY-MM-DD HH:mm:ss} | "
- "{extra[module]: <12} | "
- "{message}"
+ "{time:YYYY-MM-DD HH:mm:ss} | {extra[module]: <12} | {message}"
),
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | {message}",
"log_dir": LOG_ROOT,
@@ -451,7 +449,7 @@ def get_module_logger(
sink=sys.stderr,
level=os.getenv("CONSOLE_LOG_LEVEL", console_level or current_config["console_level"]),
format=current_config["console_format"],
- filter=lambda record: record["extra"].get("module") == module_name and 'custom_style' not in record['extra'],
+ filter=lambda record: record["extra"].get("module") == module_name and "custom_style" not in record["extra"],
enqueue=True,
)
handler_ids.append(console_id)
@@ -470,7 +468,7 @@ def get_module_logger(
retention=current_config["retention"],
compression=current_config["compression"],
encoding="utf-8",
- filter=lambda record: record["extra"].get("module") == module_name and 'custom_style' not in record['extra'],
+ filter=lambda record: record["extra"].get("module") == module_name and "custom_style" not in record["extra"],
enqueue=True,
)
handler_ids.append(file_id)
From 44a7058985d31644ab7497f8575c2181a09c35bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 22:43:17 +0800
Subject: [PATCH 03/28] =?UTF-8?q?refactor(common):=20=E4=BC=98=E5=8C=96=20?=
=?UTF-8?q?SIMPLE=5FOUTPUT=20=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E8=AF=BB?=
=?UTF-8?q?=E5=8F=96=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将 SIMPLE_OUTPUT环境变量的值转换为布尔型
- 提高代码可读性和健壮性
---
src/common/logger.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/common/logger.py b/src/common/logger.py
index df075d47..740da43b 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -33,6 +33,10 @@ current_file_path = Path(__file__).resolve()
LOG_ROOT = "logs"
SIMPLE_OUTPUT = os.getenv("SIMPLE_OUTPUT", "false")
+if SIMPLE_OUTPUT == "true" or SIMPLE_OUTPUT == "True":
+ SIMPLE_OUTPUT = True
+else:
+ SIMPLE_OUTPUT = False
print(f"SIMPLE_OUTPUT: {SIMPLE_OUTPUT}")
if not SIMPLE_OUTPUT:
From 95cacee2bd506ce62065c002c93c661e4798fb96 Mon Sep 17 00:00:00 2001
From: UnCLAS-Prommer
Date: Wed, 23 Apr 2025 22:48:12 +0800
Subject: [PATCH 04/28] 1
---
src/common/logger.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/common/logger.py b/src/common/logger.py
index 740da43b..017f60fe 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -32,8 +32,8 @@ _custom_style_handlers: Dict[Tuple[str, str], List[int]] = {} # 记录自定义
current_file_path = Path(__file__).resolve()
LOG_ROOT = "logs"
-SIMPLE_OUTPUT = os.getenv("SIMPLE_OUTPUT", "false")
-if SIMPLE_OUTPUT == "true" or SIMPLE_OUTPUT == "True":
+SIMPLE_OUTPUT = os.getenv("SIMPLE_OUTPUT", "false").strip().lower()
+if SIMPLE_OUTPUT == "true":
SIMPLE_OUTPUT = True
else:
SIMPLE_OUTPUT = False
From f47d2a8b4f8f6cdfb6652d9c424e68c6a8174b64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 22:50:17 +0800
Subject: [PATCH 05/28] =?UTF-8?q?refactor(logger):=20=E7=A7=BB=E9=99=A4?=
=?UTF-8?q?=E6=97=A5=E5=BF=97=E6=A0=BC=E5=BC=8F=E4=B8=AD=E7=9A=84=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97=E4=BF=A1=E6=81=AF=E4=BB=A5=E7=AE=80=E5=8C=96=E8=BE=93?=
=?UTF-8?q?=E5=87=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
移除日志格式中的`{extra[module]}`字段,使日志输出更加简洁且易于阅读
---
src/common/logger.py | 63 ++++++++++++++++++++++----------------------
1 file changed, 31 insertions(+), 32 deletions(-)
diff --git a/src/common/logger.py b/src/common/logger.py
index 740da43b..99e28254 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -75,8 +75,7 @@ MEMORY_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
"海马体 | "
"{message}"
),
@@ -95,8 +94,8 @@ PFC_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"PFC | "
"{message}"
),
@@ -115,8 +114,8 @@ MOOD_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"心情 | "
"{message}"
),
@@ -132,8 +131,8 @@ TOOL_USE_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"工具使用 | "
"{message}"
),
@@ -151,8 +150,8 @@ RELATION_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"关系 | "
"{message}"
),
@@ -169,8 +168,8 @@ CONFIG_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"配置 | "
"{message}"
),
@@ -186,8 +185,8 @@ SENDER_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"消息发送 | "
"{message}"
),
@@ -203,8 +202,8 @@ HEARTFLOW_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"麦麦大脑袋 | "
"{message}"
),
@@ -222,8 +221,8 @@ SCHEDULE_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"在干嘛 | "
"{message}"
),
@@ -239,8 +238,8 @@ LLM_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"麦麦组织语言 | "
"{message}"
),
@@ -258,8 +257,8 @@ TOPIC_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"话题 | "
"{message}"
),
@@ -276,8 +275,8 @@ CHAT_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"见闻 | "
"{message}"
),
@@ -295,8 +294,8 @@ SUB_HEARTFLOW_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"麦麦小脑袋 | "
"{message}"
),
@@ -314,8 +313,8 @@ WILLING_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"意愿 | "
"{message}"
),
@@ -332,8 +331,8 @@ MAI_STATE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"麦麦状态 | "
"{message}"
),
@@ -350,8 +349,8 @@ LPMM_STYLE_CONFIG = {
"advanced": {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
- "{level: <8} | "
- "{extra[module]: <12} | "
+ "{level: <8} | "
+
"LPMM | "
"{message}"
),
From 2a424d087c3ec2e98df185d6919ae1011815fa68 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 14:50:38 +0000
Subject: [PATCH 06/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/common/logger.py | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/src/common/logger.py b/src/common/logger.py
index e139cc02..b6d690cd 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -95,7 +95,6 @@ PFC_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"PFC | "
"{message}"
),
@@ -115,7 +114,6 @@ MOOD_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"心情 | "
"{message}"
),
@@ -132,7 +130,6 @@ TOOL_USE_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"工具使用 | "
"{message}"
),
@@ -151,7 +148,6 @@ RELATION_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"关系 | "
"{message}"
),
@@ -169,7 +165,6 @@ CONFIG_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"配置 | "
"{message}"
),
@@ -186,7 +181,6 @@ SENDER_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"消息发送 | "
"{message}"
),
@@ -203,7 +197,6 @@ HEARTFLOW_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"麦麦大脑袋 | "
"{message}"
),
@@ -222,7 +215,6 @@ SCHEDULE_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"在干嘛 | "
"{message}"
),
@@ -239,7 +231,6 @@ LLM_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"麦麦组织语言 | "
"{message}"
),
@@ -258,7 +249,6 @@ TOPIC_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"话题 | "
"{message}"
),
@@ -276,7 +266,6 @@ CHAT_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"见闻 | "
"{message}"
),
@@ -295,7 +284,6 @@ SUB_HEARTFLOW_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"麦麦小脑袋 | "
"{message}"
),
@@ -314,7 +302,6 @@ WILLING_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"意愿 | "
"{message}"
),
@@ -332,7 +319,6 @@ MAI_STATE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"麦麦状态 | "
"{message}"
),
@@ -350,7 +336,6 @@ LPMM_STYLE_CONFIG = {
"console_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
-
"LPMM | "
"{message}"
),
From c727929bcfe796dd984f015652b558550ebc72ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 23:04:30 +0800
Subject: [PATCH 07/28] =?UTF-8?q?refactor(logger):=20=E4=BC=98=E5=8C=96?=
=?UTF-8?q?=E6=97=A5=E5=BF=97=E9=85=8D=E7=BD=AE=E4=BB=A5=E5=A2=9E=E5=BC=BA?=
=?UTF-8?q?=E5=8F=AF=E8=AF=BB=E6=80=A7=E5=92=8C=E4=B8=80=E8=87=B4=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
重构日志模块,引入MAIN_STYLE_CONFIG和INTEREST_STYLE_CONFIG配置,统一日志格式。通过LogConfig类传递配置参数,确保日志输出在不同模块中保持一致性和可读性。
---
src/common/logger.py | 39 +++++++++++++++++++++++++++++++
src/heart_flow/interest_logger.py | 8 +++++--
src/main.py | 8 +++++--
3 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/src/common/logger.py b/src/common/logger.py
index e139cc02..7818ae15 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -70,6 +70,24 @@ else:
}
+MAIN_STYLE_CONFIG = {
+ "advanced": {
+ "console_format": (
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
+ "主程序 | "
+ "{message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 主程序 | {message}",
+ },
+ "simple": {
+ "console_format": (
+ "{time:MM-DD HH:mm} | 主程序 | {message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 主程序 | {message}",
+ },
+}
+
# 海马体日志样式配置
MEMORY_STYLE_CONFIG = {
"advanced": {
@@ -364,12 +382,32 @@ LPMM_STYLE_CONFIG = {
},
}
+# 兴趣log
+INTEREST_STYLE_CONFIG = {
+ "advanced": {
+ "console_format": (
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
+ "兴趣 | "
+ "{message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 兴趣 | {message}",
+ },
+ "simple": {
+ "console_format": (
+ "{time:MM-DD HH:mm} | 兴趣 | {message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 兴趣 | {message}",
+ },
+}
+
CONFIRM_STYLE_CONFIG = {
"console_format": "{message}", # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | EULA与PRIVACY确认 | {message}",
}
# 根据SIMPLE_OUTPUT选择配置
+MAIN_STYLE_CONFIG = MAIN_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MAIN_STYLE_CONFIG["advanced"]
MEMORY_STYLE_CONFIG = MEMORY_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MEMORY_STYLE_CONFIG["advanced"]
TOPIC_STYLE_CONFIG = TOPIC_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else TOPIC_STYLE_CONFIG["advanced"]
SENDER_STYLE_CONFIG = SENDER_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SENDER_STYLE_CONFIG["advanced"]
@@ -388,6 +426,7 @@ CONFIG_STYLE_CONFIG = CONFIG_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else CONFIG
TOOL_USE_STYLE_CONFIG = TOOL_USE_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else TOOL_USE_STYLE_CONFIG["advanced"]
PFC_STYLE_CONFIG = PFC_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else PFC_STYLE_CONFIG["advanced"]
LPMM_STYLE_CONFIG = LPMM_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else LPMM_STYLE_CONFIG["advanced"]
+INTEREST_STYLE_CONFIG = INTEREST_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else INTEREST_STYLE_CONFIG["advanced"]
def is_registered_module(record: dict) -> bool:
diff --git a/src/heart_flow/interest_logger.py b/src/heart_flow/interest_logger.py
index 3833ef88..206cfa4b 100644
--- a/src/heart_flow/interest_logger.py
+++ b/src/heart_flow/interest_logger.py
@@ -5,7 +5,7 @@ import os
import traceback
from typing import TYPE_CHECKING, Dict, List
-from src.common.logger import get_module_logger
+from src.common.logger import get_module_logger, LogConfig, INTEREST_STYLE_CONFIG
# Need chat_manager to get stream names
from src.plugins.chat.chat_stream import chat_manager
@@ -14,7 +14,11 @@ if TYPE_CHECKING:
from src.heart_flow.subheartflow_manager import SubHeartflowManager
from src.heart_flow.sub_heartflow import SubHeartflow # For type hint in get_interest_states
-logger = get_module_logger("interest_logger")
+interest_logger_config = LogConfig(
+ console_format = INTEREST_STYLE_CONFIG["console_format"],
+ file_format=INTEREST_STYLE_CONFIG["file_format"],
+)
+logger = get_module_logger("interest_logger", config=interest_logger_config)
# Consider moving log directory/filename constants here
LOG_DIRECTORY = "logs/interest"
diff --git a/src/main.py b/src/main.py
index 75ab2ae7..62fa70a6 100644
--- a/src/main.py
+++ b/src/main.py
@@ -13,12 +13,16 @@ from .plugins.chat.message_sender import message_manager
from .plugins.storage.storage import MessageStorage
from .config.config import global_config
from .plugins.chat.bot import chat_bot
-from .common.logger import get_module_logger
+from .common.logger import get_module_logger, LogConfig, MAIN_STYLE_CONFIG
from .plugins.remote import heartbeat_thread # noqa: F401
from .individuality.individuality import Individuality
from .common.server import global_server
-logger = get_module_logger("main")
+main_log_config = LogConfig(
+ console_format=MAIN_STYLE_CONFIG["console_format"],
+ file_format=MAIN_STYLE_CONFIG["file_format"],
+)
+logger = get_module_logger("main", config=main_log_config)
class MainSystem:
From b4f37d04fec63aad7ec4fa2560d3ef89ce9cd572 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 15:04:54 +0000
Subject: [PATCH 08/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/heart_flow/interest_logger.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/heart_flow/interest_logger.py b/src/heart_flow/interest_logger.py
index 206cfa4b..4b7e6323 100644
--- a/src/heart_flow/interest_logger.py
+++ b/src/heart_flow/interest_logger.py
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
from src.heart_flow.sub_heartflow import SubHeartflow # For type hint in get_interest_states
interest_logger_config = LogConfig(
- console_format = INTEREST_STYLE_CONFIG["console_format"],
+ console_format=INTEREST_STYLE_CONFIG["console_format"],
file_format=INTEREST_STYLE_CONFIG["file_format"],
)
logger = get_module_logger("interest_logger", config=interest_logger_config)
From 773378292952b361fe73461bb76ef177714f2a42 Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Wed, 23 Apr 2025 23:08:19 +0800
Subject: [PATCH 09/28] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8Dhfc?=
=?UTF-8?q?=E6=A0=B9=E6=9C=AC=E4=B8=8D=E4=BC=9A=E8=A2=AB=E8=A7=A6=E5=8F=91?=
=?UTF-8?q?=E7=9A=84bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/heart_flow/background_tasks.py | 123 +++--
src/heart_flow/heartflow.py | 4 +-
src/heart_flow/interest_logger.py | 148 +++---
src/heart_flow/mai_state_manager.py | 18 +-
src/heart_flow/sub_heartflow.py | 460 ++++++++++--------
src/heart_flow/subheartflow_manager.py | 105 ++--
src/plugins/chat/bot.py | 2 +-
.../only_message_process.py | 0
src/plugins/heartFC_chat/heartFC_chat.py | 145 +++---
src/plugins/heartFC_chat/heartFC_generator.py | 2 +-
.../heartFC_chat/heartflow_processor.py | 34 +-
src/plugins/heartFC_chat/normal_chat.py | 48 +-
.../heartFC_chat/normal_chat_generator.py | 4 +-
13 files changed, 621 insertions(+), 472 deletions(-)
rename src/plugins/{chat_module/only_process => chat}/only_message_process.py (100%)
diff --git a/src/heart_flow/background_tasks.py b/src/heart_flow/background_tasks.py
index f4f74732..3a280f29 100644
--- a/src/heart_flow/background_tasks.py
+++ b/src/heart_flow/background_tasks.py
@@ -6,11 +6,14 @@ from src.common.logger import get_module_logger
# Need manager types for dependency injection
from src.heart_flow.mai_state_manager import MaiStateManager, MaiStateInfo
-from src.heart_flow.subheartflow_manager import SubHeartflowManager
+from src.heart_flow.subheartflow_manager import SubHeartflowManager, ChatState
from src.heart_flow.interest_logger import InterestLogger
logger = get_module_logger("background_tasks")
+# 新增兴趣评估间隔
+INTEREST_EVAL_INTERVAL_SECONDS = 5
+
class BackgroundTaskManager:
"""管理 Heartflow 的后台周期性任务。"""
@@ -25,6 +28,8 @@ class BackgroundTaskManager:
cleanup_interval: int,
log_interval: int,
inactive_threshold: int,
+ # 新增兴趣评估间隔参数
+ interest_eval_interval: int = INTEREST_EVAL_INTERVAL_SECONDS,
):
self.mai_state_info = mai_state_info
self.mai_state_manager = mai_state_manager
@@ -36,46 +41,70 @@ class BackgroundTaskManager:
self.cleanup_interval = cleanup_interval
self.log_interval = log_interval
self.inactive_threshold = inactive_threshold # For cleanup task
+ self.interest_eval_interval = interest_eval_interval # 存储兴趣评估间隔
# Task references
self._state_update_task: Optional[asyncio.Task] = None
self._cleanup_task: Optional[asyncio.Task] = None
self._logging_task: Optional[asyncio.Task] = None
+ self._interest_eval_task: Optional[asyncio.Task] = None # 新增兴趣评估任务引用
self._tasks: List[Optional[asyncio.Task]] = [] # Keep track of all tasks
async def start_tasks(self):
- """启动所有后台任务"""
- # 状态更新任务
- if self._state_update_task is None or self._state_update_task.done():
- self._state_update_task = asyncio.create_task(
- self._run_state_update_cycle(self.update_interval), name="hf_state_update"
- )
- self._tasks.append(self._state_update_task)
- logger.debug(f"聊天状态更新任务已启动 间隔:{self.update_interval}s")
- else:
- logger.warning("状态更新任务已在运行")
+ """启动所有后台任务
+
+ 功能说明:
+ - 启动核心后台任务: 状态更新、清理、日志记录和兴趣评估
+ - 每个任务启动前检查是否已在运行
+ - 将任务引用保存到任务列表
+ """
+
+ # 任务配置列表: (任务变量名, 任务函数, 任务名称, 日志级别, 额外日志信息, 任务对象引用属性名)
+ task_configs = [
+ (self._state_update_task,
+ lambda: self._run_state_update_cycle(self.update_interval),
+ "hf_state_update",
+ "debug",
+ f"聊天状态更新任务已启动 间隔:{self.update_interval}s",
+ "_state_update_task"),
+
+ (self._cleanup_task,
+ self._run_cleanup_cycle,
+ "hf_cleanup",
+ "info",
+ f"清理任务已启动 间隔:{self.cleanup_interval}s 阈值:{self.inactive_threshold}s",
+ "_cleanup_task"),
+
+ (self._logging_task,
+ self._run_logging_cycle,
+ "hf_logging",
+ "info",
+ f"日志任务已启动 间隔:{self.log_interval}s",
+ "_logging_task"),
- # 清理任务
- if self._cleanup_task is None or self._cleanup_task.done():
- self._cleanup_task = asyncio.create_task(self._run_cleanup_cycle(), name="hf_cleanup")
- self._tasks.append(self._cleanup_task)
- logger.info(f"清理任务已启动 间隔:{self.cleanup_interval}s 阈值:{self.inactive_threshold}s")
- else:
- logger.warning("清理任务已在运行")
+ # 新增兴趣评估任务配置
+ (self._interest_eval_task,
+ self._run_interest_eval_cycle,
+ "hf_interest_eval",
+ "debug", # 设为debug,避免过多日志
+ f"兴趣评估任务已启动 间隔:{self.interest_eval_interval}s",
+ "_interest_eval_task"),
+ ]
- # 日志任务
- if self._logging_task is None or self._logging_task.done():
- self._logging_task = asyncio.create_task(self._run_logging_cycle(), name="hf_logging")
- self._tasks.append(self._logging_task)
- logger.info(f"日志任务已启动 间隔:{self.log_interval}s")
- else:
- logger.warning("日志任务已在运行")
-
- # # 初始状态检查
- # initial_state = self.mai_state_info.get_current_state()
- # if initial_state != self.mai_state_info.mai_status.OFFLINE:
- # logger.info(f"初始状态:{initial_state.value} 触发初始激活检查")
- # asyncio.create_task(self.subheartflow_manager.activate_random_subflows_to_chat(initial_state))
+ # 统一启动所有任务
+ for task_var, task_func, task_name, log_level, log_msg, task_attr_name in task_configs:
+ # 检查任务变量是否存在且未完成
+ current_task_var = getattr(self, task_attr_name)
+ if current_task_var is None or current_task_var.done():
+ new_task = asyncio.create_task(task_func(), name=task_name)
+ setattr(self, task_attr_name, new_task) # 更新任务变量
+ if new_task not in self._tasks: # 避免重复添加
+ self._tasks.append(new_task)
+
+ # 根据配置记录不同级别的日志
+ getattr(logger, log_level)(log_msg)
+ else:
+ logger.warning(f"{task_name}任务已在运行")
async def stop_tasks(self):
"""停止所有后台任务。
@@ -126,15 +155,11 @@ class BackgroundTaskManager:
# 计算并执行间隔等待
elapsed = asyncio.get_event_loop().time() - start_time
sleep_time = max(0, interval - elapsed)
- if sleep_time < 0.1: # 任务超时处理
- logger.warning(f"任务 {task_name} 超时执行 ({elapsed:.2f}s > {interval}s)")
+ # if sleep_time < 0.1: # 任务超时处理, DEBUG 时可能干扰断点
+ # logger.warning(f"任务 {task_name} 超时执行 ({elapsed:.2f}s > {interval}s)")
await asyncio.sleep(sleep_time)
- # 非离线状态时评估兴趣
- if self.mai_state_info.get_current_state() != self.mai_state_info.mai_status.OFFLINE:
- await self.subheartflow_manager.evaluate_interest_and_promote()
-
- logger.debug(f"任务循环结束, 当前状态: {self.mai_state_info.get_current_state().value}")
+ logger.debug(f"任务循环结束: {task_name}") # 调整日志信息
async def _perform_state_update_work(self):
"""执行状态更新工作"""
@@ -184,8 +209,15 @@ class BackgroundTaskManager:
logger.debug("[Background Task Cleanup] Cleanup cycle finished. No inactive flows found.")
async def _perform_logging_work(self):
- """执行一轮兴趣日志记录。"""
- await self.interest_logger.log_interest_states()
+ """执行一轮状态日志记录。"""
+ await self.interest_logger.log_all_states()
+
+ # --- 新增兴趣评估工作函数 ---
+ async def _perform_interest_eval_work(self):
+ """执行一轮子心流兴趣评估与提升检查。"""
+ # 直接调用 subheartflow_manager 的方法,并传递当前状态信息
+ await self.subheartflow_manager.evaluate_interest_and_promote(self.mai_state_info)
+ # --- 结束新增 ---
# --- Specific Task Runners --- #
async def _run_state_update_cycle(self, interval: int):
@@ -200,5 +232,14 @@ class BackgroundTaskManager:
async def _run_logging_cycle(self):
await self._run_periodic_loop(
- task_name="Interest Logging", interval=self.log_interval, task_func=self._perform_logging_work
+ task_name="State Logging",
+ interval=self.log_interval,
+ task_func=self._perform_logging_work
)
+
+ # --- 新增兴趣评估任务运行器 ---
+ async def _run_interest_eval_cycle(self):
+ await self._run_periodic_loop(
+ task_name="Interest Evaluation", interval=self.interest_eval_interval, task_func=self._perform_interest_eval_work
+ )
+ # --- 结束新增 ---
diff --git a/src/heart_flow/heartflow.py b/src/heart_flow/heartflow.py
index 4bf3765b..eb61e4ea 100644
--- a/src/heart_flow/heartflow.py
+++ b/src/heart_flow/heartflow.py
@@ -23,7 +23,7 @@ logger = get_module_logger("heartflow", config=heartflow_config)
# Task Intervals (should be in BackgroundTaskManager or config)
CLEANUP_INTERVAL_SECONDS = 1200
-STATE_UPDATE_INTERVAL_SECONDS = 30
+STATE_UPDATE_INTERVAL_SECONDS = 60
# Thresholds (should be in SubHeartflowManager or config)
INACTIVE_THRESHOLD_SECONDS = 1200
@@ -63,7 +63,7 @@ class Heartflow:
# 子系统初始化
self.mind: Mind = Mind(self.subheartflow_manager, self.llm_model) # 思考管理器
- self.interest_logger: InterestLogger = InterestLogger(self.subheartflow_manager) # 兴趣日志记录器
+ self.interest_logger: InterestLogger = InterestLogger(self.subheartflow_manager, self) # 兴趣日志记录器
# 后台任务管理器 (整合所有定时任务)
self.background_task_manager: BackgroundTaskManager = BackgroundTaskManager(
diff --git a/src/heart_flow/interest_logger.py b/src/heart_flow/interest_logger.py
index 3833ef88..c2af9ba6 100644
--- a/src/heart_flow/interest_logger.py
+++ b/src/heart_flow/interest_logger.py
@@ -12,7 +12,8 @@ from src.plugins.chat.chat_stream import chat_manager
if TYPE_CHECKING:
from src.heart_flow.subheartflow_manager import SubHeartflowManager
- from src.heart_flow.sub_heartflow import SubHeartflow # For type hint in get_interest_states
+ from src.heart_flow.sub_heartflow import SubHeartflow
+ from src.heart_flow.heartflow import Heartflow # 导入 Heartflow 类型
logger = get_module_logger("interest_logger")
@@ -22,25 +23,28 @@ HISTORY_LOG_FILENAME = "interest_history.log"
class InterestLogger:
- """负责定期记录所有子心流的兴趣状态到日志文件。"""
+ """负责定期记录主心流和所有子心流的状态到日志文件。"""
- def __init__(self, subheartflow_manager: "SubHeartflowManager"):
+ def __init__(self, subheartflow_manager: "SubHeartflowManager", heartflow: "Heartflow"):
+ """
+ 初始化 InterestLogger。
+
+ Args:
+ subheartflow_manager: 子心流管理器实例。
+ heartflow: 主心流实例,用于获取主心流状态。
+ """
self.subheartflow_manager = subheartflow_manager
+ self.heartflow = heartflow # 存储 Heartflow 实例
self._history_log_file_path = os.path.join(LOG_DIRECTORY, HISTORY_LOG_FILENAME)
self._ensure_log_directory()
def _ensure_log_directory(self):
"""确保日志目录存在。"""
- try:
- os.makedirs(LOG_DIRECTORY, exist_ok=True)
- logger.info(f"已确保日志目录 '{LOG_DIRECTORY}' 存在")
- except OSError as e:
- logger.error(f"创建日志目录 '{LOG_DIRECTORY}' 出错: {e}")
+ os.makedirs(LOG_DIRECTORY, exist_ok=True)
+ logger.info(f"已确保日志目录 '{LOG_DIRECTORY}' 存在")
- async def get_all_interest_states(self) -> Dict[str, Dict]:
- """并发获取所有活跃子心流的当前兴趣状态。"""
- _states = {}
- # Get snapshot from the manager
+ async def get_all_subflow_states(self) -> Dict[str, Dict]:
+ """并发获取所有活跃子心流的当前完整状态。"""
all_flows: List["SubHeartflow"] = self.subheartflow_manager.get_all_subheartflows()
tasks = []
results = {}
@@ -49,12 +53,11 @@ class InterestLogger:
logger.debug("未找到任何子心流状态")
return results
- # logger.debug(f"正在获取 {len(all_flows)} 个子心流的兴趣状态...")
for subheartflow in all_flows:
if self.subheartflow_manager.get_subheartflow(subheartflow.subheartflow_id):
tasks.append(
asyncio.create_task(
- subheartflow.get_interest_state(), name=f"get_state_{subheartflow.subheartflow_id}"
+ subheartflow.get_full_state(), name=f"get_state_{subheartflow.subheartflow_id}"
)
)
else:
@@ -64,74 +67,87 @@ class InterestLogger:
done, pending = await asyncio.wait(tasks, timeout=5.0)
if pending:
- logger.warning(f"获取兴趣状态超时,有 {len(pending)} 个任务未完成")
+ logger.warning(f"获取子心流状态超时,有 {len(pending)} 个任务未完成")
for task in pending:
task.cancel()
for task in done:
- try:
- stream_id_str = task.get_name().split("get_state_")[-1]
- stream_id = stream_id_str
- except IndexError:
- logger.error(f"无法从任务名 {task.get_name()} 中提取 stream_id")
- continue
+ stream_id_str = task.get_name().split("get_state_")[-1]
+ stream_id = stream_id_str
- try:
+ if task.cancelled():
+ logger.warning(f"获取子心流 {stream_id} 状态的任务已取消(超时)", exc_info=False)
+ elif task.exception():
+ exc = task.exception()
+ logger.warning(f"获取子心流 {stream_id} 状态出错: {exc}")
+ else:
result = task.result()
results[stream_id] = result
- except asyncio.CancelledError:
- logger.warning(f"获取子心流 {stream_id} 兴趣状态的任务已取消(超时)", exc_info=False)
- except Exception as e:
- logger.warning(f"获取子心流 {stream_id} 兴趣状态出错: {e}")
- logger.trace(f"成功获取 {len(results)} 个兴趣状态")
+ logger.trace(f"成功获取 {len(results)} 个子心流的完整状态")
return results
- async def log_interest_states(self):
- """获取所有子心流的兴趣状态并写入日志文件。"""
- # logger.debug("开始定期记录兴趣状态...")
+ async def log_all_states(self):
+ """获取主心流状态和所有子心流的完整状态并写入日志文件。"""
try:
current_timestamp = time.time()
- all_interest_states = await self.get_all_interest_states()
- if not all_interest_states:
- logger.debug("没有获取到任何兴趣状态")
+ main_mind = self.heartflow.current_mind
+ # 获取 Mai 状态名称
+ mai_state_name = self.heartflow.current_state.get_current_state().name
+
+ all_subflow_states = await self.get_all_subflow_states()
+
+ log_entry_base = {
+ "timestamp": round(current_timestamp, 2),
+ "main_mind": main_mind,
+ "mai_state": mai_state_name,
+ "subflow_count": len(all_subflow_states),
+ "subflows": []
+ }
+
+ if not all_subflow_states:
+ logger.debug("没有获取到任何子心流状态,仅记录主心流状态")
+ with open(self._history_log_file_path, "a", encoding="utf-8") as f:
+ f.write(json.dumps(log_entry_base, ensure_ascii=False) + "\n")
return
- count = 0
- try:
- with open(self._history_log_file_path, "a", encoding="utf-8") as f:
- items_snapshot = list(all_interest_states.items())
- for stream_id, state in items_snapshot:
- group_name = stream_id
- try:
- chat_stream = chat_manager.get_stream(stream_id)
- if chat_stream and chat_stream.group_info:
- group_name = chat_stream.group_info.group_name
- elif chat_stream and not chat_stream.group_info:
- group_name = (
- f"私聊_{chat_stream.user_info.user_nickname}"
- if chat_stream.user_info
- else stream_id
- )
- except Exception as e:
- logger.trace(f"无法获取 stream_id {stream_id} 的群组名: {e}")
- pass
+ subflow_details = []
+ items_snapshot = list(all_subflow_states.items())
+ for stream_id, state in items_snapshot:
+ group_name = stream_id
+ try:
+ chat_stream = chat_manager.get_stream(stream_id)
+ if chat_stream:
+ if chat_stream.group_info:
+ group_name = chat_stream.group_info.group_name
+ elif chat_stream.user_info:
+ group_name = f"私聊_{chat_stream.user_info.user_nickname}"
+ except Exception as e:
+ logger.trace(f"无法获取 stream_id {stream_id} 的群组名: {e}")
- log_entry = {
- "timestamp": round(current_timestamp, 2),
- "stream_id": stream_id,
- "interest_level": state.get("interest_level", 0.0),
- "group_name": group_name,
- "reply_probability": state.get("current_reply_probability", 0.0),
- "is_above_threshold": state.get("is_above_threshold", False),
- }
- f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
- count += 1
- # logger.debug(f"成功记录 {count} 条兴趣历史到 {self._history_log_file_path}")
- except IOError as e:
- logger.error(f"写入兴趣历史日志到 {self._history_log_file_path} 出错: {e}")
+ interest_state = state.get("interest_state", {})
+ subflow_entry = {
+ "stream_id": stream_id,
+ "group_name": group_name,
+ "sub_mind": state.get("current_mind", "未知"),
+ "sub_chat_state": state.get("chat_state", "未知"),
+ "interest_level": interest_state.get("interest_level", 0.0),
+ "reply_probability": interest_state.get("current_reply_probability", 0.0),
+ "is_above_threshold": interest_state.get("is_above_threshold", False),
+ "last_active_time": state.get("last_active_time", 0.0),
+ "last_interaction_time": interest_state.get("last_interaction_time", 0.0),
+ }
+ subflow_details.append(subflow_entry)
+
+ log_entry_base["subflows"] = subflow_details
+
+ with open(self._history_log_file_path, "a", encoding="utf-8") as f:
+ f.write(json.dumps(log_entry_base, ensure_ascii=False) + "\n")
+
+ except IOError as e:
+ logger.error(f"写入状态日志到 {self._history_log_file_path} 出错: {e}")
except Exception as e:
- logger.error(f"定期记录兴趣历史时发生意外错误: {e}")
+ logger.error(f"记录状态时发生意外错误: {e}")
logger.error(traceback.format_exc())
diff --git a/src/heart_flow/mai_state_manager.py b/src/heart_flow/mai_state_manager.py
index 2831df38..5048ff08 100644
--- a/src/heart_flow/mai_state_manager.py
+++ b/src/heart_flow/mai_state_manager.py
@@ -23,9 +23,9 @@ class MaiState(enum.Enum):
"""
OFFLINE = "不在线"
- PEEKING = "看一眼手机"
+ PEEKING = "看一眼"
NORMAL_CHAT = "正常聊天"
- FOCUSED_CHAT = "专注聊天"
+ FOCUSED_CHAT = "专心聊天"
def get_normal_chat_max_num(self):
if self == MaiState.OFFLINE:
@@ -127,9 +127,9 @@ class MaiStateManager:
elif current_status == MaiState.PEEKING:
logger.info("当前[在窥屏],思考要不要继续聊下去......")
elif current_status == MaiState.NORMAL_CHAT:
- logger.info("当前在[闲聊]思考要不要继续聊下去......")
+ logger.info("当前在[随便看]思考要不要继续聊下去......")
elif current_status == MaiState.FOCUSED_CHAT:
- logger.info("当前在[激情聊天]思考要不要继续聊下去......")
+ logger.info("当前在[专心看]思考要不要继续聊下去......")
# 1. 麦麦每分钟都有概率离线
if time_since_last_min_check >= 60:
@@ -149,7 +149,7 @@ class MaiStateManager:
next_state_candidate = random.choices(choices_list, weights=weights, k=1)[0]
if next_state_candidate != MaiState.OFFLINE:
next_state = next_state_candidate
- logger.debug(f"上线!开始 {next_state.name}")
+ logger.debug(f"上线!开始 {next_state.value}")
else:
# 继续离线状态
next_state = MaiState.OFFLINE
@@ -159,7 +159,7 @@ class MaiStateManager:
weights = [70, 20, 10]
choices_list = [MaiState.OFFLINE, MaiState.NORMAL_CHAT, MaiState.FOCUSED_CHAT]
next_state = random.choices(choices_list, weights=weights, k=1)[0]
- logger.debug(f"手机看完了,接下来 {next_state.name}")
+ logger.debug(f"手机看完了,接下来 {next_state.value}")
elif current_status == MaiState.NORMAL_CHAT:
if time_in_current_status >= 300: # NORMAL_CHAT 最多持续 300 秒
@@ -167,16 +167,16 @@ class MaiStateManager:
choices_list = [MaiState.OFFLINE, MaiState.FOCUSED_CHAT]
next_state = random.choices(choices_list, weights=weights, k=1)[0]
if next_state == MaiState.FOCUSED_CHAT:
- logger.debug(f"继续深入聊天, {next_state.name}")
+ logger.debug(f"继续深入聊天, {next_state.value}")
else:
- logger.debug(f"聊完了,接下来 {next_state.name}")
+ logger.debug(f"聊完了,接下来 {next_state.value}")
elif current_status == MaiState.FOCUSED_CHAT:
if time_in_current_status >= 600: # FOCUSED_CHAT 最多持续 600 秒
weights = [80, 20]
choices_list = [MaiState.OFFLINE, MaiState.NORMAL_CHAT]
next_state = random.choices(choices_list, weights=weights, k=1)[0]
- logger.debug(f"深入聊天结束,接下来 {next_state.name}")
+ logger.debug(f"深入聊天结束,接下来 {next_state.value}")
# 如果决定了下一个状态,且这个状态与当前状态不同,则返回下一个状态
if next_state is not None and next_state != current_status:
diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py
index 97149d4f..f30e25b4 100644
--- a/src/heart_flow/sub_heartflow.py
+++ b/src/heart_flow/sub_heartflow.py
@@ -18,7 +18,6 @@ from src.plugins.chat.chat_stream import chat_manager
import math
from src.plugins.heartFC_chat.heartFC_chat import HeartFChatting
from src.plugins.heartFC_chat.normal_chat import NormalChat
-from src.plugins.heartFC_chat.normal_chat_generator import ResponseGenerator
from src.do_tool.tool_use import ToolUser
from src.heart_flow.mai_state_manager import MaiStateInfo
@@ -61,16 +60,15 @@ def init_prompt():
class ChatState(enum.Enum):
- ABSENT = "不参与"
- CHAT = "闲聊"
- FOCUSED = "专注"
+ ABSENT = "没在看群"
+ CHAT = "随便水群"
+ FOCUSED = "激情水群"
class ChatStateInfo:
def __init__(self):
- self.willing = 0
-
self.chat_status: ChatState = ChatState.ABSENT
+ self.current_state_time = 120
self.mood_manager = MoodManager()
self.mood = self.mood_manager.get_prompt()
@@ -106,110 +104,74 @@ class InterestChatting:
self.max_reply_probability: float = max_probability
self.current_reply_probability: float = 0.0
self.is_above_threshold: bool = False
- self.state_change_callback = state_change_callback
+ self.update_task: Optional[asyncio.Task] = None
+ self._stop_event = asyncio.Event()
self.interest_dict: Dict[str, tuple[MessageRecv, float, bool]] = {}
+ self.update_interval = 1.0
+ self.start_updates(self.update_interval) # 初始化时启动后台更新任务
+
+ self.above_threshold = False
+ self.start_hfc_probability = 0.0
def add_interest_dict(self, message: MessageRecv, interest_value: float, is_mentioned: bool):
self.interest_dict[message.message_info.message_id] = (message, interest_value, is_mentioned)
self.last_interaction_time = time.time()
- async def _calculate_decay(self, current_time: float):
- time_delta = current_time - self.last_update_time
- if time_delta > 0:
- old_interest = self.interest_level
- if self.interest_level < 1e-9:
- self.interest_level = 0.0
- else:
- if self.decay_rate_per_second <= 0:
- interest_logger.warning(
- f"InterestChatting encountered non-positive decay rate: {self.decay_rate_per_second}. Setting interest to 0."
- )
- self.interest_level = 0.0
- elif self.interest_level < 0:
- interest_logger.warning(
- f"InterestChatting encountered negative interest level: {self.interest_level}. Setting interest to 0."
- )
- self.interest_level = 0.0
- else:
- try:
- decay_factor = math.pow(self.decay_rate_per_second, time_delta)
- self.interest_level *= decay_factor
- except ValueError as e:
- interest_logger.error(
- f"Math error during decay calculation: {e}. Rate: {self.decay_rate_per_second}, Delta: {time_delta}, Level: {self.interest_level}. Setting interest to 0."
- )
- self.interest_level = 0.0
-
- if old_interest != self.interest_level:
- self.last_update_time = current_time
-
- async def _update_reply_probability(self, current_time: float):
- time_delta = current_time - self.last_update_time
- if time_delta <= 0:
+ async def _calculate_decay(self):
+ """计算兴趣值的衰减
+
+ 参数:
+ current_time: 当前时间戳
+
+ 处理逻辑:
+ 1. 计算时间差
+ 2. 处理各种异常情况(负值/零值)
+ 3. 正常计算衰减
+ 4. 更新最后更新时间
+ """
+
+ # 处理极小兴趣值情况
+ if self.interest_level < 1e-9:
+ self.interest_level = 0.0
return
+
+ # 异常情况处理
+ if self.decay_rate_per_second <= 0:
+ interest_logger.warning(f"衰减率({self.decay_rate_per_second})无效,重置兴趣值为0")
+ self.interest_level = 0.0
+ return
+
+ # 正常衰减计算
+ try:
+ decay_factor = math.pow(self.decay_rate_per_second, self.update_interval)
+ self.interest_level *= decay_factor
+ except ValueError as e:
+ interest_logger.error(f"衰减计算错误: {e} 参数: 衰减率={self.decay_rate_per_second} 时间差={self.update_interval} 当前兴趣={self.interest_level}")
+ self.interest_level = 0.0
+
- currently_above = self.interest_level >= self.trigger_threshold
- previous_is_above = self.is_above_threshold
-
- if currently_above:
- if not self.is_above_threshold:
- self.current_reply_probability = self.base_reply_probability
- interest_logger.debug(
- f"兴趣跨过阈值 ({self.trigger_threshold}). 概率重置为基础值: {self.base_reply_probability:.4f}"
- )
- else:
- increase_amount = self.probability_increase_rate * time_delta
- self.current_reply_probability += increase_amount
-
- self.current_reply_probability = min(self.current_reply_probability, self.max_reply_probability)
-
+ async def _update_reply_probability(self):
+ self.above_threshold = self.interest_level >= self.trigger_threshold
+ if self.above_threshold:
+ self.start_hfc_probability += 0.1
else:
- if previous_is_above:
- if self.state_change_callback:
- try:
- await self.state_change_callback(ChatState.ABSENT)
- except Exception as e:
- interest_logger.error(f"Error calling state_change_callback for ABSENT: {e}")
-
- if 0 < self.probability_decay_factor < 1:
- decay_multiplier = math.pow(self.probability_decay_factor, time_delta)
- self.current_reply_probability *= decay_multiplier
- if self.current_reply_probability < 1e-6:
- self.current_reply_probability = 0.0
- elif self.probability_decay_factor <= 0:
- if self.current_reply_probability > 0:
- interest_logger.warning(f"无效的衰减因子 ({self.probability_decay_factor}). 设置概率为0.")
- self.current_reply_probability = 0.0
-
- self.current_reply_probability = max(self.current_reply_probability, 0.0)
-
- self.is_above_threshold = currently_above
+ if self.start_hfc_probability != 0:
+ self.start_hfc_probability -= 0.1
async def increase_interest(self, current_time: float, value: float):
- await self._update_reply_probability(current_time)
- await self._calculate_decay(current_time)
self.interest_level += value
self.interest_level = min(self.interest_level, self.max_interest)
- self.last_update_time = current_time
- self.last_interaction_time = current_time
async def decrease_interest(self, current_time: float, value: float):
- await self._update_reply_probability(current_time)
self.interest_level -= value
self.interest_level = max(self.interest_level, 0.0)
- self.last_update_time = current_time
- self.last_interaction_time = current_time
async def get_interest(self) -> float:
- current_time = time.time()
- await self._update_reply_probability(current_time)
- await self._calculate_decay(current_time)
- self.last_update_time = current_time
return self.interest_level
async def get_state(self) -> dict:
- interest = await self.get_interest()
+ interest = self.interest_level # 直接使用属性值
return {
"interest_level": round(interest, 2),
"last_update_time": self.last_update_time,
@@ -219,8 +181,6 @@ class InterestChatting:
}
async def should_evaluate_reply(self) -> bool:
- current_time = time.time()
- await self._update_reply_probability(current_time)
if self.current_reply_probability > 0:
trigger = random.random() < self.current_reply_probability
@@ -228,6 +188,67 @@ class InterestChatting:
else:
return False
+ # --- 新增后台更新任务相关方法 ---
+ async def _run_update_loop(self, update_interval: float = 1.0):
+ """后台循环,定期更新兴趣和回复概率。"""
+ while not self._stop_event.is_set():
+ try:
+ if self.interest_level != 0:
+ await self._calculate_decay()
+
+ await self._update_reply_probability()
+
+
+ # 等待下一个周期或停止事件
+ await asyncio.wait_for(self._stop_event.wait(), timeout=update_interval)
+ except asyncio.TimeoutError:
+ # 正常超时,继续循环
+ continue
+ except asyncio.CancelledError:
+ interest_logger.info("InterestChatting 更新循环被取消。")
+ break
+ except Exception as e:
+ interest_logger.error(f"InterestChatting 更新循环出错: {e}")
+ interest_logger.error(traceback.format_exc())
+ # 防止错误导致CPU飙升,稍作等待
+ await asyncio.sleep(5)
+ interest_logger.info("InterestChatting 更新循环已停止。")
+
+
+ def start_updates(self, update_interval: float = 1.0):
+ """启动后台更新任务"""
+ if self.update_task is None or self.update_task.done():
+ self._stop_event.clear()
+ self.update_task = asyncio.create_task(self._run_update_loop(update_interval))
+ interest_logger.debug("后台兴趣更新任务已创建并启动。")
+ else:
+ interest_logger.debug("后台兴趣更新任务已在运行中。")
+
+
+ async def stop_updates(self):
+ """停止后台更新任务"""
+ if self.update_task and not self.update_task.done():
+ interest_logger.info("正在停止 InterestChatting 后台更新任务...")
+ self._stop_event.set() # 发送停止信号
+ try:
+ # 等待任务结束,设置超时
+ await asyncio.wait_for(self.update_task, timeout=5.0)
+ interest_logger.info("InterestChatting 后台更新任务已成功停止。")
+ except asyncio.TimeoutError:
+ interest_logger.warning("停止 InterestChatting 后台任务超时,尝试取消...")
+ self.update_task.cancel()
+ try:
+ await self.update_task # 等待取消完成
+ except asyncio.CancelledError:
+ interest_logger.info("InterestChatting 后台更新任务已被取消。")
+ except Exception as e:
+ interest_logger.error(f"停止 InterestChatting 后台任务时发生异常: {e}")
+ finally:
+ self.update_task = None
+ else:
+ interest_logger.debug("InterestChatting 后台更新任务未运行或已完成。")
+ # --- 结束 新增方法 ---
+
class SubHeartflow:
def __init__(self, subheartflow_id, mai_states: MaiStateInfo):
@@ -272,122 +293,163 @@ class SubHeartflow:
request_type="sub_heart_flow",
)
- self.gpt_instance = ResponseGenerator() # 响应生成器
- self.tool_user_instance = ToolUser() # 工具使用模块
-
self.log_prefix = chat_manager.get_stream_name(self.subheartflow_id) or self.subheartflow_id
+ async def add_time_current_state(self, add_time: float):
+ self.current_state_time += add_time
+
+ async def change_to_state_chat(self):
+ self.current_state_time = 120
+ self._start_normal_chat()
+
+ async def change_to_state_focused(self):
+ self.current_state_time = 60
+ self._start_heart_fc_chat()
+
+ async def _stop_normal_chat(self):
+ """停止 NormalChat 的兴趣监控"""
+ if self.normal_chat_instance:
+ logger.info(f"{self.log_prefix} 停止 NormalChat 兴趣监控...")
+ try:
+ await self.normal_chat_instance.stop_chat() # 调用 stop_chat
+ except Exception as e:
+ logger.error(f"{self.log_prefix} 停止 NormalChat 监控任务时出错: {e}")
+ logger.error(traceback.format_exc())
+
+ async def _start_normal_chat(self) -> bool:
+ """启动 NormalChat 实例及其兴趣监控,确保 HeartFChatting 已停止"""
+ await self._stop_heart_fc_chat() # 确保专注聊天已停止
+
+ log_prefix = self.log_prefix
+ try:
+ # 总是尝试创建或获取最新的 stream 和 interest_dict
+ chat_stream = chat_manager.get_stream(self.chat_id)
+ if not chat_stream:
+ logger.error(f"{log_prefix} 无法获取 chat_stream,无法启动 NormalChat。")
+ return False
+
+ # 如果实例不存在或需要更新,则创建新实例
+ # if not self.normal_chat_instance: # 或者总是重新创建以获取最新的 interest_dict?
+ self.normal_chat_instance = NormalChat(chat_stream=chat_stream, interest_dict=self.get_interest_dict())
+ logger.info(f"{log_prefix} 创建或更新 NormalChat 实例。")
+
+ logger.info(f"{log_prefix} 启动 NormalChat 兴趣监控...")
+ await self.normal_chat_instance.start_chat() # <--- 修正:调用 start_chat
+ return True
+ except Exception as e:
+ logger.error(f"{log_prefix} 启动 NormalChat 时出错: {e}")
+ logger.error(traceback.format_exc())
+ self.normal_chat_instance = None # 启动失败,清理实例
+ return False
+
+ async def _stop_heart_fc_chat(self):
+ """停止并清理 HeartFChatting 实例"""
+ if self.heart_fc_instance:
+ logger.info(f"{self.log_prefix} 关闭 HeartFChatting 实例...")
+ try:
+ await self.heart_fc_instance.shutdown()
+ except Exception as e:
+ logger.error(f"{self.log_prefix} 关闭 HeartFChatting 实例时出错: {e}")
+ logger.error(traceback.format_exc())
+ finally:
+ # 无论是否成功关闭,都清理引用
+ self.heart_fc_instance = None
+
+ async def _start_heart_fc_chat(self) -> bool:
+ """启动 HeartFChatting 实例,确保 NormalChat 已停止"""
+ await self._stop_normal_chat() # 确保普通聊天监控已停止
+ self.clear_interest_dict() # 清理兴趣字典,准备专注聊天
+
+ log_prefix = self.log_prefix
+ if self.heart_fc_instance:
+ if not self.heart_fc_instance._loop_active:
+ logger.warning(f"{log_prefix} HeartFChatting 实例存在但未激活,尝试重新激活...")
+ await self.heart_fc_instance.add_time() # 尝试添加时间以激活循环
+ return True # 假设 add_time 会处理激活逻辑
+ else:
+ logger.debug(f"{log_prefix} HeartFChatting 已在运行中。")
+ return True # 已经在运行
+
+ logger.info(f"{log_prefix} 麦麦准备开始专注聊天...")
+ try:
+ self.heart_fc_instance = HeartFChatting(
+ chat_id=self.chat_id,
+ )
+ if await self.heart_fc_instance._initialize():
+ await self.heart_fc_instance.add_time() # 初始化成功后添加初始时间
+ logger.info(f"{log_prefix} 麦麦已成功进入专注聊天模式。")
+ return True
+ else:
+ logger.error(f"{log_prefix} HeartFChatting 初始化失败,无法进入专注模式。")
+ self.heart_fc_instance = None # 初始化失败,清理实例
+ return False
+ except Exception as e:
+ logger.error(f"{log_prefix} 创建或初始化 HeartFChatting 实例时出错: {e}")
+ logger.error(traceback.format_exc())
+ self.heart_fc_instance = None # 创建或初始化异常,清理实例
+ return False
+
async def set_chat_state(self, new_state: "ChatState", current_states_num: tuple = ()):
"""更新sub_heartflow的聊天状态,并管理 HeartFChatting 和 NormalChat 实例及任务"""
-
current_state = self.chat_state.chat_status
if current_state == new_state:
- logger.trace(f"{self.log_prefix} 状态已为 {current_state.value}, 无需更改。")
+ # logger.trace(f"{self.log_prefix} 状态已为 {current_state.value}, 无需更改。") # 减少日志噪音
return
- log_prefix = self.log_prefix # 使用实例属性
+ log_prefix = self.log_prefix
current_mai_state = self.mai_states.get_current_state()
+ state_changed = False # 标记状态是否实际发生改变
# --- 状态转换逻辑 ---
if new_state == ChatState.CHAT:
normal_limit = current_mai_state.get_normal_chat_max_num()
- current_chat_count = current_states_num[1]
+ current_chat_count = current_states_num[1] if len(current_states_num) > 1 else 0
- if current_chat_count >= normal_limit and current_state != ChatState.CHAT: # 仅在状态转换时检查限制
- logger.debug(
- f"{log_prefix} 麦麦不能从 {current_state.value} 转换到 聊天。原因:聊不过来了 ({current_chat_count}/{normal_limit})"
- )
- return # 阻止状态转换
+ if current_chat_count >= normal_limit and current_state != ChatState.CHAT:
+ logger.debug(f"{log_prefix} 无法从 {current_state.value} 转到 聊天。原因:聊不过来了 ({current_chat_count}/{normal_limit})")
+ return # 阻止状态转换
else:
- logger.debug(f"{log_prefix} 麦麦可以进入或保持 聊天 状态 ({current_chat_count}/{normal_limit})")
- if current_state == ChatState.FOCUSED and self.heart_fc_instance:
- logger.info(f"{log_prefix} 麦麦不再专注聊天,转为随便水水...")
- await self.heart_fc_instance.shutdown() # 正确关闭 HeartFChatting
- self.heart_fc_instance = None
-
- chat_stream = chat_manager.get_stream(self.chat_id)
- self.normal_chat_instance = NormalChat(chat_stream=chat_stream, interest_dict=self.get_interest_dict())
- await self.normal_chat_instance.start_monitoring_interest()
- # NormalChat 启动/停止逻辑将在下面处理
+ logger.debug(f"{log_prefix} 准备进入或保持 聊天 状态 ({current_chat_count}/{normal_limit})")
+ if await self._start_normal_chat():
+ logger.info(f"{log_prefix} 成功进入或保持 NormalChat 状态。")
+ state_changed = True
+ else:
+ logger.error(f"{log_prefix} 启动 NormalChat 失败,无法进入 CHAT 状态。")
+ # 考虑是否需要回滚状态或采取其他措施
+ return # 启动失败,不改变状态
elif new_state == ChatState.FOCUSED:
focused_limit = current_mai_state.get_focused_chat_max_num()
- current_focused_count = current_states_num[2]
-
- if current_focused_count >= focused_limit and current_state != ChatState.FOCUSED: # 仅在状态转换时检查限制
- logger.debug(
- f"{log_prefix} 麦麦不能从 {current_state.value} 转换到 专注的聊天,原因:聊不过来了。({current_focused_count}/{focused_limit})"
- )
- return # 阻止状态转换
+ current_focused_count = current_states_num[2] if len(current_states_num) > 2 else 0
+
+ if current_focused_count >= focused_limit and current_state != ChatState.FOCUSED:
+ logger.debug(f"{log_prefix} 无法从 {current_state.value} 转到 专注。原因:聊不过来了 ({current_focused_count}/{focused_limit})")
+ return # 阻止状态转换
else:
- logger.debug(f"{log_prefix} 麦麦可以进入或保持 专注聊天 状态 ({current_focused_count}/{focused_limit})")
- if not self.heart_fc_instance:
- logger.info(f"{log_prefix} 麦麦准备开始专注聊天...")
- try:
- await self.normal_chat_instance.stop_monitoring_interest()
- self.clear_interest_dict()
-
- logger.info(f"{log_prefix} 停止 NormalChat 兴趣监控成功。")
- except Exception as e:
- logger.error(f"{log_prefix} 停止 NormalChat 兴趣监控时出错: {e}")
- logger.error(traceback.format_exc())
- try:
- self.heart_fc_instance = HeartFChatting(
- chat_id=self.chat_id,
- gpt_instance=self.gpt_instance,
- tool_user_instance=self.tool_user_instance,
- )
- if await self.heart_fc_instance._initialize():
- await self.heart_fc_instance.add_time() # 初始化成功后添加初始时间
- logger.info(f"{log_prefix} 麦麦已成功进入专注聊天模式。")
- else:
- logger.error(
- f"{log_prefix} 麦麦不能专注聊天,因为 HeartFChatting 初始化失败了,状态回滚到 {current_state.value}"
- )
- self.heart_fc_instance = None
- return # 阻止进入 FOCUSED 状态
-
- except Exception as e:
- logger.error(f"{log_prefix} 创建麦麦专注聊天实例时出错: {e}")
- logger.error(traceback.format_exc())
- self.heart_fc_instance = None
- return # 创建实例异常,阻止进入 FOCUSED 状态
-
+ logger.debug(f"{log_prefix} 准备进入或保持 专注聊天 状态 ({current_focused_count}/{focused_limit})")
+ if await self._start_heart_fc_chat():
+ logger.info(f"{log_prefix} 成功进入或保持 HeartFChatting 状态。")
+ state_changed = True
else:
- # 已经是 FOCUSED 状态,或者 heart_fc_instance 已存在但未运行(不太可能)
- if not self.heart_fc_instance._loop_active:
- logger.warning(f"{log_prefix} HeartFChatting 实例存在但未激活,尝试重新激活...")
- await self.heart_fc_instance.add_time() # 尝试添加时间以激活循环
- else:
- logger.debug(f"{log_prefix} 麦麦已经在专注聊天中。")
- # NormalChat 启动/停止逻辑将在下面处理
+ logger.error(f"{log_prefix} 启动 HeartFChatting 失败,无法进入 FOCUSED 状态。")
+ # 启动失败,状态回滚到之前的状态或ABSENT?这里保持不改变
+ return # 启动失败,不改变状态
+
elif new_state == ChatState.ABSENT:
- if current_state == ChatState.FOCUSED and self.heart_fc_instance:
- logger.info(f"{log_prefix} 麦麦离开专注的聊天,撤退了.....")
- await self.heart_fc_instance.shutdown() # 正确关闭 HeartFChatting
- self.heart_fc_instance = None
- # NormalChat 启动/停止逻辑将在下面处理
+ logger.info(f"{log_prefix} 进入 ABSENT 状态,停止所有聊天活动...")
+ await self._stop_normal_chat()
+ await self._stop_heart_fc_chat()
+ state_changed = True # 总是可以成功转换到 ABSENT
- # --- 更新状态和最后活动时间 (先更新状态,再根据新状态管理任务)---
- self.chat_state.chat_status = new_state
- self.last_active_time = time.time()
- logger.info(f"{log_prefix} 麦麦的聊天状态从 {current_state.value} 变更为 {new_state.value}")
-
- # --- 根据新的状态管理 NormalChat 的监控任务 ---
- if self.normal_chat_instance:
- try:
- if new_state == ChatState.ABSENT:
- logger.info(f"{log_prefix} 状态变为 ABSENT,停止 NormalChat 兴趣监控...")
- await self.normal_chat_instance.stop_monitoring_interest()
- else: # CHAT or FOCUSED
- logger.info(f"{log_prefix} 状态变为 {new_state.value},启动或确认 NormalChat 兴趣监控...")
- await self.normal_chat_instance.start_monitoring_interest()
- except Exception as e:
- logger.error(f"{log_prefix} 管理 NormalChat 监控任务时出错 (新状态: {new_state.value}): {e}")
- logger.error(traceback.format_exc())
+ # --- 更新状态和最后活动时间 ---
+ if state_changed:
+ logger.info(f"{log_prefix} 麦麦的聊天状态从 {current_state.value} 变更为 {new_state.value}")
+ self.chat_state.chat_status = new_state
+ self.last_active_time = time.time()
else:
- logger.warning(f"{log_prefix} NormalChat 实例不可用,无法管理其监控任务。")
+ # 如果因为某些原因(如启动失败)没有成功改变状态,记录一下
+ logger.debug(f"{log_prefix} 尝试将状态从 {current_state.value} 变为 {new_state.value},但未成功或未执行更改。")
async def subheartflow_start_working(self):
"""启动子心流的后台任务
@@ -399,8 +461,8 @@ class SubHeartflow:
logger.info(f"{self.log_prefix} 子心流开始工作...")
while not self.should_stop:
- # 主循环保持简单,只做状态检查
await asyncio.sleep(30) # 30秒检查一次停止标志
+
logger.info(f"{self.log_prefix} 子心流后台任务已停止。")
@@ -543,6 +605,16 @@ class SubHeartflow:
def clear_interest_dict(self):
self.interest_chatting.interest_dict.clear()
+ async def get_full_state(self) -> dict:
+ """获取子心流的完整状态,包括兴趣、思维和聊天状态。"""
+ interest_state = await self.get_interest_state()
+ return {
+ "interest_state": interest_state,
+ "current_mind": self.current_mind,
+ "chat_state": self.chat_state.chat_status.value,
+ "last_active_time": self.last_active_time,
+ }
+
async def shutdown(self):
"""安全地关闭子心流及其管理的任务"""
if self.should_stop:
@@ -552,24 +624,14 @@ class SubHeartflow:
logger.info(f"{self.log_prefix} 开始关闭子心流...")
self.should_stop = True # 标记为停止,让后台任务退出
- # 停止 NormalChat 监控 (保持调用,确保清理)
- if self.normal_chat_instance:
- logger.info(f"{self.log_prefix} 停止 NormalChat 监控任务 (Shutdown)...")
- try:
- await self.normal_chat_instance.stop_monitoring_interest()
- except Exception as e:
- logger.error(f"{self.log_prefix} 停止 NormalChat 监控任务时出错 (Shutdown): {e}")
- logger.error(traceback.format_exc())
+ # 使用新的停止方法
+ await self._stop_normal_chat()
+ await self._stop_heart_fc_chat()
- # 停止 HeartFChatting (如果存在且正在运行)
- if self.heart_fc_instance:
- logger.info(f"{self.log_prefix} 关闭 HeartFChatting 实例 (Shutdown)...")
- try:
- await self.heart_fc_instance.shutdown()
- except Exception as e:
- logger.error(f"{self.log_prefix} 关闭 HeartFChatting 实例时出错 (Shutdown): {e}")
- logger.error(traceback.format_exc())
- self.heart_fc_instance = None # 清理实例引用
+ # 停止兴趣更新任务
+ if self.interest_chatting:
+ logger.info(f"{self.log_prefix} 停止兴趣系统后台任务...")
+ await self.interest_chatting.stop_updates()
# 取消可能存在的旧后台任务 (self.task)
if self.task and not self.task.done():
diff --git a/src/heart_flow/subheartflow_manager.py b/src/heart_flow/subheartflow_manager.py
index 4a207f93..9c4f7315 100644
--- a/src/heart_flow/subheartflow_manager.py
+++ b/src/heart_flow/subheartflow_manager.py
@@ -257,42 +257,89 @@ class SubHeartflowManager:
logger.info(f"[停用] 完成, 尝试停止{len(flow_ids)}个, 成功{stopped_count}个")
- async def evaluate_interest_and_promote(self):
- """评估CHAT状态的子心流兴趣度,满足条件则提升到FOCUSED状态"""
- logger.debug("[子心流管理器] 开始兴趣评估周期...")
- evaluated_count = 0
- promoted_count = 0
+ async def evaluate_interest_and_promote(self, current_mai_state: MaiStateInfo):
+ """评估子心流兴趣度,满足条件且未达上限则提升到FOCUSED状态(基于start_hfc_probability)"""
+ log_prefix_manager = "[子心流管理器-兴趣评估]"
+ logger.debug(f"{log_prefix_manager} 开始周期... 当前状态: {current_mai_state.get_current_state().value}")
+
+ # 获取 FOCUSED 状态的数量上限
+ current_state_enum = current_mai_state.get_current_state()
+ focused_limit = current_state_enum.get_focused_chat_max_num()
+ if focused_limit <= 0:
+ logger.debug(f"{log_prefix_manager} 当前状态 ({current_state_enum.value}) 不允许 FOCUSED 子心流, 跳过提升检查。")
+ return
+
+ # 获取当前 FOCUSED 状态的数量 (初始值)
+ current_focused_count = self.count_subflows_by_state(ChatState.FOCUSED)
+ logger.debug(f"{log_prefix_manager} 专注上限: {focused_limit}, 当前专注数: {current_focused_count}")
# 使用快照安全遍历
subflows_snapshot = list(self.subheartflows.values())
-
- for sub_hf in subflows_snapshot:
- flow_id = sub_hf.subheartflow_id
- if flow_id in self.subheartflows and self.subheartflows[flow_id].chat_state.chat_status == ChatState.CHAT:
- evaluated_count += 1
+ promoted_count = 0 # 记录本次提升的数量
+ try:
+ for sub_hf in subflows_snapshot:
+ flow_id = sub_hf.subheartflow_id
stream_name = chat_manager.get_stream_name(flow_id) or flow_id
- log_prefix = f"[{stream_name}]"
+ log_prefix_flow = f"[{stream_name}]"
- should_promote = await sub_hf.should_evaluate_reply()
- if should_promote:
- logger.info(f"{log_prefix} 兴趣评估触发升级: CHAT -> FOCUSED")
- states_num = (
- self.count_subflows_by_state(ChatState.ABSENT),
- self.count_subflows_by_state(ChatState.CHAT),
- self.count_subflows_by_state(ChatState.FOCUSED),
- )
- await sub_hf.set_chat_state(ChatState.FOCUSED, states_num)
- if (
- self.subheartflows.get(flow_id)
- and self.subheartflows[flow_id].chat_state.chat_status == ChatState.FOCUSED
- ):
+ # 只处理 CHAT 状态的子心流
+ if sub_hf.chat_state.chat_status != ChatState.CHAT:
+ continue
+
+ # 检查是否满足提升概率
+ should_hfc = random.random() < sub_hf.interest_chatting.start_hfc_probability
+ if not should_hfc:
+ continue
+
+ # --- 关键检查:检查 FOCUSED 数量是否已达上限 ---
+ # 注意:在循环内部再次获取当前数量,因为之前的提升可能已经改变了计数
+ # 使用已经记录并在循环中更新的 current_focused_count
+ if current_focused_count >= focused_limit:
+ logger.debug(f"{log_prefix_manager} {log_prefix_flow} 达到专注上限 ({current_focused_count}/{focused_limit}), 无法提升。概率={sub_hf.interest_chatting.start_hfc_probability:.2f}")
+ continue # 跳过这个子心流,继续检查下一个
+
+ # --- 执行提升 ---
+ # 获取当前实例以检查最新状态 (防御性编程)
+ current_subflow = self.subheartflows.get(flow_id)
+ if not current_subflow or current_subflow.chat_state.chat_status != ChatState.CHAT:
+ logger.warning(f"{log_prefix_manager} {log_prefix_flow} 尝试提升时状态已改变或实例消失,跳过。")
+ continue
+
+ logger.info(f"{log_prefix_manager} {log_prefix_flow} 兴趣评估触发升级 (prob={sub_hf.interest_chatting.start_hfc_probability:.2f}, 上限:{focused_limit}, 当前:{current_focused_count}) -> FOCUSED")
+
+ states_num = (
+ self.count_subflows_by_state(ChatState.ABSENT),
+ self.count_subflows_by_state(ChatState.CHAT), # 这个值在提升前计算
+ current_focused_count, # 这个值在提升前计算
+ )
+
+ # --- 状态设置 ---
+ original_state = current_subflow.chat_state.chat_status # 记录原始状态
+ await current_subflow.set_chat_state(ChatState.FOCUSED, states_num)
+
+ # --- 状态验证 ---
+ final_subflow = self.subheartflows.get(flow_id)
+ if final_subflow:
+ final_state = final_subflow.chat_state.chat_status
+ if final_state == ChatState.FOCUSED:
+ logger.debug(f"{log_prefix_manager} {log_prefix_flow} 成功从 {original_state.value} 升级到 FOCUSED 状态")
promoted_count += 1
- logger.debug(f"{log_prefix} 成功升级到FOCUSED状态")
- else:
- logger.info(f"{log_prefix} 升级FOCUSED可能被限制阻止")
+ # 提升成功后,更新当前专注计数,以便后续检查能使用最新值
+ current_focused_count += 1
+ elif final_state == original_state: # 状态未变
+ logger.warning(f"{log_prefix_manager} {log_prefix_flow} 尝试从 {original_state.value} 升级 FOCUSED 失败,状态仍为: {final_state.value} (可能被内部逻辑阻止)")
+ else: # 状态变成其他了?
+ logger.warning(f"{log_prefix_manager} {log_prefix_flow} 尝试从 {original_state.value} 升级 FOCUSED 后状态变为 {final_state.value}")
+ else: # 子心流消失了?
+ logger.warning(f"{log_prefix_manager} {log_prefix_flow} 升级后验证时子心流 {flow_id} 消失")
- if evaluated_count > 0:
- logger.debug(f"[子心流管理器] 评估完成. 评估{evaluated_count}个CHAT流, 升级{promoted_count}个到FOCUSED")
+ except Exception as e:
+ logger.error(f"{log_prefix_manager} 兴趣评估周期出错: {e}", exc_info=True)
+
+ if promoted_count > 0:
+ logger.info(f"{log_prefix_manager} 评估周期结束, 成功提升 {promoted_count} 个子心流到 FOCUSED。")
+ else:
+ logger.debug(f"{log_prefix_manager} 评估周期结束, 未提升任何子心流。")
def count_subflows_by_state(self, state: ChatState) -> int:
"""统计指定状态的子心流数量
diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py
index c98065d1..aaf91842 100644
--- a/src/plugins/chat/bot.py
+++ b/src/plugins/chat/bot.py
@@ -3,7 +3,7 @@ from ...config.config import global_config
from .message import MessageRecv
from ..PFC.pfc_manager import PFCManager
from .chat_stream import chat_manager
-from ..chat_module.only_process.only_message_process import MessageProcessor
+from .only_message_process import MessageProcessor
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
from ..heartFC_chat.heartflow_processor import HeartFCProcessor
diff --git a/src/plugins/chat_module/only_process/only_message_process.py b/src/plugins/chat/only_message_process.py
similarity index 100%
rename from src/plugins/chat_module/only_process/only_message_process.py
rename to src/plugins/chat/only_message_process.py
diff --git a/src/plugins/heartFC_chat/heartFC_chat.py b/src/plugins/heartFC_chat/heartFC_chat.py
index 476798f4..ffed743a 100644
--- a/src/plugins/heartFC_chat/heartFC_chat.py
+++ b/src/plugins/heartFC_chat/heartFC_chat.py
@@ -13,9 +13,7 @@ from src.plugins.models.utils_model import LLMRequest
from src.config.config import global_config
from src.plugins.chat.utils_image import image_path_to_base64 # Local import needed after move
from src.plugins.utils.timer_calculater import Timer # <--- Import Timer
-
-# --- Import necessary dependencies directly ---
-from .heartFC_generator import ResponseGenerator # Assuming this is the type for gpt
+from src.plugins.heartFC_chat.heartFC_generator import HeartFCGenerator
from src.do_tool.tool_use import ToolUser
from ..chat.message_sender import message_manager # <-- Import the global manager
from src.plugins.chat.emoji_manager import emoji_manager
@@ -77,9 +75,7 @@ class HeartFChatting:
def __init__(
self,
- chat_id: str,
- gpt_instance: ResponseGenerator, # 文本回复生成器
- tool_user_instance: ToolUser, # 工具使用实例
+ chat_id: str
):
"""
HeartFChatting 初始化函数
@@ -97,13 +93,12 @@ class HeartFChatting:
# 初始化状态控制
self._initialized = False # 是否已初始化标志
- self._init_lock = asyncio.Lock() # 初始化锁(确保只初始化一次)
self._processing_lock = asyncio.Lock() # 处理锁(确保单次Plan-Replier-Sender周期)
self._timer_lock = asyncio.Lock() # 计时器锁(安全更新计时器)
# 依赖注入存储
- self.gpt_instance = gpt_instance # 文本回复生成器
- self.tool_user = tool_user_instance # 工具使用实例
+ self.gpt_instance = HeartFCGenerator() # 文本回复生成器
+ self.tool_user = ToolUser() # 工具使用实例
# LLM规划器配置
self.planner_llm = LLMRequest(
@@ -117,7 +112,6 @@ class HeartFChatting:
self._loop_timer: float = 0.0 # 循环剩余时间(秒)
self._loop_active: bool = False # 循环是否正在运行
self._loop_task: Optional[asyncio.Task] = None # 主循环任务
- self._trigger_count_this_activation: int = 0 # 当前激活周期内的触发计数
self._initial_duration: float = INITIAL_DURATION # 首次触发增加的时间
self._last_added_duration: float = self._initial_duration # 上次增加的时间
@@ -131,35 +125,34 @@ class HeartFChatting:
懒初始化以使用提供的标识符解析chat_stream和sub_hf。
确保实例已准备好处理触发器。
"""
- async with self._init_lock:
- if self._initialized:
- return True
- log_prefix = self._get_log_prefix() # 获取前缀
- try:
- self.chat_stream = chat_manager.get_stream(self.stream_id)
+ if self._initialized:
+ return True
+ log_prefix = self._get_log_prefix() # 获取前缀
+ try:
+ self.chat_stream = chat_manager.get_stream(self.stream_id)
- if not self.chat_stream:
- logger.error(f"{log_prefix} 获取ChatStream失败。")
- return False
-
- # <-- 在这里导入 heartflow 实例
- from src.heart_flow.heartflow import heartflow
-
- self.sub_hf = heartflow.get_subheartflow(self.stream_id)
- if not self.sub_hf:
- logger.warning(f"{log_prefix} 获取SubHeartflow失败。一些功能可能受限。")
-
- self._initialized = True
- logger.info(f"麦麦感觉到了,激发了HeartFChatting{log_prefix} 初始化成功。")
- return True
- except Exception as e:
- logger.error(f"{log_prefix} 初始化失败: {e}")
- logger.error(traceback.format_exc())
+ if not self.chat_stream:
+ logger.error(f"{log_prefix} 获取ChatStream失败。")
return False
+ # <-- 在这里导入 heartflow 实例
+ from src.heart_flow.heartflow import heartflow
+
+ self.sub_hf = heartflow.get_subheartflow(self.stream_id)
+ if not self.sub_hf:
+ logger.warning(f"{log_prefix} 获取SubHeartflow失败。一些功能可能受限。")
+
+ self._initialized = True
+ logger.info(f"麦麦感觉到了,激发了HeartFChatting{log_prefix} 初始化成功。")
+ return True
+ except Exception as e:
+ logger.error(f"{log_prefix} 初始化失败: {e}")
+ logger.error(traceback.format_exc())
+ return False
+
async def add_time(self):
"""
- 为麦麦添加时间,麦麦有兴趣时,时间增加。
+ 为麦麦添加时间,麦麦有兴趣时,固定增加15秒
"""
log_prefix = self._get_log_prefix()
if not self._initialized:
@@ -168,45 +161,61 @@ class HeartFChatting:
return
async with self._timer_lock:
- duration_to_add: float = 0.0
+ duration_to_add: float = 15.0 # 固定增加15秒
+ if not self._loop_active: # 首次触发
+ logger.info(f"{log_prefix} 麦麦有兴趣! 打算聊:15s.")
+ else: # 循环已激活
+ logger.info(f"{log_prefix} 麦麦想继续聊:15s, 还能聊: {self._loop_timer:.1f}s.")
- if not self._loop_active: # First trigger for this activation cycle
- duration_to_add = self._initial_duration # 使用初始值
- self._last_added_duration = duration_to_add # 更新上次增加的值
- self._trigger_count_this_activation = 1 # Start counting
- logger.info(
- f"{log_prefix} 麦麦有兴趣! #{self._trigger_count_this_activation}. 麦麦打算聊: {duration_to_add:.2f}s."
- )
- else: # Loop is already active, apply 50% reduction
- self._trigger_count_this_activation += 1
- duration_to_add = self._last_added_duration * 0.5
- if duration_to_add < 1.5:
- duration_to_add = 1.5
- # Update _last_added_duration only if it's >= 0.5 to prevent it from becoming too small
- self._last_added_duration = duration_to_add
- logger.info(
- f"{log_prefix} 麦麦兴趣增加! #{self._trigger_count_this_activation}. 想继续聊: {duration_to_add:.2f}s, 麦麦还能聊: {self._loop_timer:.1f}s."
- )
-
- # 添加计算出的时间
+ # 添加固定时间
new_timer_value = self._loop_timer + duration_to_add
- # Add max timer duration limit? e.g., max(0, min(new_timer_value, 300))
self._loop_timer = max(0, new_timer_value)
- # Log less frequently, e.g., every 10 seconds or significant change?
- # if self._trigger_count_this_activation % 5 == 0:
- # logger.info(f"{log_prefix} 麦麦现在想聊{self._loop_timer:.1f}秒")
- # Start the loop if it wasn't active and timer is positive
+ # 添加时间后,检查是否需要启动循环
+ await self._start_loop_if_needed()
+
+ async def start(self):
+ """
+ 显式尝试启动 HeartFChatting 的主循环。
+ 如果循环未激活且计时器 > 0,则启动循环。
+ """
+ log_prefix = self._get_log_prefix()
+ if not self._initialized:
+ if not await self._initialize():
+ logger.error(f"{log_prefix} 无法启动循环: 初始化失败。")
+ return
+ logger.info(f"{log_prefix} 尝试显式启动循环...")
+ await self._start_loop_if_needed()
+
+ async def _start_loop_if_needed(self):
+ """检查是否需要启动主循环,如果未激活且计时器大于0,则启动。"""
+ log_prefix = self._get_log_prefix()
+ should_start_loop = False
+ async with self._timer_lock:
+ # 检查是否满足启动条件:未激活且计时器有时间
if not self._loop_active and self._loop_timer > 0:
- self._loop_active = True
- if self._loop_task and not self._loop_task.done():
- logger.warning(f"{log_prefix} 发现意外的循环任务正在进行。取消它。")
- self._loop_task.cancel()
+ should_start_loop = True
+ self._loop_active = True # 在锁内标记为活动,防止重复启动
- self._loop_task = asyncio.create_task(self._run_pf_loop())
- self._loop_task.add_done_callback(self._handle_loop_completion)
- elif self._loop_active:
- logger.trace(f"{log_prefix} 循环已经激活。计时器延长。")
+ if should_start_loop:
+ # 检查是否已有任务在运行(理论上不应该,因为 _loop_active=False)
+ if self._loop_task and not self._loop_task.done():
+ logger.warning(f"{log_prefix} 发现之前的循环任务仍在运行(不符合预期)。取消旧任务。")
+ self._loop_task.cancel()
+ try:
+ # 等待旧任务确实被取消
+ await asyncio.wait_for(self._loop_task, timeout=0.5)
+ except (asyncio.CancelledError, asyncio.TimeoutError):
+ pass # 忽略取消或超时错误
+ self._loop_task = None # 清理旧任务引用
+
+ logger.info(f"{log_prefix} 计时器 > 0 且循环未激活,启动主循环...")
+ # 创建新的循环任务
+ self._loop_task = asyncio.create_task(self._run_pf_loop())
+ # 添加完成回调
+ self._loop_task.add_done_callback(self._handle_loop_completion)
+ # else:
+ # logger.trace(f"{log_prefix} 不需要启动循环(已激活或计时器为0)") # 可以取消注释以进行调试
def _handle_loop_completion(self, task: asyncio.Task):
"""当 _run_pf_loop 任务完成时执行的回调。"""
@@ -228,8 +237,6 @@ class HeartFChatting:
if self._processing_lock.locked():
logger.warning(f"{log_prefix} HeartFChatting: 处理锁在循环结束时仍被锁定,强制释放。")
self._processing_lock.release()
- # Instance removal is now handled by SubHeartflow
- # asyncio.create_task(self.heartfc_controller._remove_heartFC_chat_instance(self.stream_id)) # Removed
async def _run_pf_loop(self):
"""
diff --git a/src/plugins/heartFC_chat/heartFC_generator.py b/src/plugins/heartFC_chat/heartFC_generator.py
index 70d4109f..28329b89 100644
--- a/src/plugins/heartFC_chat/heartFC_generator.py
+++ b/src/plugins/heartFC_chat/heartFC_generator.py
@@ -22,7 +22,7 @@ llm_config = LogConfig(
logger = get_module_logger("llm_generator", config=llm_config)
-class ResponseGenerator:
+class HeartFCGenerator:
def __init__(self):
self.model_normal = LLMRequest(
model=global_config.llm_normal,
diff --git a/src/plugins/heartFC_chat/heartflow_processor.py b/src/plugins/heartFC_chat/heartflow_processor.py
index 44206d8b..30ac61c5 100644
--- a/src/plugins/heartFC_chat/heartflow_processor.py
+++ b/src/plugins/heartFC_chat/heartflow_processor.py
@@ -67,11 +67,8 @@ class HeartFCProcessor:
group_info=groupinfo,
)
- # --- 确保 SubHeartflow 存在 ---
subheartflow = await heartflow.create_subheartflow(chat.stream_id)
- if not subheartflow:
- logger.error(f"无法为 stream_id {chat.stream_id} 创建或获取 SubHeartflow,中止处理")
- return
+
message.update_chat_stream(chat)
@@ -137,33 +134,16 @@ class HeartFCProcessor:
# --- 修改:兴趣度更新逻辑 --- #
if is_mentioned:
- interest_increase_on_mention = 2
+ interest_increase_on_mention = 1
mentioned_boost = interest_increase_on_mention # 从配置获取提及增加值
interested_rate += mentioned_boost
- logger.trace(f"消息提及机器人,额外增加兴趣 {mentioned_boost:.2f}")
# 更新兴趣度 (调用 SubHeartflow 的方法)
- current_interest = 0.0 # 初始化
- try:
- # 获取当前时间,传递给 increase_interest
- current_time = time.time()
- await subheartflow.interest_chatting.increase_interest(current_time, value=interested_rate)
- current_interest = await subheartflow.get_interest_level() # 获取更新后的值
+ current_time = time.time()
+ await subheartflow.interest_chatting.increase_interest(current_time, value=interested_rate)
- logger.trace(
- f"使用激活率 {interested_rate:.2f} 更新后 (通过缓冲后),当前兴趣度: {current_interest:.2f} (Stream: {chat.stream_id})"
- )
-
- # 添加到 SubHeartflow 的 interest_dict
- await subheartflow.add_interest_dict_entry(message, interested_rate, is_mentioned)
- logger.trace(
- f"Message {message.message_info.message_id} added to interest dict for stream {chat.stream_id}"
- )
-
- except Exception as e:
- logger.error(f"更新兴趣度失败 (Stream: {chat.stream_id}): {e}")
- logger.error(traceback.format_exc())
- # --- 结束修改 --- #
+ # 添加到 SubHeartflow 的 interest_dict,给normal_chat处理
+ await subheartflow.add_interest_dict_entry(message, interested_rate, is_mentioned)
# 打印消息接收和处理信息
mes_name = chat.group_info.group_name if chat.group_info else "私聊"
@@ -172,7 +152,7 @@ class HeartFCProcessor:
f"[{current_time}][{mes_name}]"
f"{chat.user_info.user_nickname}:"
f"{message.processed_plain_text}"
- f"兴趣度: {current_interest:.2f}"
+ f"[兴趣度: {interested_rate:.2f}]"
)
try:
diff --git a/src/plugins/heartFC_chat/normal_chat.py b/src/plugins/heartFC_chat/normal_chat.py
index 8de50415..902623eb 100644
--- a/src/plugins/heartFC_chat/normal_chat.py
+++ b/src/plugins/heartFC_chat/normal_chat.py
@@ -7,7 +7,7 @@ from typing import List, Optional # 导入 Optional
from ..moods.moods import MoodManager
from ...config.config import global_config
from ..chat.emoji_manager import emoji_manager
-from .normal_chat_generator import ResponseGenerator
+from .normal_chat_generator import NormalChatGenerator
from ..chat.message import MessageSending, MessageRecv, MessageThinking, MessageSet
from ..chat.message_sender import message_manager
from ..chat.utils_image import image_path_to_base64
@@ -43,12 +43,10 @@ class NormalChat:
self.interest_dict = interest_dict
- logger.info(f"[{self.stream_name}] 正在初始化 NormalChat 实例...")
-
- self.gpt = ResponseGenerator()
+ self.gpt = NormalChatGenerator()
self.mood_manager = MoodManager.get_instance() # MoodManager 保持单例
# 存储此实例的兴趣监控任务
- self._interest_monitoring_task: Optional[asyncio.Task] = None
+ self._chat_task: Optional[asyncio.Task] = None
logger.info(f"[{self.stream_name}] NormalChat 实例初始化完成。")
# 改为实例方法
@@ -73,7 +71,6 @@ class NormalChat:
)
await message_manager.add_message(thinking_message)
-
return thinking_id
# 改为实例方法
@@ -176,7 +173,7 @@ class NormalChat:
await asyncio.sleep(1) # 每秒检查一次
# 检查任务是否已被取消
- if self._interest_monitoring_task is None or self._interest_monitoring_task.cancelled():
+ if self._chat_task is None or self._chat_task.cancelled():
logger.info(f"[{self.stream_name}] 兴趣监控任务被取消或置空,退出")
break
@@ -352,19 +349,21 @@ class NormalChat:
return False
# 改为实例方法, 移除 chat 参数
- async def start_monitoring_interest(self):
- """为此 NormalChat 实例关联的 ChatStream 启动兴趣消息监控任务(如果尚未运行)。"""
- if self._interest_monitoring_task is None or self._interest_monitoring_task.done():
- logger.info(f"[{self.stream_name}] 启动兴趣消息监控任务...")
+
+ async def start_chat(self):
+ """为此 NormalChat 实例关联的 ChatStream 启动聊天任务(如果尚未运行)。"""
+ if self._chat_task is None or self._chat_task.done():
+ logger.info(f"[{self.stream_name}] 启动聊天任务...")
task = asyncio.create_task(self._find_interested_message())
task.add_done_callback(lambda t: self._handle_task_completion(t)) # 回调现在是实例方法
- self._interest_monitoring_task = task
+ self._chat_task = task
+
# 改为实例方法, 移除 stream_id 参数
def _handle_task_completion(self, task: asyncio.Task):
"""兴趣监控任务完成时的回调函数。"""
# 检查完成的任务是否是当前实例的任务
- if task is not self._interest_monitoring_task:
+ if task is not self._chat_task:
logger.warning(f"[{self.stream_name}] 收到一个未知或过时任务的完成回调。")
return
@@ -382,27 +381,26 @@ class NormalChat:
logger.error(f"[{self.stream_name}] 处理任务完成回调时出错: {e}")
finally:
# 标记任务已完成/移除
- if self._interest_monitoring_task is task: # 再次确认是当前任务
- self._interest_monitoring_task = None
- logger.debug(f"[{self.stream_name}] 兴趣监控任务已被标记为完成/移除。")
+ if self._chat_task is task: # 再次确认是当前任务
+ self._chat_task = None
+ logger.debug(f"[{self.stream_name}] 聊天任务已被标记为完成/移除。")
# 改为实例方法, 移除 stream_id 参数
- async def stop_monitoring_interest(self):
+ async def stop_chat(self):
"""停止当前实例的兴趣监控任务。"""
- if self._interest_monitoring_task and not self._interest_monitoring_task.done():
- task = self._interest_monitoring_task
- logger.info(f"[{self.stream_name}] 尝试取消兴趣监控任务。")
+ if self._chat_task and not self._chat_task.done():
+ task = self._chat_task
+ logger.info(f"[{self.stream_name}] 尝试取消聊天任务。")
task.cancel()
try:
await task # 等待任务响应取消
except asyncio.CancelledError:
- logger.info(f"[{self.stream_name}] 兴趣监控任务已成功取消。")
+ logger.info(f"[{self.stream_name}] 聊天任务已成功取消。")
except Exception as e:
# 回调函数 _handle_task_completion 会处理异常日志
logger.warning(f"[{self.stream_name}] 等待监控任务取消时捕获到异常 (可能已在回调中记录): {e}")
finally:
# 确保任务状态更新,即使等待出错 (回调函数也会尝试更新)
- if self._interest_monitoring_task is task:
- self._interest_monitoring_task = None
- # else:
- # logger.debug(f"[{self.stream_name}] 没有正在运行的兴趣监控任务可停止。")
+ if self._chat_task is task:
+ self._chat_task = None
+
diff --git a/src/plugins/heartFC_chat/normal_chat_generator.py b/src/plugins/heartFC_chat/normal_chat_generator.py
index ee133f44..07635baf 100644
--- a/src/plugins/heartFC_chat/normal_chat_generator.py
+++ b/src/plugins/heartFC_chat/normal_chat_generator.py
@@ -19,7 +19,7 @@ llm_config = LogConfig(
logger = get_module_logger("llm_generator", config=llm_config)
-class ResponseGenerator:
+class NormalChatGenerator:
def __init__(self):
self.model_reasoning = LLMRequest(
model=global_config.llm_reasoning,
@@ -77,8 +77,6 @@ class ResponseGenerator:
sender_name = f"({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}"
else:
sender_name = f"用户({message.chat_stream.user_info.user_id})"
-
- logger.debug("开始使用生成回复-2")
# 构建prompt
with Timer() as t_build_prompt:
prompt = await prompt_builder.build_prompt(
From ae95218dc6dc2a9b1b618594ce6bccb5e69dc135 Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Wed, 23 Apr 2025 23:11:38 +0800
Subject: [PATCH 10/28] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=85=81?=
=?UTF-8?q?=E8=AE=B8=E8=AF=B4=E8=AF=9D=E7=9A=84=E7=BE=A4=E6=97=A0=E6=95=88?=
=?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/heart_flow/heartflow.py | 2 --
src/plugins/chat/bot.py | 4 ++++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/heart_flow/heartflow.py b/src/heart_flow/heartflow.py
index eb61e4ea..7fbc0f58 100644
--- a/src/heart_flow/heartflow.py
+++ b/src/heart_flow/heartflow.py
@@ -4,7 +4,6 @@ from src.config.config import global_config
from src.plugins.schedule.schedule_generator import bot_schedule
from src.common.logger import get_module_logger, LogConfig, HEARTFLOW_STYLE_CONFIG
from typing import Any, Optional
-from src.plugins.heartFC_chat.heartFC_generator import ResponseGenerator
from src.do_tool.tool_use import ToolUser
from src.plugins.person_info.relationship_manager import relationship_manager # Module instance
from src.heart_flow.mai_state_manager import MaiStateInfo, MaiStateManager
@@ -57,7 +56,6 @@ class Heartflow:
)
# 外部依赖模块
- self.gpt_instance = ResponseGenerator() # 响应生成器
self.tool_user_instance = ToolUser() # 工具使用模块
self.relationship_manager_instance = relationship_manager # 关系管理模块
diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py
index aaf91842..fdb2576a 100644
--- a/src/plugins/chat/bot.py
+++ b/src/plugins/chat/bot.py
@@ -82,6 +82,10 @@ class ChatBot:
logger.debug(f"用户{userinfo.user_id}被禁止回复")
return
+ if groupinfo.group_id not in global_config.talk_allowed_groups:
+ logger.debug(f"群{groupinfo.group_id}被禁止回复")
+ return
+
if message.message_info.template_info and not message.message_info.template_info.template_default:
template_group_name = message.message_info.template_info.template_name
template_items = message.message_info.template_info.template_items
From 79260d3b86a45864c13294d5702320b4ee1f7df4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 23:16:20 +0800
Subject: [PATCH 11/28] =?UTF-8?q?refactor(logger):=20=E7=BB=9F=E4=B8=80?=
=?UTF-8?q?=E6=97=A5=E5=BF=97=E9=85=8D=E7=BD=AE=E5=B9=B6=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=89=B9=E5=AE=9A=E6=A0=B7=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
为远程、表情、主程序、后台任务等模块添加了特定的日志样式配置,并统一了日志初始化方式,以提高日志的可读性和一致性。
---
bot.py | 9 +++--
src/common/logger.py | 53 ++++++++++++++++++++++++++++++
src/heart_flow/background_tasks.py | 9 +++--
src/plugins/chat/emoji_manager.py | 9 +++--
src/plugins/remote/remote.py | 9 +++--
5 files changed, 81 insertions(+), 8 deletions(-)
diff --git a/bot.py b/bot.py
index 53dbc9be..d530f03c 100644
--- a/bot.py
+++ b/bot.py
@@ -8,11 +8,16 @@ import time
import platform
import traceback
from dotenv import load_dotenv
-from src.common.logger import get_module_logger, LogConfig, CONFIRM_STYLE_CONFIG
+from src.common.logger import get_module_logger, LogConfig, CONFIRM_STYLE_CONFIG, MAIN_STYLE_CONFIG
from src.common.crash_logger import install_crash_handler
from src.main import MainSystem
-logger = get_module_logger("main_bot")
+
+main_log_config = LogConfig(
+ console_format=MAIN_STYLE_CONFIG["console_format"],
+ file_format=MAIN_STYLE_CONFIG["file_format"],
+)
+logger = get_module_logger("main_bot", config=main_log_config)
confirm_logger_config = LogConfig(
console_format=CONFIRM_STYLE_CONFIG["console_format"],
file_format=CONFIRM_STYLE_CONFIG["file_format"],
diff --git a/src/common/logger.py b/src/common/logger.py
index 48d476e6..9e8bac52 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -297,6 +297,22 @@ CHAT_STYLE_CONFIG = {
},
}
+REMOTE_STYLE_CONFIG = {
+ "advanced": {
+ "console_format": (
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
+ "远程 | "
+ "{message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 远程 | {message}",
+ },
+ "simple": {
+ "console_format": "{time:MM-DD HH:mm} | 远程 | {message}",
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 远程 | {message}",
+ },
+}
+
SUB_HEARTFLOW_STYLE_CONFIG = {
"advanced": {
"console_format": (
@@ -315,6 +331,24 @@ SUB_HEARTFLOW_STYLE_CONFIG = {
},
}
+BACKGROUND_TASKS_STYLE_CONFIG = {
+ "advanced": {
+ "console_format": (
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
+ "后台任务 | "
+ "{message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 后台任务 | {message}",
+ },
+ "simple": {
+ "console_format": (
+ "{time:MM-DD HH:mm} | 后台任务 | {message}"
+ ), # noqa: E501
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 后台任务 | {message}",
+ },
+}
+
WILLING_STYLE_CONFIG = {
"advanced": {
"console_format": (
@@ -331,6 +365,22 @@ WILLING_STYLE_CONFIG = {
},
}
+EMOJI_STYLE_CONFIG = {
+ "advanced": {
+ "console_format": (
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
+ "表情 | "
+ "{message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 表情 | {message}",
+ },
+ "simple": {
+ "console_format": "{time:MM-DD HH:mm} | 表情 | {message} ", # noqa: E501
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 表情 | {message}",
+ },
+
+}
MAI_STATE_CONFIG = {
"advanced": {
@@ -393,6 +443,9 @@ CONFIRM_STYLE_CONFIG = {
# 根据SIMPLE_OUTPUT选择配置
MAIN_STYLE_CONFIG = MAIN_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MAIN_STYLE_CONFIG["advanced"]
+EMOJI_STYLE_CONFIG = EMOJI_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else EMOJI_STYLE_CONFIG["advanced"]
+REMOTE_STYLE_CONFIG = REMOTE_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else REMOTE_STYLE_CONFIG["advanced"]
+BACKGROUND_TASKS_STYLE_CONFIG = BACKGROUND_TASKS_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else BACKGROUND_TASKS_STYLE_CONFIG["advanced"]
MEMORY_STYLE_CONFIG = MEMORY_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MEMORY_STYLE_CONFIG["advanced"]
TOPIC_STYLE_CONFIG = TOPIC_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else TOPIC_STYLE_CONFIG["advanced"]
SENDER_STYLE_CONFIG = SENDER_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SENDER_STYLE_CONFIG["advanced"]
diff --git a/src/heart_flow/background_tasks.py b/src/heart_flow/background_tasks.py
index f4f74732..190a7300 100644
--- a/src/heart_flow/background_tasks.py
+++ b/src/heart_flow/background_tasks.py
@@ -2,14 +2,19 @@ import asyncio
import traceback
from typing import Optional, Coroutine, Callable, Any, List
-from src.common.logger import get_module_logger
+from src.common.logger import get_module_logger, LogConfig, BACKGROUND_TASKS_STYLE_CONFIG
# Need manager types for dependency injection
from src.heart_flow.mai_state_manager import MaiStateManager, MaiStateInfo
from src.heart_flow.subheartflow_manager import SubHeartflowManager
from src.heart_flow.interest_logger import InterestLogger
-logger = get_module_logger("background_tasks")
+background_tasks_log_config = LogConfig(
+ console_format=BACKGROUND_TASKS_STYLE_CONFIG["console_format"],
+ file_format=BACKGROUND_TASKS_STYLE_CONFIG["file_format"],
+)
+
+logger = get_module_logger("background_tasks", config=background_tasks_log_config)
class BackgroundTaskManager:
diff --git a/src/plugins/chat/emoji_manager.py b/src/plugins/chat/emoji_manager.py
index 6b09a583..f7f507b7 100644
--- a/src/plugins/chat/emoji_manager.py
+++ b/src/plugins/chat/emoji_manager.py
@@ -14,9 +14,14 @@ from ...config.config import global_config
from ..chat.utils import get_embedding
from ..chat.utils_image import ImageManager, image_path_to_base64
from ..models.utils_model import LLMRequest
-from src.common.logger import get_module_logger
+from src.common.logger import get_module_logger, LogConfig, EMOJI_STYLE_CONFIG
-logger = get_module_logger("emoji")
+emoji_log_config = LogConfig(
+ console_format=EMOJI_STYLE_CONFIG["console_format"],
+ file_format=EMOJI_STYLE_CONFIG["file_format"],
+)
+
+logger = get_module_logger("emoji", config=emoji_log_config)
image_manager = ImageManager()
diff --git a/src/plugins/remote/remote.py b/src/plugins/remote/remote.py
index f96bc088..1ba32ec9 100644
--- a/src/plugins/remote/remote.py
+++ b/src/plugins/remote/remote.py
@@ -5,10 +5,15 @@ import platform
import os
import json
import threading
-from src.common.logger import get_module_logger
+from src.common.logger import get_module_logger, LogConfig, REMOTE_STYLE_CONFIG
from src.config.config import global_config
-logger = get_module_logger("remote")
+
+remote_log_config = LogConfig(
+ console_format=REMOTE_STYLE_CONFIG["console_format"],
+ file_format=REMOTE_STYLE_CONFIG["file_format"],
+)
+logger = get_module_logger("remote", config=remote_log_config)
# UUID文件路径
UUID_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "client_uuid.json")
From 7281c13a127cbf1295fdb408f278e02608d4a8dc Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 15:17:06 +0000
Subject: [PATCH 12/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/common/logger.py | 9 +++++----
src/heart_flow/background_tasks.py | 2 +-
src/plugins/chat/emoji_manager.py | 2 +-
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/common/logger.py b/src/common/logger.py
index 9e8bac52..5169b7b7 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -310,7 +310,7 @@ REMOTE_STYLE_CONFIG = {
"simple": {
"console_format": "{time:MM-DD HH:mm} | 远程 | {message}",
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 远程 | {message}",
- },
+ },
}
SUB_HEARTFLOW_STYLE_CONFIG = {
@@ -338,7 +338,7 @@ BACKGROUND_TASKS_STYLE_CONFIG = {
"{level: <8} | "
"后台任务 | "
"{message}"
- ),
+ ),
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 后台任务 | {message}",
},
"simple": {
@@ -379,7 +379,6 @@ EMOJI_STYLE_CONFIG = {
"console_format": "{time:MM-DD HH:mm} | 表情 | {message} ", # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 表情 | {message}",
},
-
}
MAI_STATE_CONFIG = {
@@ -445,7 +444,9 @@ CONFIRM_STYLE_CONFIG = {
MAIN_STYLE_CONFIG = MAIN_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MAIN_STYLE_CONFIG["advanced"]
EMOJI_STYLE_CONFIG = EMOJI_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else EMOJI_STYLE_CONFIG["advanced"]
REMOTE_STYLE_CONFIG = REMOTE_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else REMOTE_STYLE_CONFIG["advanced"]
-BACKGROUND_TASKS_STYLE_CONFIG = BACKGROUND_TASKS_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else BACKGROUND_TASKS_STYLE_CONFIG["advanced"]
+BACKGROUND_TASKS_STYLE_CONFIG = (
+ BACKGROUND_TASKS_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else BACKGROUND_TASKS_STYLE_CONFIG["advanced"]
+)
MEMORY_STYLE_CONFIG = MEMORY_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MEMORY_STYLE_CONFIG["advanced"]
TOPIC_STYLE_CONFIG = TOPIC_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else TOPIC_STYLE_CONFIG["advanced"]
SENDER_STYLE_CONFIG = SENDER_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SENDER_STYLE_CONFIG["advanced"]
diff --git a/src/heart_flow/background_tasks.py b/src/heart_flow/background_tasks.py
index 190a7300..c24054bd 100644
--- a/src/heart_flow/background_tasks.py
+++ b/src/heart_flow/background_tasks.py
@@ -11,7 +11,7 @@ from src.heart_flow.interest_logger import InterestLogger
background_tasks_log_config = LogConfig(
console_format=BACKGROUND_TASKS_STYLE_CONFIG["console_format"],
- file_format=BACKGROUND_TASKS_STYLE_CONFIG["file_format"],
+ file_format=BACKGROUND_TASKS_STYLE_CONFIG["file_format"],
)
logger = get_module_logger("background_tasks", config=background_tasks_log_config)
diff --git a/src/plugins/chat/emoji_manager.py b/src/plugins/chat/emoji_manager.py
index f7f507b7..cbc8e600 100644
--- a/src/plugins/chat/emoji_manager.py
+++ b/src/plugins/chat/emoji_manager.py
@@ -18,7 +18,7 @@ from src.common.logger import get_module_logger, LogConfig, EMOJI_STYLE_CONFIG
emoji_log_config = LogConfig(
console_format=EMOJI_STYLE_CONFIG["console_format"],
- file_format=EMOJI_STYLE_CONFIG["file_format"],
+ file_format=EMOJI_STYLE_CONFIG["file_format"],
)
logger = get_module_logger("emoji", config=emoji_log_config)
From 5f830aff862ebaaa0ba5b39e6a8718b989741d6b Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Wed, 23 Apr 2025 23:21:30 +0800
Subject: [PATCH 13/28] Update interest_monitor_gui.py
---
interest_monitor_gui.py | 98 ++++++++++++++++++++++++++---------------
1 file changed, 62 insertions(+), 36 deletions(-)
diff --git a/interest_monitor_gui.py b/interest_monitor_gui.py
index 5b19d480..d5f1b15f 100644
--- a/interest_monitor_gui.py
+++ b/interest_monitor_gui.py
@@ -128,53 +128,79 @@ class InterestMonitorApp:
read_count += 1
try:
log_entry = json.loads(line.strip())
- timestamp = log_entry.get("timestamp")
+ timestamp = log_entry.get("timestamp") # 获取顶层时间戳
- # *** Add time filtering ***
- if timestamp is None or float(timestamp) < time_threshold:
- continue # Skip old or invalid entries
-
- stream_id = log_entry.get("stream_id")
- interest_level = log_entry.get("interest_level")
- group_name = log_entry.get(
- "group_name", stream_id
- ) # *** Get group_name, fallback to stream_id ***
- reply_probability = log_entry.get("reply_probability") # <--- 获取概率值
-
- # *** Check other required fields AFTER time filtering ***
- if stream_id is None or interest_level is None:
+ # *** 时间过滤 ***
+ if timestamp is None:
error_count += 1
- continue # 跳过无效行
+ continue # 跳过没有时间戳的行
+ try:
+ entry_timestamp = float(timestamp)
+ if entry_timestamp < time_threshold:
+ continue # 跳过时间过早的条目
+ except (ValueError, TypeError):
+ error_count += 1
+ continue # 跳过时间戳格式错误的行
- # 如果是第一次读到这个 stream_id,则创建 deque
- if stream_id not in new_stream_history:
- new_stream_history[stream_id] = deque(maxlen=MAX_HISTORY_POINTS)
- new_probability_history[stream_id] = deque(maxlen=MAX_HISTORY_POINTS) # <--- 创建概率 deque
- # 检查是否已有颜色,没有则分配
- if stream_id not in self.stream_colors:
- self.stream_colors[stream_id] = self.get_random_color()
- # *** Store the latest display name found for this stream_id ***
- new_stream_display_names[stream_id] = group_name
+ # --- 修改开始:迭代 subflows ---
+ subflows = log_entry.get("subflows")
+ if not isinstance(subflows, list): # 检查 subflows 是否存在且为列表
+ error_count += 1
+ continue # 跳过没有 subflows 或格式无效的行
- # 添加数据点
- new_stream_history[stream_id].append((float(timestamp), float(interest_level)))
- # 添加概率数据点 (如果存在)
- if reply_probability is not None:
+ for subflow_entry in subflows:
+ stream_id = subflow_entry.get("stream_id")
+ interest_level = subflow_entry.get("interest_level")
+ # 获取 group_name,如果不存在则回退到 stream_id
+ group_name = subflow_entry.get("group_name", stream_id)
+ reply_probability = subflow_entry.get("reply_probability") # 获取概率值
+
+ # *** 检查必要的字段 ***
+ # 注意:时间戳已在顶层检查过
+ if stream_id is None or interest_level is None:
+ # 这里可以选择记录子流错误,但暂时跳过
+ continue # 跳过无效的 subflow 条目
+
+ # 确保 interest_level 可以转换为浮点数
try:
- new_probability_history[stream_id].append((float(timestamp), float(reply_probability)))
- except (TypeError, ValueError):
- # 如果概率值无效,可以跳过或记录一个默认值,这里跳过
- pass
+ interest_level_float = float(interest_level)
+ except (ValueError, TypeError):
+ continue # 跳过 interest_level 无效的 subflow
+
+ # 如果是第一次读到这个 stream_id,则创建 deque
+ if stream_id not in new_stream_history:
+ new_stream_history[stream_id] = deque(maxlen=MAX_HISTORY_POINTS)
+ new_probability_history[stream_id] = deque(maxlen=MAX_HISTORY_POINTS) # 创建概率 deque
+ # 检查是否已有颜色,没有则分配
+ if stream_id not in self.stream_colors:
+ self.stream_colors[stream_id] = self.get_random_color()
+
+ # *** 存储此 stream_id 最新的显示名称 ***
+ new_stream_display_names[stream_id] = group_name
+
+ # 添加数据点 (使用顶层时间戳)
+ new_stream_history[stream_id].append((entry_timestamp, interest_level_float))
+
+ # 添加概率数据点 (如果存在且有效)
+ if reply_probability is not None:
+ try:
+ # 尝试将概率转换为浮点数
+ probability_float = float(reply_probability)
+ new_probability_history[stream_id].append((entry_timestamp, probability_float))
+ except (TypeError, ValueError):
+ # 如果概率值无效,可以跳过或记录一个默认值,这里跳过
+ pass
+ # --- 修改结束 ---
except json.JSONDecodeError:
error_count += 1
# logger.warning(f"Skipping invalid JSON line: {line.strip()}")
continue # 跳过无法解析的行
- except (TypeError, ValueError):
- error_count += 1
- # logger.warning(f"Skipping line due to data type error ({e}): {line.strip()}")
- continue # 跳过数据类型错误的行
+ # except (TypeError, ValueError) as e: # 这个外层 catch 可能不再需要,因为类型错误在内部处理了
+ # error_count += 1
+ # # logger.warning(f"Skipping line due to data type error ({e}): {line.strip()}")
+ # continue # 跳过数据类型错误的行
# 读取完成后,用新数据替换旧数据
self.stream_history = new_stream_history
From 6da2a77b785936bbf53d379dd6c2a2eb690e7a9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 23:24:08 +0800
Subject: [PATCH 14/28] =?UTF-8?q?refactor(logger):=20=E4=B8=BAbase=5Ftool?=
=?UTF-8?q?=E5=92=8Cperson=5Finfo=E6=A8=A1=E5=9D=97=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=97=A5=E5=BF=97=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
为base_tool和person_info模块引入自定义日志配置,提升日志输出的可读性和一致性。分别在logger.py中新增BASE_TOOL_STYLE_CONFIG和PERSON_INFO_STYLE_CONFIG配置,并在相应模块中应用这些配置。
---
src/common/logger.py | 38 ++++++++++++++++++++++++++
src/do_tool/tool_can_use/base_tool.py | 9 ++++--
src/plugins/person_info/person_info.py | 9 ++++--
3 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/src/common/logger.py b/src/common/logger.py
index 5169b7b7..652d95d8 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -331,6 +331,42 @@ SUB_HEARTFLOW_STYLE_CONFIG = {
},
}
+BASE_TOOL_STYLE_CONFIG = {
+ "advanced": {
+ "console_format": (
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
+ "工具使用 | "
+ "{message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 工具使用 | {message}",
+ },
+ "simple": {
+ "console_format": (
+ "{time:MM-DD HH:mm} | 工具使用 | {message}"
+ ), # noqa: E501
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 工具使用 | {message}",
+ },
+}
+
+PERSON_INFO_STYLE_CONFIG = {
+ "advanced": {
+ "console_format": (
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
+ "人物信息 | "
+ "{message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 人物信息 | {message}",
+ },
+ "simple": {
+ "console_format": (
+ "{time:MM-DD HH:mm} | 人物信息 | {message}"
+ ), # noqa: E501
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 人物信息 | {message}",
+ },
+}
+
BACKGROUND_TASKS_STYLE_CONFIG = {
"advanced": {
"console_format": (
@@ -444,6 +480,8 @@ CONFIRM_STYLE_CONFIG = {
MAIN_STYLE_CONFIG = MAIN_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MAIN_STYLE_CONFIG["advanced"]
EMOJI_STYLE_CONFIG = EMOJI_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else EMOJI_STYLE_CONFIG["advanced"]
REMOTE_STYLE_CONFIG = REMOTE_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else REMOTE_STYLE_CONFIG["advanced"]
+BASE_TOOL_STYLE_CONFIG = BASE_TOOL_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else BASE_TOOL_STYLE_CONFIG["advanced"]
+PERSON_INFO_STYLE_CONFIG = PERSON_INFO_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else PERSON_INFO_STYLE_CONFIG["advanced"]
BACKGROUND_TASKS_STYLE_CONFIG = (
BACKGROUND_TASKS_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else BACKGROUND_TASKS_STYLE_CONFIG["advanced"]
)
diff --git a/src/do_tool/tool_can_use/base_tool.py b/src/do_tool/tool_can_use/base_tool.py
index b1edf805..df332262 100644
--- a/src/do_tool/tool_can_use/base_tool.py
+++ b/src/do_tool/tool_can_use/base_tool.py
@@ -3,9 +3,14 @@ import inspect
import importlib
import pkgutil
import os
-from src.common.logger import get_module_logger
+from src.common.logger import get_module_logger, LogConfig, BASE_TOOL_STYLE_CONFIG
-logger = get_module_logger("base_tool")
+base_tool_log_config = LogConfig(
+ console_format=BASE_TOOL_STYLE_CONFIG["console_format"],
+ file_format=BASE_TOOL_STYLE_CONFIG["file_format"],
+)
+
+logger = get_module_logger("base_tool", config=base_tool_log_config)
# 工具注册表
TOOL_REGISTRY = {}
diff --git a/src/plugins/person_info/person_info.py b/src/plugins/person_info/person_info.py
index fc9b47c0..e4f4004e 100644
--- a/src/plugins/person_info/person_info.py
+++ b/src/plugins/person_info/person_info.py
@@ -1,4 +1,4 @@
-from src.common.logger import get_module_logger
+from src.common.logger import get_module_logger, LogConfig, PERSON_INFO_STYLE_CONFIG
from ...common.database import db
import copy
import hashlib
@@ -33,7 +33,12 @@ PersonInfoManager 类方法功能摘要:
9. personal_habit_deduction - 定时推断个人习惯
"""
-logger = get_module_logger("person_info")
+person_info_log_config = LogConfig(
+ console_format=PERSON_INFO_STYLE_CONFIG["console_format"],
+ file_format=PERSON_INFO_STYLE_CONFIG["file_format"],
+)
+
+logger = get_module_logger("person_info", config=person_info_log_config)
person_info_default = {
"person_id": None,
From 95210e8b2598e9490a8cf1fbee047565b4626669 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 15:24:27 +0000
Subject: [PATCH 15/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/common/logger.py | 4 ++--
src/do_tool/tool_can_use/base_tool.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/common/logger.py b/src/common/logger.py
index 652d95d8..67a38594 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -346,7 +346,7 @@ BASE_TOOL_STYLE_CONFIG = {
"{time:MM-DD HH:mm} | 工具使用 | {message}"
), # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 工具使用 | {message}",
- },
+ },
}
PERSON_INFO_STYLE_CONFIG = {
@@ -364,7 +364,7 @@ PERSON_INFO_STYLE_CONFIG = {
"{time:MM-DD HH:mm} | 人物信息 | {message}"
), # noqa: E501
"file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 人物信息 | {message}",
- },
+ },
}
BACKGROUND_TASKS_STYLE_CONFIG = {
diff --git a/src/do_tool/tool_can_use/base_tool.py b/src/do_tool/tool_can_use/base_tool.py
index df332262..7a89369f 100644
--- a/src/do_tool/tool_can_use/base_tool.py
+++ b/src/do_tool/tool_can_use/base_tool.py
@@ -7,7 +7,7 @@ from src.common.logger import get_module_logger, LogConfig, BASE_TOOL_STYLE_CONF
base_tool_log_config = LogConfig(
console_format=BASE_TOOL_STYLE_CONFIG["console_format"],
- file_format=BASE_TOOL_STYLE_CONFIG["file_format"],
+ file_format=BASE_TOOL_STYLE_CONFIG["file_format"],
)
logger = get_module_logger("base_tool", config=base_tool_log_config)
From f825da590d1ad1c55b47fddb7b32bc0d70bf6225 Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Wed, 23 Apr 2025 23:27:29 +0800
Subject: [PATCH 16/28] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=90=8D?=
=?UTF-8?q?=E5=AD=97=E8=AF=86=E5=88=AB=E9=94=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
interest_monitor_gui.py | 115 ++++++++++++++++++++++--
src/plugins/heartFC_chat/normal_chat.py | 2 +-
2 files changed, 111 insertions(+), 6 deletions(-)
diff --git a/interest_monitor_gui.py b/interest_monitor_gui.py
index d5f1b15f..c713cc12 100644
--- a/interest_monitor_gui.py
+++ b/interest_monitor_gui.py
@@ -37,24 +37,57 @@ class InterestMonitorApp:
# 使用 deque 来存储有限的历史数据点
# key: stream_id, value: deque([(timestamp, interest_level), ...])
self.stream_history = {}
- # key: stream_id, value: deque([(timestamp, reply_probability), ...]) # <--- 新增:存储概率历史
+ # key: stream_id, value: deque([(timestamp, reply_probability), ...])
self.probability_history = {}
self.stream_colors = {} # 为每个 stream 分配颜色
- self.stream_display_names = {} # *** New: Store display names (group_name) ***
+ self.stream_display_names = {} # 存储显示名称 (group_name)
self.selected_stream_id = tk.StringVar() # 用于 Combobox 绑定
+ # --- 新增:存储其他参数 ---
+ # 顶层信息
+ self.latest_main_mind = tk.StringVar(value="N/A")
+ self.latest_mai_state = tk.StringVar(value="N/A")
+ self.latest_subflow_count = tk.IntVar(value=0)
+ # 子流最新状态 (key: stream_id)
+ self.stream_sub_minds = {}
+ self.stream_chat_states = {}
+ self.stream_threshold_status = {}
+ self.stream_last_active = {}
+ self.stream_last_interaction = {}
+ # 用于显示单个流详情的 StringVar
+ self.single_stream_sub_mind = tk.StringVar(value="想法: N/A")
+ self.single_stream_chat_state = tk.StringVar(value="状态: N/A")
+ self.single_stream_threshold = tk.StringVar(value="阈值: N/A")
+ self.single_stream_last_active = tk.StringVar(value="活跃: N/A")
+ self.single_stream_last_interaction = tk.StringVar(value="交互: N/A")
+
+
# --- UI 元素 ---
+
+ # --- 新增:顶部全局信息框架 ---
+ self.global_info_frame = ttk.Frame(root, padding="5 0 5 5") # 顶部内边距调整
+ self.global_info_frame.pack(side=tk.TOP, fill=tk.X, pady=(5, 0)) # 底部外边距为0
+
+ ttk.Label(self.global_info_frame, text="全局状态:").pack(side=tk.LEFT, padx=(0, 10))
+ ttk.Label(self.global_info_frame, textvariable=self.latest_mai_state).pack(side=tk.LEFT, padx=5)
+ ttk.Label(self.global_info_frame, text="想法:").pack(side=tk.LEFT, padx=(10, 0))
+ ttk.Label(self.global_info_frame, textvariable=self.latest_main_mind).pack(side=tk.LEFT, padx=5)
+ ttk.Label(self.global_info_frame, text="子流数:").pack(side=tk.LEFT, padx=(10, 0))
+ ttk.Label(self.global_info_frame, textvariable=self.latest_subflow_count).pack(side=tk.LEFT, padx=5)
+
+
# 创建 Notebook (选项卡控件)
self.notebook = ttk.Notebook(root)
- self.notebook.pack(pady=10, padx=10, fill=tk.BOTH, expand=1)
+ # 修改:fill 和 expand,让 notebook 填充剩余空间
+ self.notebook.pack(pady=(5, 0), padx=10, fill=tk.BOTH, expand=1) #顶部外边距改小
# --- 第一个选项卡:所有流 ---
self.frame_all = ttk.Frame(self.notebook, padding="5 5 5 5")
self.notebook.add(self.frame_all, text="所有聊天流")
- # 状态标签
+ # 状态标签 (移动到最底部)
self.status_label = tk.Label(root, text="Initializing...", anchor="w", fg="grey")
- self.status_label.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=2)
+ self.status_label.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=(0, 5)) # 调整边距
# Matplotlib 图表设置 (用于第一个选项卡)
self.fig = Figure(figsize=(5, 4), dpi=100)
@@ -81,6 +114,16 @@ class InterestMonitorApp:
self.stream_selector.pack(side=tk.LEFT, fill=tk.X, expand=True)
self.stream_selector.bind("<>", self.on_stream_selected)
+ # --- 新增:单个流详情显示区域 ---
+ self.single_stream_details_frame = ttk.Frame(self.frame_single, padding="5 5 5 0")
+ self.single_stream_details_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 5))
+
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_sub_mind).pack(side=tk.LEFT, padx=5)
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_chat_state).pack(side=tk.LEFT, padx=5)
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_threshold).pack(side=tk.LEFT, padx=5)
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_last_active).pack(side=tk.LEFT, padx=5)
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_last_interaction).pack(side=tk.LEFT, padx=5)
+
# Matplotlib 图表设置 (用于第二个选项卡)
self.fig_single = Figure(figsize=(5, 4), dpi=100)
# 修改:创建两个子图,一个显示兴趣度,一个显示概率
@@ -116,6 +159,11 @@ class InterestMonitorApp:
new_stream_history = {}
new_stream_display_names = {}
new_probability_history = {} # <--- 重置概率历史
+ # --- 新增:重置其他子流状态 --- (如果需要的话,但通常覆盖即可)
+ # self.stream_sub_minds = {}
+ # self.stream_chat_states = {}
+ # ... 等等 ...
+
read_count = 0
error_count = 0
# *** Calculate the timestamp threshold for the last 30 minutes ***
@@ -142,6 +190,10 @@ class InterestMonitorApp:
error_count += 1
continue # 跳过时间戳格式错误的行
+ # --- 新增:更新顶层信息 (使用最后一个有效行的数据) ---
+ self.latest_main_mind.set(log_entry.get("main_mind", self.latest_main_mind.get())) # 保留旧值如果缺失
+ self.latest_mai_state.set(log_entry.get("mai_state", self.latest_mai_state.get()))
+ self.latest_subflow_count.set(log_entry.get("subflow_count", self.latest_subflow_count.get()))
# --- 修改开始:迭代 subflows ---
subflows = log_entry.get("subflows")
@@ -179,6 +231,13 @@ class InterestMonitorApp:
# *** 存储此 stream_id 最新的显示名称 ***
new_stream_display_names[stream_id] = group_name
+ # --- 新增:存储其他子流信息 ---
+ self.stream_sub_minds[stream_id] = subflow_entry.get("sub_mind", "N/A")
+ self.stream_chat_states[stream_id] = subflow_entry.get("sub_chat_state", "N/A")
+ self.stream_threshold_status[stream_id] = subflow_entry.get("is_above_threshold", False)
+ self.stream_last_active[stream_id] = subflow_entry.get("last_active_time") # 存储原始时间戳
+ self.stream_last_interaction[stream_id] = subflow_entry.get("last_interaction_time") # 存储原始时间戳
+
# 添加数据点 (使用顶层时间戳)
new_stream_history[stream_id].append((entry_timestamp, interest_level_float))
@@ -206,6 +265,16 @@ class InterestMonitorApp:
self.stream_history = new_stream_history
self.stream_display_names = new_stream_display_names # *** Update display names ***
self.probability_history = new_probability_history # <--- 更新概率历史
+ # 清理不再存在的 stream_id 的附加信息 (可选,但保持一致性)
+ streams_to_remove = set(self.stream_sub_minds.keys()) - set(new_stream_history.keys())
+ for sid in streams_to_remove:
+ self.stream_sub_minds.pop(sid, None)
+ self.stream_chat_states.pop(sid, None)
+ self.stream_threshold_status.pop(sid, None)
+ self.stream_last_active.pop(sid, None)
+ self.stream_last_interaction.pop(sid, None)
+ # 颜色和显示名称也应该清理,但当前逻辑是保留旧颜色
+ # self.stream_colors.pop(sid, None)
status_msg = f"Data loaded at {datetime.now().strftime('%H:%M:%S')}. Lines read: {read_count}."
if error_count > 0:
status_msg += f" Skipped {error_count} invalid lines."
@@ -409,9 +478,45 @@ class InterestMonitorApp:
self.ax_single_interest.set_xlim(one_hour_ago, now)
# self.ax_single_probability.set_xlim(one_hour_ago, now) # sharex 会自动同步
+ # --- 新增:更新单个流的详细信息标签 ---
+ self.update_single_stream_details(selected_sid)
+
# --- 新增:重新绘制画布 ---
self.canvas_single.draw()
+ def format_timestamp(self, ts):
+ """辅助函数:格式化时间戳,处理 None 或无效值"""
+ if ts is None:
+ return "N/A"
+ try:
+ # 假设 ts 是 float 类型的时间戳
+ dt_object = datetime.fromtimestamp(float(ts))
+ return dt_object.strftime("%Y-%m-%d %H:%M:%S")
+ except (ValueError, TypeError):
+ return "Invalid Time"
+
+ def update_single_stream_details(self, stream_id):
+ """更新单个流详情区域的标签内容"""
+ if stream_id:
+ sub_mind = self.stream_sub_minds.get(stream_id, "N/A")
+ chat_state = self.stream_chat_states.get(stream_id, "N/A")
+ threshold = self.stream_threshold_status.get(stream_id, False)
+ last_active_ts = self.stream_last_active.get(stream_id)
+ last_interaction_ts = self.stream_last_interaction.get(stream_id)
+
+ self.single_stream_sub_mind.set(f"想法: {sub_mind}")
+ self.single_stream_chat_state.set(f"状态: {chat_state}")
+ self.single_stream_threshold.set(f"阈值以上: {'是' if threshold else '否'}")
+ self.single_stream_last_active.set(f"最后活跃: {self.format_timestamp(last_active_ts)}")
+ self.single_stream_last_interaction.set(f"最后交互: {self.format_timestamp(last_interaction_ts)}")
+ else:
+ # 如果没有选择流,则清空详情
+ self.single_stream_sub_mind.set("想法: N/A")
+ self.single_stream_chat_state.set("状态: N/A")
+ self.single_stream_threshold.set("阈值: N/A")
+ self.single_stream_last_active.set("活跃: N/A")
+ self.single_stream_last_interaction.set("交互: N/A")
+
def update_display(self):
"""主更新循环"""
try:
diff --git a/src/plugins/heartFC_chat/normal_chat.py b/src/plugins/heartFC_chat/normal_chat.py
index 902623eb..4acacd6d 100644
--- a/src/plugins/heartFC_chat/normal_chat.py
+++ b/src/plugins/heartFC_chat/normal_chat.py
@@ -230,7 +230,7 @@ class NormalChat:
willing_log = f"[回复意愿:{await willing_manager.get_willing(self.stream_id):.2f}]" if is_willing else ""
logger.info(
f"[{current_time}][{mes_name}]"
- f"{self.chat_stream.user_info.user_nickname}:" # 使用 self.chat_stream
+ f"{message.message_info.user_info.user_nickname}:" # 使用 self.chat_stream
f"{message.processed_plain_text}{willing_log}[概率:{reply_probability * 100:.1f}%]"
)
do_reply = False
From bbe70586f24e732f85c5d3ebfda499020a0e8051 Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Wed, 23 Apr 2025 23:30:13 +0800
Subject: [PATCH 17/28] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E6=80=9D?=
=?UTF-8?q?=E8=80=83=E7=82=B8=E8=A3=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/heart_flow/sub_heartflow.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py
index f30e25b4..4f00b6d3 100644
--- a/src/heart_flow/sub_heartflow.py
+++ b/src/heart_flow/sub_heartflow.py
@@ -501,7 +501,7 @@ class SubHeartflow:
extra_info_prompt = "无工具信息。\n"
individuality = Individuality.get_instance()
- prompt_personality = f"你的名字是{individuality.bot_nickname},你"
+ prompt_personality = f"你的名字是{individuality.personality.bot_nickname},你"
prompt_personality += individuality.personality.personality_core
if individuality.personality.personality_sides:
From 5172f2d22c6b9db09827f942d7a3a7337f2e6f08 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 23:30:59 +0800
Subject: [PATCH 18/28] =?UTF-8?q?=E7=AB=9F=E7=84=B6=E8=BF=98=E6=BC=8F?=
=?UTF-8?q?=E4=BA=86=E4=B8=80=E4=B8=AA=EF=BC=9F=E6=9D=80=EF=BC=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/common/logger.py | 21 +++++++++++++++++++++
src/heart_flow/subheartflow_manager.py | 9 +++++++--
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/src/common/logger.py b/src/common/logger.py
index 67a38594..4347fd97 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -385,6 +385,24 @@ BACKGROUND_TASKS_STYLE_CONFIG = {
},
}
+SUBHEARTFLOW_MANAGER_STYLE_CONFIG = {
+ "advanced": {
+ "console_format": (
+ "{time:YYYY-MM-DD HH:mm:ss} | "
+ "{level: <8} | "
+ "小脑袋管理 | "
+ "{message}"
+ ),
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 小脑袋管理 | {message}",
+ },
+ "simple": {
+ "console_format": (
+ "{time:MM-DD HH:mm} | 小脑袋管理 | {message}"
+ ), # noqa: E501
+ "file_format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 小脑袋管理 | {message}",
+ },
+}
+
WILLING_STYLE_CONFIG = {
"advanced": {
"console_format": (
@@ -482,6 +500,9 @@ EMOJI_STYLE_CONFIG = EMOJI_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else EMOJI_ST
REMOTE_STYLE_CONFIG = REMOTE_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else REMOTE_STYLE_CONFIG["advanced"]
BASE_TOOL_STYLE_CONFIG = BASE_TOOL_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else BASE_TOOL_STYLE_CONFIG["advanced"]
PERSON_INFO_STYLE_CONFIG = PERSON_INFO_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else PERSON_INFO_STYLE_CONFIG["advanced"]
+SUBHEARTFLOW_MANAGER_STYLE_CONFIG = (
+ SUBHEARTFLOW_MANAGER_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SUBHEARTFLOW_MANAGER_STYLE_CONFIG["advanced"]
+)
BACKGROUND_TASKS_STYLE_CONFIG = (
BACKGROUND_TASKS_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else BACKGROUND_TASKS_STYLE_CONFIG["advanced"]
)
diff --git a/src/heart_flow/subheartflow_manager.py b/src/heart_flow/subheartflow_manager.py
index 4a207f93..02f104bf 100644
--- a/src/heart_flow/subheartflow_manager.py
+++ b/src/heart_flow/subheartflow_manager.py
@@ -4,7 +4,7 @@ import random
from typing import Dict, Any, Optional, List
# 导入日志模块
-from src.common.logger import get_module_logger
+from src.common.logger import get_module_logger, LogConfig, SUBHEARTFLOW_MANAGER_STYLE_CONFIG
# 导入聊天流管理模块
from src.plugins.chat.chat_stream import chat_manager
@@ -15,7 +15,12 @@ from src.heart_flow.mai_state_manager import MaiState, MaiStateInfo
from .observation import ChattingObservation
# 初始化日志记录器
-logger = get_module_logger("subheartflow_manager")
+
+subheartflow_manager_log_config = LogConfig(
+ console_format=SUBHEARTFLOW_MANAGER_STYLE_CONFIG["console_format"],
+ file_format=SUBHEARTFLOW_MANAGER_STYLE_CONFIG["file_format"],
+)
+logger = get_module_logger("subheartflow_manager", config=subheartflow_manager_log_config)
# 子心流管理相关常量
INACTIVE_THRESHOLD_SECONDS = 1200 # 子心流不活跃超时时间(秒)
From 3dd50aeb0ac8d76542955bdf60412544bbabcd53 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 23:34:54 +0800
Subject: [PATCH 19/28] =?UTF-8?q?ban=E6=8E=89fastapi=E7=9A=84logger?=
=?UTF-8?q?=E8=BE=93=E5=87=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/common/server.py | 3 ++-
src/plugins/message/api.py | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/common/server.py b/src/common/server.py
index a4998a30..51799629 100644
--- a/src/common/server.py
+++ b/src/common/server.py
@@ -45,7 +45,8 @@ class Server:
async def run(self):
"""启动服务器"""
- config = Config(app=self.app, host=self._host, port=self._port)
+ # 禁用 uvicorn 默认日志和访问日志
+ config = Config(app=self.app, host=self._host, port=self._port, log_config=None, access_log=False)
self._server = UvicornServer(config=config)
try:
await self._server.serve()
diff --git a/src/plugins/message/api.py b/src/plugins/message/api.py
index e01289e9..31302f51 100644
--- a/src/plugins/message/api.py
+++ b/src/plugins/message/api.py
@@ -147,7 +147,8 @@ class MessageServer(BaseMessageHandler):
try:
if self.own_app:
# 如果使用自己的 FastAPI 实例,运行 uvicorn 服务器
- config = uvicorn.Config(self.app, host=self.host, port=self.port, loop="asyncio")
+ # 禁用 uvicorn 默认日志和访问日志
+ config = uvicorn.Config(self.app, host=self.host, port=self.port, loop="asyncio", log_config=None, access_log=False)
self.server = uvicorn.Server(config)
await self.server.serve()
else:
From c4499829a657813a0e900360ae0a065aa3f4aaea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com>
Date: Wed, 23 Apr 2025 23:37:07 +0800
Subject: [PATCH 20/28] fix: Ruff
---
src/heart_flow/background_tasks.py | 4 ++--
src/heart_flow/sub_heartflow.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/heart_flow/background_tasks.py b/src/heart_flow/background_tasks.py
index d6a9869b..f5784dfa 100644
--- a/src/heart_flow/background_tasks.py
+++ b/src/heart_flow/background_tasks.py
@@ -6,7 +6,7 @@ from src.common.logger import get_module_logger, LogConfig, BACKGROUND_TASKS_STY
# Need manager types for dependency injection
from src.heart_flow.mai_state_manager import MaiStateManager, MaiStateInfo
-from src.heart_flow.subheartflow_manager import SubHeartflowManager, ChatState
+from src.heart_flow.subheartflow_manager import SubHeartflowManager
from src.heart_flow.interest_logger import InterestLogger
background_tasks_log_config = LogConfig(
@@ -97,7 +97,7 @@ class BackgroundTaskManager:
]
# 统一启动所有任务
- for task_var, task_func, task_name, log_level, log_msg, task_attr_name in task_configs:
+ for _task_var, task_func, task_name, log_level, log_msg, task_attr_name in task_configs:
# 检查任务变量是否存在且未完成
current_task_var = getattr(self, task_attr_name)
if current_task_var is None or current_task_var.done():
diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py
index 4f00b6d3..0e39946e 100644
--- a/src/heart_flow/sub_heartflow.py
+++ b/src/heart_flow/sub_heartflow.py
@@ -18,7 +18,7 @@ from src.plugins.chat.chat_stream import chat_manager
import math
from src.plugins.heartFC_chat.heartFC_chat import HeartFChatting
from src.plugins.heartFC_chat.normal_chat import NormalChat
-from src.do_tool.tool_use import ToolUser
+# from src.do_tool.tool_use import ToolUser
from src.heart_flow.mai_state_manager import MaiStateInfo
From 1b380ff13101045211a56f8c7ed8af7870ddf4c8 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 15:37:28 +0000
Subject: [PATCH 21/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
interest_monitor_gui.py | 54 +++++++-----
src/heart_flow/background_tasks.py | 83 ++++++++++--------
src/heart_flow/interest_logger.py | 6 +-
src/heart_flow/sub_heartflow.py | 87 ++++++++++---------
src/heart_flow/subheartflow_manager.py | 42 +++++----
src/plugins/heartFC_chat/heartFC_chat.py | 13 ++-
.../heartFC_chat/heartflow_processor.py | 1 -
src/plugins/heartFC_chat/normal_chat.py | 4 +-
src/plugins/message/api.py | 4 +-
9 files changed, 160 insertions(+), 134 deletions(-)
diff --git a/interest_monitor_gui.py b/interest_monitor_gui.py
index c713cc12..97536f70 100644
--- a/interest_monitor_gui.py
+++ b/interest_monitor_gui.py
@@ -40,7 +40,7 @@ class InterestMonitorApp:
# key: stream_id, value: deque([(timestamp, reply_probability), ...])
self.probability_history = {}
self.stream_colors = {} # 为每个 stream 分配颜色
- self.stream_display_names = {} # 存储显示名称 (group_name)
+ self.stream_display_names = {} # 存储显示名称 (group_name)
self.selected_stream_id = tk.StringVar() # 用于 Combobox 绑定
# --- 新增:存储其他参数 ---
@@ -61,12 +61,11 @@ class InterestMonitorApp:
self.single_stream_last_active = tk.StringVar(value="活跃: N/A")
self.single_stream_last_interaction = tk.StringVar(value="交互: N/A")
-
# --- UI 元素 ---
# --- 新增:顶部全局信息框架 ---
- self.global_info_frame = ttk.Frame(root, padding="5 0 5 5") # 顶部内边距调整
- self.global_info_frame.pack(side=tk.TOP, fill=tk.X, pady=(5, 0)) # 底部外边距为0
+ self.global_info_frame = ttk.Frame(root, padding="5 0 5 5") # 顶部内边距调整
+ self.global_info_frame.pack(side=tk.TOP, fill=tk.X, pady=(5, 0)) # 底部外边距为0
ttk.Label(self.global_info_frame, text="全局状态:").pack(side=tk.LEFT, padx=(0, 10))
ttk.Label(self.global_info_frame, textvariable=self.latest_mai_state).pack(side=tk.LEFT, padx=5)
@@ -75,11 +74,10 @@ class InterestMonitorApp:
ttk.Label(self.global_info_frame, text="子流数:").pack(side=tk.LEFT, padx=(10, 0))
ttk.Label(self.global_info_frame, textvariable=self.latest_subflow_count).pack(side=tk.LEFT, padx=5)
-
# 创建 Notebook (选项卡控件)
self.notebook = ttk.Notebook(root)
# 修改:fill 和 expand,让 notebook 填充剩余空间
- self.notebook.pack(pady=(5, 0), padx=10, fill=tk.BOTH, expand=1) #顶部外边距改小
+ self.notebook.pack(pady=(5, 0), padx=10, fill=tk.BOTH, expand=1) # 顶部外边距改小
# --- 第一个选项卡:所有流 ---
self.frame_all = ttk.Frame(self.notebook, padding="5 5 5 5")
@@ -87,7 +85,7 @@ class InterestMonitorApp:
# 状态标签 (移动到最底部)
self.status_label = tk.Label(root, text="Initializing...", anchor="w", fg="grey")
- self.status_label.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=(0, 5)) # 调整边距
+ self.status_label.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=(0, 5)) # 调整边距
# Matplotlib 图表设置 (用于第一个选项卡)
self.fig = Figure(figsize=(5, 4), dpi=100)
@@ -119,10 +117,18 @@ class InterestMonitorApp:
self.single_stream_details_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 5))
ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_sub_mind).pack(side=tk.LEFT, padx=5)
- ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_chat_state).pack(side=tk.LEFT, padx=5)
- ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_threshold).pack(side=tk.LEFT, padx=5)
- ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_last_active).pack(side=tk.LEFT, padx=5)
- ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_last_interaction).pack(side=tk.LEFT, padx=5)
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_chat_state).pack(
+ side=tk.LEFT, padx=5
+ )
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_threshold).pack(
+ side=tk.LEFT, padx=5
+ )
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_last_active).pack(
+ side=tk.LEFT, padx=5
+ )
+ ttk.Label(self.single_stream_details_frame, textvariable=self.single_stream_last_interaction).pack(
+ side=tk.LEFT, padx=5
+ )
# Matplotlib 图表设置 (用于第二个选项卡)
self.fig_single = Figure(figsize=(5, 4), dpi=100)
@@ -176,37 +182,39 @@ class InterestMonitorApp:
read_count += 1
try:
log_entry = json.loads(line.strip())
- timestamp = log_entry.get("timestamp") # 获取顶层时间戳
+ timestamp = log_entry.get("timestamp") # 获取顶层时间戳
# *** 时间过滤 ***
if timestamp is None:
error_count += 1
- continue # 跳过没有时间戳的行
+ continue # 跳过没有时间戳的行
try:
entry_timestamp = float(timestamp)
if entry_timestamp < time_threshold:
continue # 跳过时间过早的条目
except (ValueError, TypeError):
error_count += 1
- continue # 跳过时间戳格式错误的行
+ continue # 跳过时间戳格式错误的行
# --- 新增:更新顶层信息 (使用最后一个有效行的数据) ---
- self.latest_main_mind.set(log_entry.get("main_mind", self.latest_main_mind.get())) # 保留旧值如果缺失
+ self.latest_main_mind.set(
+ log_entry.get("main_mind", self.latest_main_mind.get())
+ ) # 保留旧值如果缺失
self.latest_mai_state.set(log_entry.get("mai_state", self.latest_mai_state.get()))
self.latest_subflow_count.set(log_entry.get("subflow_count", self.latest_subflow_count.get()))
# --- 修改开始:迭代 subflows ---
subflows = log_entry.get("subflows")
- if not isinstance(subflows, list): # 检查 subflows 是否存在且为列表
+ if not isinstance(subflows, list): # 检查 subflows 是否存在且为列表
error_count += 1
- continue # 跳过没有 subflows 或格式无效的行
+ continue # 跳过没有 subflows 或格式无效的行
for subflow_entry in subflows:
stream_id = subflow_entry.get("stream_id")
interest_level = subflow_entry.get("interest_level")
# 获取 group_name,如果不存在则回退到 stream_id
group_name = subflow_entry.get("group_name", stream_id)
- reply_probability = subflow_entry.get("reply_probability") # 获取概率值
+ reply_probability = subflow_entry.get("reply_probability") # 获取概率值
# *** 检查必要的字段 ***
# 注意:时间戳已在顶层检查过
@@ -218,12 +226,12 @@ class InterestMonitorApp:
try:
interest_level_float = float(interest_level)
except (ValueError, TypeError):
- continue # 跳过 interest_level 无效的 subflow
+ continue # 跳过 interest_level 无效的 subflow
# 如果是第一次读到这个 stream_id,则创建 deque
if stream_id not in new_stream_history:
new_stream_history[stream_id] = deque(maxlen=MAX_HISTORY_POINTS)
- new_probability_history[stream_id] = deque(maxlen=MAX_HISTORY_POINTS) # 创建概率 deque
+ new_probability_history[stream_id] = deque(maxlen=MAX_HISTORY_POINTS) # 创建概率 deque
# 检查是否已有颜色,没有则分配
if stream_id not in self.stream_colors:
self.stream_colors[stream_id] = self.get_random_color()
@@ -235,8 +243,10 @@ class InterestMonitorApp:
self.stream_sub_minds[stream_id] = subflow_entry.get("sub_mind", "N/A")
self.stream_chat_states[stream_id] = subflow_entry.get("sub_chat_state", "N/A")
self.stream_threshold_status[stream_id] = subflow_entry.get("is_above_threshold", False)
- self.stream_last_active[stream_id] = subflow_entry.get("last_active_time") # 存储原始时间戳
- self.stream_last_interaction[stream_id] = subflow_entry.get("last_interaction_time") # 存储原始时间戳
+ self.stream_last_active[stream_id] = subflow_entry.get("last_active_time") # 存储原始时间戳
+ self.stream_last_interaction[stream_id] = subflow_entry.get(
+ "last_interaction_time"
+ ) # 存储原始时间戳
# 添加数据点 (使用顶层时间戳)
new_stream_history[stream_id].append((entry_timestamp, interest_level_float))
diff --git a/src/heart_flow/background_tasks.py b/src/heart_flow/background_tasks.py
index f5784dfa..7fb63bf6 100644
--- a/src/heart_flow/background_tasks.py
+++ b/src/heart_flow/background_tasks.py
@@ -46,54 +46,59 @@ class BackgroundTaskManager:
self.cleanup_interval = cleanup_interval
self.log_interval = log_interval
self.inactive_threshold = inactive_threshold # For cleanup task
- self.interest_eval_interval = interest_eval_interval # 存储兴趣评估间隔
+ self.interest_eval_interval = interest_eval_interval # 存储兴趣评估间隔
# Task references
self._state_update_task: Optional[asyncio.Task] = None
self._cleanup_task: Optional[asyncio.Task] = None
self._logging_task: Optional[asyncio.Task] = None
- self._interest_eval_task: Optional[asyncio.Task] = None # 新增兴趣评估任务引用
+ self._interest_eval_task: Optional[asyncio.Task] = None # 新增兴趣评估任务引用
self._tasks: List[Optional[asyncio.Task]] = [] # Keep track of all tasks
async def start_tasks(self):
"""启动所有后台任务
-
+
功能说明:
- 启动核心后台任务: 状态更新、清理、日志记录和兴趣评估
- 每个任务启动前检查是否已在运行
- 将任务引用保存到任务列表
"""
-
+
# 任务配置列表: (任务变量名, 任务函数, 任务名称, 日志级别, 额外日志信息, 任务对象引用属性名)
task_configs = [
- (self._state_update_task,
- lambda: self._run_state_update_cycle(self.update_interval),
- "hf_state_update",
- "debug",
- f"聊天状态更新任务已启动 间隔:{self.update_interval}s",
- "_state_update_task"),
-
- (self._cleanup_task,
- self._run_cleanup_cycle,
- "hf_cleanup",
- "info",
- f"清理任务已启动 间隔:{self.cleanup_interval}s 阈值:{self.inactive_threshold}s",
- "_cleanup_task"),
-
- (self._logging_task,
- self._run_logging_cycle,
- "hf_logging",
- "info",
- f"日志任务已启动 间隔:{self.log_interval}s",
- "_logging_task"),
-
+ (
+ self._state_update_task,
+ lambda: self._run_state_update_cycle(self.update_interval),
+ "hf_state_update",
+ "debug",
+ f"聊天状态更新任务已启动 间隔:{self.update_interval}s",
+ "_state_update_task",
+ ),
+ (
+ self._cleanup_task,
+ self._run_cleanup_cycle,
+ "hf_cleanup",
+ "info",
+ f"清理任务已启动 间隔:{self.cleanup_interval}s 阈值:{self.inactive_threshold}s",
+ "_cleanup_task",
+ ),
+ (
+ self._logging_task,
+ self._run_logging_cycle,
+ "hf_logging",
+ "info",
+ f"日志任务已启动 间隔:{self.log_interval}s",
+ "_logging_task",
+ ),
# 新增兴趣评估任务配置
- (self._interest_eval_task,
- self._run_interest_eval_cycle,
- "hf_interest_eval",
- "debug", # 设为debug,避免过多日志
- f"兴趣评估任务已启动 间隔:{self.interest_eval_interval}s",
- "_interest_eval_task"),
+ (
+ self._interest_eval_task,
+ self._run_interest_eval_cycle,
+ "hf_interest_eval",
+ "debug", # 设为debug,避免过多日志
+ f"兴趣评估任务已启动 间隔:{self.interest_eval_interval}s",
+ "_interest_eval_task",
+ ),
]
# 统一启动所有任务
@@ -103,9 +108,9 @@ class BackgroundTaskManager:
if current_task_var is None or current_task_var.done():
new_task = asyncio.create_task(task_func(), name=task_name)
setattr(self, task_attr_name, new_task) # 更新任务变量
- if new_task not in self._tasks: # 避免重复添加
+ if new_task not in self._tasks: # 避免重复添加
self._tasks.append(new_task)
-
+
# 根据配置记录不同级别的日志
getattr(logger, log_level)(log_msg)
else:
@@ -164,7 +169,7 @@ class BackgroundTaskManager:
# logger.warning(f"任务 {task_name} 超时执行 ({elapsed:.2f}s > {interval}s)")
await asyncio.sleep(sleep_time)
- logger.debug(f"任务循环结束: {task_name}") # 调整日志信息
+ logger.debug(f"任务循环结束: {task_name}") # 调整日志信息
async def _perform_state_update_work(self):
"""执行状态更新工作"""
@@ -222,6 +227,7 @@ class BackgroundTaskManager:
"""执行一轮子心流兴趣评估与提升检查。"""
# 直接调用 subheartflow_manager 的方法,并传递当前状态信息
await self.subheartflow_manager.evaluate_interest_and_promote(self.mai_state_info)
+
# --- 结束新增 ---
# --- Specific Task Runners --- #
@@ -237,14 +243,15 @@ class BackgroundTaskManager:
async def _run_logging_cycle(self):
await self._run_periodic_loop(
- task_name="State Logging",
- interval=self.log_interval,
- task_func=self._perform_logging_work
+ task_name="State Logging", interval=self.log_interval, task_func=self._perform_logging_work
)
# --- 新增兴趣评估任务运行器 ---
async def _run_interest_eval_cycle(self):
await self._run_periodic_loop(
- task_name="Interest Evaluation", interval=self.interest_eval_interval, task_func=self._perform_interest_eval_work
+ task_name="Interest Evaluation",
+ interval=self.interest_eval_interval,
+ task_func=self._perform_interest_eval_work,
)
+
# --- 结束新增 ---
diff --git a/src/heart_flow/interest_logger.py b/src/heart_flow/interest_logger.py
index 15a08b64..866ae4e0 100644
--- a/src/heart_flow/interest_logger.py
+++ b/src/heart_flow/interest_logger.py
@@ -60,9 +60,7 @@ class InterestLogger:
for subheartflow in all_flows:
if self.subheartflow_manager.get_subheartflow(subheartflow.subheartflow_id):
tasks.append(
- asyncio.create_task(
- subheartflow.get_full_state(), name=f"get_state_{subheartflow.subheartflow_id}"
- )
+ asyncio.create_task(subheartflow.get_full_state(), name=f"get_state_{subheartflow.subheartflow_id}")
)
else:
logger.warning(f"子心流 {subheartflow.subheartflow_id} 在创建任务前已消失")
@@ -107,7 +105,7 @@ class InterestLogger:
"main_mind": main_mind,
"mai_state": mai_state_name,
"subflow_count": len(all_subflow_states),
- "subflows": []
+ "subflows": [],
}
if not all_subflow_states:
diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py
index 0e39946e..0b17c278 100644
--- a/src/heart_flow/sub_heartflow.py
+++ b/src/heart_flow/sub_heartflow.py
@@ -18,6 +18,7 @@ from src.plugins.chat.chat_stream import chat_manager
import math
from src.plugins.heartFC_chat.heartFC_chat import HeartFChatting
from src.plugins.heartFC_chat.normal_chat import NormalChat
+
# from src.do_tool.tool_use import ToolUser
from src.heart_flow.mai_state_manager import MaiStateInfo
@@ -109,8 +110,8 @@ class InterestChatting:
self.interest_dict: Dict[str, tuple[MessageRecv, float, bool]] = {}
self.update_interval = 1.0
- self.start_updates(self.update_interval) # 初始化时启动后台更新任务
-
+ self.start_updates(self.update_interval) # 初始化时启动后台更新任务
+
self.above_threshold = False
self.start_hfc_probability = 0.0
@@ -120,36 +121,37 @@ class InterestChatting:
async def _calculate_decay(self):
"""计算兴趣值的衰减
-
+
参数:
current_time: 当前时间戳
-
+
处理逻辑:
1. 计算时间差
2. 处理各种异常情况(负值/零值)
3. 正常计算衰减
4. 更新最后更新时间
"""
-
+
# 处理极小兴趣值情况
if self.interest_level < 1e-9:
self.interest_level = 0.0
return
-
+
# 异常情况处理
if self.decay_rate_per_second <= 0:
interest_logger.warning(f"衰减率({self.decay_rate_per_second})无效,重置兴趣值为0")
self.interest_level = 0.0
return
-
+
# 正常衰减计算
try:
decay_factor = math.pow(self.decay_rate_per_second, self.update_interval)
self.interest_level *= decay_factor
except ValueError as e:
- interest_logger.error(f"衰减计算错误: {e} 参数: 衰减率={self.decay_rate_per_second} 时间差={self.update_interval} 当前兴趣={self.interest_level}")
+ interest_logger.error(
+ f"衰减计算错误: {e} 参数: 衰减率={self.decay_rate_per_second} 时间差={self.update_interval} 当前兴趣={self.interest_level}"
+ )
self.interest_level = 0.0
-
async def _update_reply_probability(self):
self.above_threshold = self.interest_level >= self.trigger_threshold
@@ -171,7 +173,7 @@ class InterestChatting:
return self.interest_level
async def get_state(self) -> dict:
- interest = self.interest_level # 直接使用属性值
+ interest = self.interest_level # 直接使用属性值
return {
"interest_level": round(interest, 2),
"last_update_time": self.last_update_time,
@@ -181,7 +183,6 @@ class InterestChatting:
}
async def should_evaluate_reply(self) -> bool:
-
if self.current_reply_probability > 0:
trigger = random.random() < self.current_reply_probability
return trigger
@@ -195,9 +196,8 @@ class InterestChatting:
try:
if self.interest_level != 0:
await self._calculate_decay()
-
+
await self._update_reply_probability()
-
# 等待下一个周期或停止事件
await asyncio.wait_for(self._stop_event.wait(), timeout=update_interval)
@@ -214,7 +214,6 @@ class InterestChatting:
await asyncio.sleep(5)
interest_logger.info("InterestChatting 更新循环已停止。")
-
def start_updates(self, update_interval: float = 1.0):
"""启动后台更新任务"""
if self.update_task is None or self.update_task.done():
@@ -224,12 +223,11 @@ class InterestChatting:
else:
interest_logger.debug("后台兴趣更新任务已在运行中。")
-
async def stop_updates(self):
"""停止后台更新任务"""
if self.update_task and not self.update_task.done():
interest_logger.info("正在停止 InterestChatting 后台更新任务...")
- self._stop_event.set() # 发送停止信号
+ self._stop_event.set() # 发送停止信号
try:
# 等待任务结束,设置超时
await asyncio.wait_for(self.update_task, timeout=5.0)
@@ -238,7 +236,7 @@ class InterestChatting:
interest_logger.warning("停止 InterestChatting 后台任务超时,尝试取消...")
self.update_task.cancel()
try:
- await self.update_task # 等待取消完成
+ await self.update_task # 等待取消完成
except asyncio.CancelledError:
interest_logger.info("InterestChatting 后台更新任务已被取消。")
except Exception as e:
@@ -247,6 +245,7 @@ class InterestChatting:
self.update_task = None
else:
interest_logger.debug("InterestChatting 后台更新任务未运行或已完成。")
+
# --- 结束 新增方法 ---
@@ -297,11 +296,11 @@ class SubHeartflow:
async def add_time_current_state(self, add_time: float):
self.current_state_time += add_time
-
+
async def change_to_state_chat(self):
self.current_state_time = 120
self._start_normal_chat()
-
+
async def change_to_state_focused(self):
self.current_state_time = 60
self._start_heart_fc_chat()
@@ -311,14 +310,14 @@ class SubHeartflow:
if self.normal_chat_instance:
logger.info(f"{self.log_prefix} 停止 NormalChat 兴趣监控...")
try:
- await self.normal_chat_instance.stop_chat() # 调用 stop_chat
+ await self.normal_chat_instance.stop_chat() # 调用 stop_chat
except Exception as e:
logger.error(f"{self.log_prefix} 停止 NormalChat 监控任务时出错: {e}")
logger.error(traceback.format_exc())
async def _start_normal_chat(self) -> bool:
"""启动 NormalChat 实例及其兴趣监控,确保 HeartFChatting 已停止"""
- await self._stop_heart_fc_chat() # 确保专注聊天已停止
+ await self._stop_heart_fc_chat() # 确保专注聊天已停止
log_prefix = self.log_prefix
try:
@@ -334,12 +333,12 @@ class SubHeartflow:
logger.info(f"{log_prefix} 创建或更新 NormalChat 实例。")
logger.info(f"{log_prefix} 启动 NormalChat 兴趣监控...")
- await self.normal_chat_instance.start_chat() # <--- 修正:调用 start_chat
+ await self.normal_chat_instance.start_chat() # <--- 修正:调用 start_chat
return True
except Exception as e:
logger.error(f"{log_prefix} 启动 NormalChat 时出错: {e}")
logger.error(traceback.format_exc())
- self.normal_chat_instance = None # 启动失败,清理实例
+ self.normal_chat_instance = None # 启动失败,清理实例
return False
async def _stop_heart_fc_chat(self):
@@ -357,18 +356,18 @@ class SubHeartflow:
async def _start_heart_fc_chat(self) -> bool:
"""启动 HeartFChatting 实例,确保 NormalChat 已停止"""
- await self._stop_normal_chat() # 确保普通聊天监控已停止
- self.clear_interest_dict() # 清理兴趣字典,准备专注聊天
+ await self._stop_normal_chat() # 确保普通聊天监控已停止
+ self.clear_interest_dict() # 清理兴趣字典,准备专注聊天
log_prefix = self.log_prefix
if self.heart_fc_instance:
if not self.heart_fc_instance._loop_active:
logger.warning(f"{log_prefix} HeartFChatting 实例存在但未激活,尝试重新激活...")
- await self.heart_fc_instance.add_time() # 尝试添加时间以激活循环
- return True # 假设 add_time 会处理激活逻辑
+ await self.heart_fc_instance.add_time() # 尝试添加时间以激活循环
+ return True # 假设 add_time 会处理激活逻辑
else:
logger.debug(f"{log_prefix} HeartFChatting 已在运行中。")
- return True # 已经在运行
+ return True # 已经在运行
logger.info(f"{log_prefix} 麦麦准备开始专注聊天...")
try:
@@ -381,12 +380,12 @@ class SubHeartflow:
return True
else:
logger.error(f"{log_prefix} HeartFChatting 初始化失败,无法进入专注模式。")
- self.heart_fc_instance = None # 初始化失败,清理实例
+ self.heart_fc_instance = None # 初始化失败,清理实例
return False
except Exception as e:
logger.error(f"{log_prefix} 创建或初始化 HeartFChatting 实例时出错: {e}")
logger.error(traceback.format_exc())
- self.heart_fc_instance = None # 创建或初始化异常,清理实例
+ self.heart_fc_instance = None # 创建或初始化异常,清理实例
return False
async def set_chat_state(self, new_state: "ChatState", current_states_num: tuple = ()):
@@ -398,7 +397,7 @@ class SubHeartflow:
log_prefix = self.log_prefix
current_mai_state = self.mai_states.get_current_state()
- state_changed = False # 标记状态是否实际发生改变
+ state_changed = False # 标记状态是否实际发生改变
# --- 状态转换逻辑 ---
if new_state == ChatState.CHAT:
@@ -406,8 +405,10 @@ class SubHeartflow:
current_chat_count = current_states_num[1] if len(current_states_num) > 1 else 0
if current_chat_count >= normal_limit and current_state != ChatState.CHAT:
- logger.debug(f"{log_prefix} 无法从 {current_state.value} 转到 聊天。原因:聊不过来了 ({current_chat_count}/{normal_limit})")
- return # 阻止状态转换
+ logger.debug(
+ f"{log_prefix} 无法从 {current_state.value} 转到 聊天。原因:聊不过来了 ({current_chat_count}/{normal_limit})"
+ )
+ return # 阻止状态转换
else:
logger.debug(f"{log_prefix} 准备进入或保持 聊天 状态 ({current_chat_count}/{normal_limit})")
if await self._start_normal_chat():
@@ -416,15 +417,17 @@ class SubHeartflow:
else:
logger.error(f"{log_prefix} 启动 NormalChat 失败,无法进入 CHAT 状态。")
# 考虑是否需要回滚状态或采取其他措施
- return # 启动失败,不改变状态
+ return # 启动失败,不改变状态
elif new_state == ChatState.FOCUSED:
focused_limit = current_mai_state.get_focused_chat_max_num()
current_focused_count = current_states_num[2] if len(current_states_num) > 2 else 0
-
+
if current_focused_count >= focused_limit and current_state != ChatState.FOCUSED:
- logger.debug(f"{log_prefix} 无法从 {current_state.value} 转到 专注。原因:聊不过来了 ({current_focused_count}/{focused_limit})")
- return # 阻止状态转换
+ logger.debug(
+ f"{log_prefix} 无法从 {current_state.value} 转到 专注。原因:聊不过来了 ({current_focused_count}/{focused_limit})"
+ )
+ return # 阻止状态转换
else:
logger.debug(f"{log_prefix} 准备进入或保持 专注聊天 状态 ({current_focused_count}/{focused_limit})")
if await self._start_heart_fc_chat():
@@ -433,14 +436,13 @@ class SubHeartflow:
else:
logger.error(f"{log_prefix} 启动 HeartFChatting 失败,无法进入 FOCUSED 状态。")
# 启动失败,状态回滚到之前的状态或ABSENT?这里保持不改变
- return # 启动失败,不改变状态
-
+ return # 启动失败,不改变状态
elif new_state == ChatState.ABSENT:
logger.info(f"{log_prefix} 进入 ABSENT 状态,停止所有聊天活动...")
await self._stop_normal_chat()
await self._stop_heart_fc_chat()
- state_changed = True # 总是可以成功转换到 ABSENT
+ state_changed = True # 总是可以成功转换到 ABSENT
# --- 更新状态和最后活动时间 ---
if state_changed:
@@ -449,7 +451,9 @@ class SubHeartflow:
self.last_active_time = time.time()
else:
# 如果因为某些原因(如启动失败)没有成功改变状态,记录一下
- logger.debug(f"{log_prefix} 尝试将状态从 {current_state.value} 变为 {new_state.value},但未成功或未执行更改。")
+ logger.debug(
+ f"{log_prefix} 尝试将状态从 {current_state.value} 变为 {new_state.value},但未成功或未执行更改。"
+ )
async def subheartflow_start_working(self):
"""启动子心流的后台任务
@@ -462,7 +466,6 @@ class SubHeartflow:
while not self.should_stop:
await asyncio.sleep(30) # 30秒检查一次停止标志
-
logger.info(f"{self.log_prefix} 子心流后台任务已停止。")
diff --git a/src/heart_flow/subheartflow_manager.py b/src/heart_flow/subheartflow_manager.py
index e19172f4..344eaf77 100644
--- a/src/heart_flow/subheartflow_manager.py
+++ b/src/heart_flow/subheartflow_manager.py
@@ -18,7 +18,7 @@ from .observation import ChattingObservation
subheartflow_manager_log_config = LogConfig(
console_format=SUBHEARTFLOW_MANAGER_STYLE_CONFIG["console_format"],
- file_format=SUBHEARTFLOW_MANAGER_STYLE_CONFIG["file_format"],
+ file_format=SUBHEARTFLOW_MANAGER_STYLE_CONFIG["file_format"],
)
logger = get_module_logger("subheartflow_manager", config=subheartflow_manager_log_config)
@@ -271,7 +271,9 @@ class SubHeartflowManager:
current_state_enum = current_mai_state.get_current_state()
focused_limit = current_state_enum.get_focused_chat_max_num()
if focused_limit <= 0:
- logger.debug(f"{log_prefix_manager} 当前状态 ({current_state_enum.value}) 不允许 FOCUSED 子心流, 跳过提升检查。")
+ logger.debug(
+ f"{log_prefix_manager} 当前状态 ({current_state_enum.value}) 不允许 FOCUSED 子心流, 跳过提升检查。"
+ )
return
# 获取当前 FOCUSED 状态的数量 (初始值)
@@ -280,7 +282,7 @@ class SubHeartflowManager:
# 使用快照安全遍历
subflows_snapshot = list(self.subheartflows.values())
- promoted_count = 0 # 记录本次提升的数量
+ promoted_count = 0 # 记录本次提升的数量
try:
for sub_hf in subflows_snapshot:
flow_id = sub_hf.subheartflow_id
@@ -300,8 +302,10 @@ class SubHeartflowManager:
# 注意:在循环内部再次获取当前数量,因为之前的提升可能已经改变了计数
# 使用已经记录并在循环中更新的 current_focused_count
if current_focused_count >= focused_limit:
- logger.debug(f"{log_prefix_manager} {log_prefix_flow} 达到专注上限 ({current_focused_count}/{focused_limit}), 无法提升。概率={sub_hf.interest_chatting.start_hfc_probability:.2f}")
- continue # 跳过这个子心流,继续检查下一个
+ logger.debug(
+ f"{log_prefix_manager} {log_prefix_flow} 达到专注上限 ({current_focused_count}/{focused_limit}), 无法提升。概率={sub_hf.interest_chatting.start_hfc_probability:.2f}"
+ )
+ continue # 跳过这个子心流,继续检查下一个
# --- 执行提升 ---
# 获取当前实例以检查最新状态 (防御性编程)
@@ -310,16 +314,18 @@ class SubHeartflowManager:
logger.warning(f"{log_prefix_manager} {log_prefix_flow} 尝试提升时状态已改变或实例消失,跳过。")
continue
- logger.info(f"{log_prefix_manager} {log_prefix_flow} 兴趣评估触发升级 (prob={sub_hf.interest_chatting.start_hfc_probability:.2f}, 上限:{focused_limit}, 当前:{current_focused_count}) -> FOCUSED")
+ logger.info(
+ f"{log_prefix_manager} {log_prefix_flow} 兴趣评估触发升级 (prob={sub_hf.interest_chatting.start_hfc_probability:.2f}, 上限:{focused_limit}, 当前:{current_focused_count}) -> FOCUSED"
+ )
states_num = (
self.count_subflows_by_state(ChatState.ABSENT),
- self.count_subflows_by_state(ChatState.CHAT), # 这个值在提升前计算
- current_focused_count, # 这个值在提升前计算
+ self.count_subflows_by_state(ChatState.CHAT), # 这个值在提升前计算
+ current_focused_count, # 这个值在提升前计算
)
# --- 状态设置 ---
- original_state = current_subflow.chat_state.chat_status # 记录原始状态
+ original_state = current_subflow.chat_state.chat_status # 记录原始状态
await current_subflow.set_chat_state(ChatState.FOCUSED, states_num)
# --- 状态验证 ---
@@ -327,15 +333,21 @@ class SubHeartflowManager:
if final_subflow:
final_state = final_subflow.chat_state.chat_status
if final_state == ChatState.FOCUSED:
- logger.debug(f"{log_prefix_manager} {log_prefix_flow} 成功从 {original_state.value} 升级到 FOCUSED 状态")
+ logger.debug(
+ f"{log_prefix_manager} {log_prefix_flow} 成功从 {original_state.value} 升级到 FOCUSED 状态"
+ )
promoted_count += 1
# 提升成功后,更新当前专注计数,以便后续检查能使用最新值
current_focused_count += 1
- elif final_state == original_state: # 状态未变
- logger.warning(f"{log_prefix_manager} {log_prefix_flow} 尝试从 {original_state.value} 升级 FOCUSED 失败,状态仍为: {final_state.value} (可能被内部逻辑阻止)")
- else: # 状态变成其他了?
- logger.warning(f"{log_prefix_manager} {log_prefix_flow} 尝试从 {original_state.value} 升级 FOCUSED 后状态变为 {final_state.value}")
- else: # 子心流消失了?
+ elif final_state == original_state: # 状态未变
+ logger.warning(
+ f"{log_prefix_manager} {log_prefix_flow} 尝试从 {original_state.value} 升级 FOCUSED 失败,状态仍为: {final_state.value} (可能被内部逻辑阻止)"
+ )
+ else: # 状态变成其他了?
+ logger.warning(
+ f"{log_prefix_manager} {log_prefix_flow} 尝试从 {original_state.value} 升级 FOCUSED 后状态变为 {final_state.value}"
+ )
+ else: # 子心流消失了?
logger.warning(f"{log_prefix_manager} {log_prefix_flow} 升级后验证时子心流 {flow_id} 消失")
except Exception as e:
diff --git a/src/plugins/heartFC_chat/heartFC_chat.py b/src/plugins/heartFC_chat/heartFC_chat.py
index ffed743a..b3478866 100644
--- a/src/plugins/heartFC_chat/heartFC_chat.py
+++ b/src/plugins/heartFC_chat/heartFC_chat.py
@@ -73,10 +73,7 @@ class HeartFChatting:
现在由其关联的 SubHeartflow 管理生命周期。
"""
- def __init__(
- self,
- chat_id: str
- ):
+ def __init__(self, chat_id: str):
"""
HeartFChatting 初始化函数
@@ -195,7 +192,7 @@ class HeartFChatting:
# 检查是否满足启动条件:未激活且计时器有时间
if not self._loop_active and self._loop_timer > 0:
should_start_loop = True
- self._loop_active = True # 在锁内标记为活动,防止重复启动
+ self._loop_active = True # 在锁内标记为活动,防止重复启动
if should_start_loop:
# 检查是否已有任务在运行(理论上不应该,因为 _loop_active=False)
@@ -206,8 +203,8 @@ class HeartFChatting:
# 等待旧任务确实被取消
await asyncio.wait_for(self._loop_task, timeout=0.5)
except (asyncio.CancelledError, asyncio.TimeoutError):
- pass # 忽略取消或超时错误
- self._loop_task = None # 清理旧任务引用
+ pass # 忽略取消或超时错误
+ self._loop_task = None # 清理旧任务引用
logger.info(f"{log_prefix} 计时器 > 0 且循环未激活,启动主循环...")
# 创建新的循环任务
@@ -215,7 +212,7 @@ class HeartFChatting:
# 添加完成回调
self._loop_task.add_done_callback(self._handle_loop_completion)
# else:
- # logger.trace(f"{log_prefix} 不需要启动循环(已激活或计时器为0)") # 可以取消注释以进行调试
+ # logger.trace(f"{log_prefix} 不需要启动循环(已激活或计时器为0)") # 可以取消注释以进行调试
def _handle_loop_completion(self, task: asyncio.Task):
"""当 _run_pf_loop 任务完成时执行的回调。"""
diff --git a/src/plugins/heartFC_chat/heartflow_processor.py b/src/plugins/heartFC_chat/heartflow_processor.py
index 30ac61c5..366bb125 100644
--- a/src/plugins/heartFC_chat/heartflow_processor.py
+++ b/src/plugins/heartFC_chat/heartflow_processor.py
@@ -69,7 +69,6 @@ class HeartFCProcessor:
subheartflow = await heartflow.create_subheartflow(chat.stream_id)
-
message.update_chat_stream(chat)
await heartflow.create_subheartflow(chat.stream_id)
diff --git a/src/plugins/heartFC_chat/normal_chat.py b/src/plugins/heartFC_chat/normal_chat.py
index 4acacd6d..c020b407 100644
--- a/src/plugins/heartFC_chat/normal_chat.py
+++ b/src/plugins/heartFC_chat/normal_chat.py
@@ -349,7 +349,7 @@ class NormalChat:
return False
# 改为实例方法, 移除 chat 参数
-
+
async def start_chat(self):
"""为此 NormalChat 实例关联的 ChatStream 启动聊天任务(如果尚未运行)。"""
if self._chat_task is None or self._chat_task.done():
@@ -358,7 +358,6 @@ class NormalChat:
task.add_done_callback(lambda t: self._handle_task_completion(t)) # 回调现在是实例方法
self._chat_task = task
-
# 改为实例方法, 移除 stream_id 参数
def _handle_task_completion(self, task: asyncio.Task):
"""兴趣监控任务完成时的回调函数。"""
@@ -403,4 +402,3 @@ class NormalChat:
# 确保任务状态更新,即使等待出错 (回调函数也会尝试更新)
if self._chat_task is task:
self._chat_task = None
-
diff --git a/src/plugins/message/api.py b/src/plugins/message/api.py
index 31302f51..fb51539e 100644
--- a/src/plugins/message/api.py
+++ b/src/plugins/message/api.py
@@ -148,7 +148,9 @@ class MessageServer(BaseMessageHandler):
if self.own_app:
# 如果使用自己的 FastAPI 实例,运行 uvicorn 服务器
# 禁用 uvicorn 默认日志和访问日志
- config = uvicorn.Config(self.app, host=self.host, port=self.port, loop="asyncio", log_config=None, access_log=False)
+ config = uvicorn.Config(
+ self.app, host=self.host, port=self.port, loop="asyncio", log_config=None, access_log=False
+ )
self.server = uvicorn.Server(config)
await self.server.serve()
else:
From 2bf72d9e5847652a8007af243b1f6fba31cdd880 Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Wed, 23 Apr 2025 23:37:51 +0800
Subject: [PATCH 22/28] Update sub_heartflow.py
---
src/heart_flow/sub_heartflow.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py
index 0b17c278..d590fb19 100644
--- a/src/heart_flow/sub_heartflow.py
+++ b/src/heart_flow/sub_heartflow.py
@@ -535,7 +535,7 @@ class SubHeartflow:
prompt = (await global_prompt_manager.get_prompt_async("sub_heartflow_prompt_before")).format(
extra_info=extra_info_prompt,
prompt_personality=prompt_personality,
- bot_name=individuality.bot_nickname,
+ bot_name=individuality.personality.bot_nickname,
current_thinking_info=current_thinking_info,
time_now=time_now,
chat_observe_info=chat_observe_info,
From 58305c3dd144f64fd4719a06e245156a7b552ed3 Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Wed, 23 Apr 2025 23:55:35 +0800
Subject: [PATCH 23/28] =?UTF-8?q?fix=EF=BC=9A=E6=B7=BB=E5=8A=A0hfc?=
=?UTF-8?q?=E8=B0=83=E8=AF=95=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=B8=80=E9=94=AE?=
=?UTF-8?q?999?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
interest_monitor_gui.py | 17 ++++++++++-------
src/heart_flow/interest_logger.py | 4 +---
src/heart_flow/mai_state_manager.py | 20 +++++++++++++++++---
src/heart_flow/sub_heartflow.py | 4 +---
4 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/interest_monitor_gui.py b/interest_monitor_gui.py
index 97536f70..0dd2eeed 100644
--- a/interest_monitor_gui.py
+++ b/interest_monitor_gui.py
@@ -214,7 +214,8 @@ class InterestMonitorApp:
interest_level = subflow_entry.get("interest_level")
# 获取 group_name,如果不存在则回退到 stream_id
group_name = subflow_entry.get("group_name", stream_id)
- reply_probability = subflow_entry.get("reply_probability") # 获取概率值
+ # reply_probability = subflow_entry.get("reply_probability") # 获取概率值 # <-- 注释掉旧行
+ start_hfc_probability = subflow_entry.get("start_hfc_probability") # <-- 添加新行,读取新字段
# *** 检查必要的字段 ***
# 注意:时间戳已在顶层检查过
@@ -252,10 +253,12 @@ class InterestMonitorApp:
new_stream_history[stream_id].append((entry_timestamp, interest_level_float))
# 添加概率数据点 (如果存在且有效)
- if reply_probability is not None:
+ # if reply_probability is not None: # <-- 注释掉旧判断
+ if start_hfc_probability is not None: # <-- 修改判断条件
try:
# 尝试将概率转换为浮点数
- probability_float = float(reply_probability)
+ # probability_float = float(reply_probability) # <-- 注释掉旧转换
+ probability_float = float(start_hfc_probability) # <-- 使用新变量
new_probability_history[stream_id].append((entry_timestamp, probability_float))
except (TypeError, ValueError):
# 如果概率值无效,可以跳过或记录一个默认值,这里跳过
@@ -410,13 +413,13 @@ class InterestMonitorApp:
# 设置子图标题和标签
self.ax_single_interest.set_title("兴趣度")
- self.ax_single_interest.set_ylabel("兴趣度")
- self.ax_single_interest.grid(True)
self.ax_single_interest.set_ylim(0, 10) # 固定 Y 轴范围 0-10
- self.ax_single_probability.set_title("回复评估概率")
+ # self.ax_single_probability.set_title("回复评估概率") # <-- 注释掉旧标题
+ self.ax_single_probability.set_title("HFC 启动概率") # <-- 修改标题
self.ax_single_probability.set_xlabel("时间")
- self.ax_single_probability.set_ylabel("概率")
+ # self.ax_single_probability.set_ylabel("概率") # <-- 注释掉旧标签
+ self.ax_single_probability.set_ylabel("HFC 概率") # <-- 修改 Y 轴标签
self.ax_single_probability.grid(True)
self.ax_single_probability.set_ylim(0, 1.05) # 固定 Y 轴范围 0-1
self.ax_single_probability.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
diff --git a/src/heart_flow/interest_logger.py b/src/heart_flow/interest_logger.py
index 866ae4e0..05a7da39 100644
--- a/src/heart_flow/interest_logger.py
+++ b/src/heart_flow/interest_logger.py
@@ -136,10 +136,8 @@ class InterestLogger:
"sub_mind": state.get("current_mind", "未知"),
"sub_chat_state": state.get("chat_state", "未知"),
"interest_level": interest_state.get("interest_level", 0.0),
- "reply_probability": interest_state.get("current_reply_probability", 0.0),
+ "start_hfc_probability": interest_state.get("start_hfc_probability", 0.0),
"is_above_threshold": interest_state.get("is_above_threshold", False),
- "last_active_time": state.get("last_active_time", 0.0),
- "last_interaction_time": interest_state.get("last_interaction_time", 0.0),
}
subflow_details.append(subflow_entry)
diff --git a/src/heart_flow/mai_state_manager.py b/src/heart_flow/mai_state_manager.py
index 5048ff08..1d309512 100644
--- a/src/heart_flow/mai_state_manager.py
+++ b/src/heart_flow/mai_state_manager.py
@@ -13,6 +13,11 @@ mai_state_config = LogConfig(
logger = get_module_logger("mai_state_manager", config=mai_state_config)
+
+enable_unlimited_hfc_chat = False
+
+
+
class MaiState(enum.Enum):
"""
聊天状态:
@@ -28,6 +33,11 @@ class MaiState(enum.Enum):
FOCUSED_CHAT = "专心聊天"
def get_normal_chat_max_num(self):
+ # 调试用
+ if enable_unlimited_hfc_chat:
+ return 1000
+
+
if self == MaiState.OFFLINE:
return 0
elif self == MaiState.PEEKING:
@@ -38,6 +48,10 @@ class MaiState(enum.Enum):
return 2
def get_focused_chat_max_num(self):
+ # 调试用
+ if enable_unlimited_hfc_chat:
+ return 1000
+
if self == MaiState.OFFLINE:
return 0
elif self == MaiState.PEEKING:
@@ -125,11 +139,11 @@ class MaiStateManager:
if current_status == MaiState.OFFLINE:
logger.info("当前[离线],没看手机,思考要不要上线看看......")
elif current_status == MaiState.PEEKING:
- logger.info("当前[在窥屏],思考要不要继续聊下去......")
+ logger.info("当前[看一眼],思考要不要继续聊下去......")
elif current_status == MaiState.NORMAL_CHAT:
- logger.info("当前在[随便看]思考要不要继续聊下去......")
+ logger.info("当前在[正常聊天]思考要不要继续聊下去......")
elif current_status == MaiState.FOCUSED_CHAT:
- logger.info("当前在[专心看]思考要不要继续聊下去......")
+ logger.info("当前在[专心聊天]思考要不要继续聊下去......")
# 1. 麦麦每分钟都有概率离线
if time_since_last_min_check >= 60:
diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py
index d590fb19..68400e83 100644
--- a/src/heart_flow/sub_heartflow.py
+++ b/src/heart_flow/sub_heartflow.py
@@ -176,10 +176,8 @@ class InterestChatting:
interest = self.interest_level # 直接使用属性值
return {
"interest_level": round(interest, 2),
- "last_update_time": self.last_update_time,
- "current_reply_probability": round(self.current_reply_probability, 4),
+ "start_hfc_probability": round(self.start_hfc_probability, 4),
"is_above_threshold": self.is_above_threshold,
- "last_interaction_time": self.last_interaction_time,
}
async def should_evaluate_reply(self) -> bool:
From afd15b28bc5699ba4e07d7d86f4c953f8f5871bc Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 15:55:48 +0000
Subject: [PATCH 24/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
interest_monitor_gui.py | 12 +++++++-----
src/heart_flow/mai_state_manager.py | 5 +----
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/interest_monitor_gui.py b/interest_monitor_gui.py
index 0dd2eeed..28c5ecc1 100644
--- a/interest_monitor_gui.py
+++ b/interest_monitor_gui.py
@@ -215,7 +215,9 @@ class InterestMonitorApp:
# 获取 group_name,如果不存在则回退到 stream_id
group_name = subflow_entry.get("group_name", stream_id)
# reply_probability = subflow_entry.get("reply_probability") # 获取概率值 # <-- 注释掉旧行
- start_hfc_probability = subflow_entry.get("start_hfc_probability") # <-- 添加新行,读取新字段
+ start_hfc_probability = subflow_entry.get(
+ "start_hfc_probability"
+ ) # <-- 添加新行,读取新字段
# *** 检查必要的字段 ***
# 注意:时间戳已在顶层检查过
@@ -254,11 +256,11 @@ class InterestMonitorApp:
# 添加概率数据点 (如果存在且有效)
# if reply_probability is not None: # <-- 注释掉旧判断
- if start_hfc_probability is not None: # <-- 修改判断条件
+ if start_hfc_probability is not None: # <-- 修改判断条件
try:
# 尝试将概率转换为浮点数
# probability_float = float(reply_probability) # <-- 注释掉旧转换
- probability_float = float(start_hfc_probability) # <-- 使用新变量
+ probability_float = float(start_hfc_probability) # <-- 使用新变量
new_probability_history[stream_id].append((entry_timestamp, probability_float))
except (TypeError, ValueError):
# 如果概率值无效,可以跳过或记录一个默认值,这里跳过
@@ -416,10 +418,10 @@ class InterestMonitorApp:
self.ax_single_interest.set_ylim(0, 10) # 固定 Y 轴范围 0-10
# self.ax_single_probability.set_title("回复评估概率") # <-- 注释掉旧标题
- self.ax_single_probability.set_title("HFC 启动概率") # <-- 修改标题
+ self.ax_single_probability.set_title("HFC 启动概率") # <-- 修改标题
self.ax_single_probability.set_xlabel("时间")
# self.ax_single_probability.set_ylabel("概率") # <-- 注释掉旧标签
- self.ax_single_probability.set_ylabel("HFC 概率") # <-- 修改 Y 轴标签
+ self.ax_single_probability.set_ylabel("HFC 概率") # <-- 修改 Y 轴标签
self.ax_single_probability.grid(True)
self.ax_single_probability.set_ylim(0, 1.05) # 固定 Y 轴范围 0-1
self.ax_single_probability.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
diff --git a/src/heart_flow/mai_state_manager.py b/src/heart_flow/mai_state_manager.py
index 1d309512..174f31be 100644
--- a/src/heart_flow/mai_state_manager.py
+++ b/src/heart_flow/mai_state_manager.py
@@ -13,11 +13,9 @@ mai_state_config = LogConfig(
logger = get_module_logger("mai_state_manager", config=mai_state_config)
-
enable_unlimited_hfc_chat = False
-
class MaiState(enum.Enum):
"""
聊天状态:
@@ -36,8 +34,7 @@ class MaiState(enum.Enum):
# 调试用
if enable_unlimited_hfc_chat:
return 1000
-
-
+
if self == MaiState.OFFLINE:
return 0
elif self == MaiState.PEEKING:
From 8962632c0b97a9f4c5819dbf800f3203246f0c03 Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Thu, 24 Apr 2025 00:11:50 +0800
Subject: [PATCH 25/28] =?UTF-8?q?fix=EF=BC=9Ahfc=E7=8E=B0=E5=9C=A8?=
=?UTF-8?q?=E4=BC=9A=E5=9C=A8FOCUS=E4=B8=8B=E6=8C=81=E7=BB=AD=E8=BF=90?=
=?UTF-8?q?=E8=A1=8C=EF=BC=8C=E6=9C=89=E5=B0=8F=E6=A6=82=E7=8E=87=E6=8E=89?=
=?UTF-8?q?=E5=87=BAFOCUS=E6=A8=A1=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/config/config.py | 2 +-
src/heart_flow/background_tasks.py | 34 ++++++-
src/heart_flow/sub_heartflow.py | 28 ++++--
src/heart_flow/subheartflow_manager.py | 68 ++++++++++++++
src/plugins/heartFC_chat/heartFC_chat.py | 114 ++++++++---------------
5 files changed, 159 insertions(+), 87 deletions(-)
diff --git a/src/config/config.py b/src/config/config.py
index bf184a00..ba9416d5 100644
--- a/src/config/config.py
+++ b/src/config/config.py
@@ -28,7 +28,7 @@ logger = get_module_logger("config", config=config_config)
# 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
is_test = True
mai_version_main = "0.6.3"
-mai_version_fix = "snapshot-3"
+mai_version_fix = "snapshot-4"
if mai_version_fix:
if is_test:
diff --git a/src/heart_flow/background_tasks.py b/src/heart_flow/background_tasks.py
index 7fb63bf6..2bfd7420 100644
--- a/src/heart_flow/background_tasks.py
+++ b/src/heart_flow/background_tasks.py
@@ -16,6 +16,8 @@ background_tasks_log_config = LogConfig(
logger = get_module_logger("background_tasks", config=background_tasks_log_config)
+# 新增随机停用间隔 (5 分钟)
+RANDOM_DEACTIVATION_INTERVAL_SECONDS = 300
# 新增兴趣评估间隔
INTEREST_EVAL_INTERVAL_SECONDS = 5
@@ -35,6 +37,8 @@ class BackgroundTaskManager:
inactive_threshold: int,
# 新增兴趣评估间隔参数
interest_eval_interval: int = INTEREST_EVAL_INTERVAL_SECONDS,
+ # 新增随机停用间隔参数
+ random_deactivation_interval: int = RANDOM_DEACTIVATION_INTERVAL_SECONDS,
):
self.mai_state_info = mai_state_info
self.mai_state_manager = mai_state_manager
@@ -47,19 +51,21 @@ class BackgroundTaskManager:
self.log_interval = log_interval
self.inactive_threshold = inactive_threshold # For cleanup task
self.interest_eval_interval = interest_eval_interval # 存储兴趣评估间隔
+ self.random_deactivation_interval = random_deactivation_interval # 存储随机停用间隔
# Task references
self._state_update_task: Optional[asyncio.Task] = None
self._cleanup_task: Optional[asyncio.Task] = None
self._logging_task: Optional[asyncio.Task] = None
self._interest_eval_task: Optional[asyncio.Task] = None # 新增兴趣评估任务引用
+ self._random_deactivation_task: Optional[asyncio.Task] = None # 新增随机停用任务引用
self._tasks: List[Optional[asyncio.Task]] = [] # Keep track of all tasks
async def start_tasks(self):
"""启动所有后台任务
功能说明:
- - 启动核心后台任务: 状态更新、清理、日志记录和兴趣评估
+ - 启动核心后台任务: 状态更新、清理、日志记录、兴趣评估和随机停用
- 每个任务启动前检查是否已在运行
- 将任务引用保存到任务列表
"""
@@ -99,6 +105,15 @@ class BackgroundTaskManager:
f"兴趣评估任务已启动 间隔:{self.interest_eval_interval}s",
"_interest_eval_task",
),
+ # 新增随机停用任务配置
+ (
+ self._random_deactivation_task,
+ self._run_random_deactivation_cycle,
+ "hf_random_deactivation",
+ "debug", # 设为debug,避免过多日志
+ f"随机停用任务已启动 间隔:{self.random_deactivation_interval}s",
+ "_random_deactivation_task",
+ ),
]
# 统一启动所有任务
@@ -230,6 +245,12 @@ class BackgroundTaskManager:
# --- 结束新增 ---
+ # --- 新增随机停用工作函数 ---
+ async def _perform_random_deactivation_work(self):
+ """执行一轮子心流随机停用检查。"""
+ await self.subheartflow_manager.randomly_deactivate_subflows()
+ # --- 结束新增 ---
+
# --- Specific Task Runners --- #
async def _run_state_update_cycle(self, interval: int):
await self._run_periodic_loop(
@@ -255,3 +276,14 @@ class BackgroundTaskManager:
)
# --- 结束新增 ---
+
+ # --- 新增随机停用任务运行器 ---
+ async def _run_random_deactivation_cycle(self):
+ """运行随机停用循环。"""
+ await self._run_periodic_loop(
+ task_name="Random Deactivation",
+ interval=self.random_deactivation_interval,
+ task_func=self._perform_random_deactivation_work,
+ )
+
+ # --- 结束新增 ---
diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py
index 68400e83..85ceaf76 100644
--- a/src/heart_flow/sub_heartflow.py
+++ b/src/heart_flow/sub_heartflow.py
@@ -358,30 +358,40 @@ class SubHeartflow:
self.clear_interest_dict() # 清理兴趣字典,准备专注聊天
log_prefix = self.log_prefix
+ # 如果实例已存在,检查其循环任务状态
if self.heart_fc_instance:
- if not self.heart_fc_instance._loop_active:
- logger.warning(f"{log_prefix} HeartFChatting 实例存在但未激活,尝试重新激活...")
- await self.heart_fc_instance.add_time() # 尝试添加时间以激活循环
- return True # 假设 add_time 会处理激活逻辑
+ # 如果任务已完成或不存在,则尝试重新启动
+ if self.heart_fc_instance._loop_task is None or self.heart_fc_instance._loop_task.done():
+ logger.info(f"{log_prefix} HeartFChatting 实例存在但循环未运行,尝试启动...")
+ try:
+ await self.heart_fc_instance.start() # 启动循环
+ logger.info(f"{log_prefix} HeartFChatting 循环已启动。")
+ return True
+ except Exception as e:
+ logger.error(f"{log_prefix} 尝试启动现有 HeartFChatting 循环时出错: {e}")
+ logger.error(traceback.format_exc())
+ return False # 启动失败
else:
+ # 任务正在运行
logger.debug(f"{log_prefix} HeartFChatting 已在运行中。")
- return True # 已经在运行
+ return True # 已经在运行
- logger.info(f"{log_prefix} 麦麦准备开始专注聊天...")
+ # 如果实例不存在,则创建并启动
+ logger.info(f"{log_prefix} 麦麦准备开始专注聊天 (创建新实例)...")
try:
self.heart_fc_instance = HeartFChatting(
chat_id=self.chat_id,
)
if await self.heart_fc_instance._initialize():
- await self.heart_fc_instance.add_time() # 初始化成功后添加初始时间
- logger.info(f"{log_prefix} 麦麦已成功进入专注聊天模式。")
+ await self.heart_fc_instance.start() # 初始化成功后启动循环
+ logger.info(f"{log_prefix} 麦麦已成功进入专注聊天模式 (新实例已启动)。")
return True
else:
logger.error(f"{log_prefix} HeartFChatting 初始化失败,无法进入专注模式。")
self.heart_fc_instance = None # 初始化失败,清理实例
return False
except Exception as e:
- logger.error(f"{log_prefix} 创建或初始化 HeartFChatting 实例时出错: {e}")
+ logger.error(f"{log_prefix} 创建或启动 HeartFChatting 实例时出错: {e}")
logger.error(traceback.format_exc())
self.heart_fc_instance = None # 创建或初始化异常,清理实例
return False
diff --git a/src/heart_flow/subheartflow_manager.py b/src/heart_flow/subheartflow_manager.py
index 344eaf77..d5f7ed86 100644
--- a/src/heart_flow/subheartflow_manager.py
+++ b/src/heart_flow/subheartflow_manager.py
@@ -358,6 +358,74 @@ class SubHeartflowManager:
else:
logger.debug(f"{log_prefix_manager} 评估周期结束, 未提升任何子心流。")
+ async def randomly_deactivate_subflows(self, deactivation_probability: float = 0.3):
+ """以一定概率将 FOCUSED 或 CHAT 状态的子心流回退到 ABSENT 状态。"""
+ log_prefix_manager = "[子心流管理器-随机停用]"
+ logger.debug(f"{log_prefix_manager} 开始随机停用检查... (概率: {deactivation_probability:.0%})")
+
+ # 使用快照安全遍历
+ subflows_snapshot = list(self.subheartflows.values())
+ deactivated_count = 0
+
+ # 预先计算状态数量,因为 set_chat_state 需要
+ states_num_before = (
+ self.count_subflows_by_state(ChatState.ABSENT),
+ self.count_subflows_by_state(ChatState.CHAT),
+ self.count_subflows_by_state(ChatState.FOCUSED),
+ )
+
+ try:
+ for sub_hf in subflows_snapshot:
+ flow_id = sub_hf.subheartflow_id
+ stream_name = chat_manager.get_stream_name(flow_id) or flow_id
+ log_prefix_flow = f"[{stream_name}]"
+ current_state = sub_hf.chat_state.chat_status
+
+ # 只处理 FOCUSED 或 CHAT 状态
+ if current_state not in [ChatState.FOCUSED, ChatState.CHAT]:
+ continue
+
+ # 检查随机概率
+ if random.random() < deactivation_probability:
+ logger.info(
+ f"{log_prefix_manager} {log_prefix_flow} 随机触发停用 (从 {current_state.value}) -> ABSENT"
+ )
+
+ # 获取当前实例以检查最新状态
+ current_subflow = self.subheartflows.get(flow_id)
+ if not current_subflow or current_subflow.chat_state.chat_status != current_state:
+ logger.warning(f"{log_prefix_manager} {log_prefix_flow} 尝试停用时状态已改变或实例消失,跳过。")
+ continue
+
+ # --- 状态设置 --- #
+ # 注意:这里传递的状态数量是 *停用前* 的状态数量
+ await current_subflow.set_chat_state(ChatState.ABSENT, states_num_before)
+
+ # --- 状态验证 (可选) ---
+ final_subflow = self.subheartflows.get(flow_id)
+ if final_subflow:
+ final_state = final_subflow.chat_state.chat_status
+ if final_state == ChatState.ABSENT:
+ logger.debug(
+ f"{log_prefix_manager} {log_prefix_flow} 成功从 {current_state.value} 停用到 ABSENT 状态"
+ )
+ deactivated_count += 1
+ # 注意:停用后不需要更新 states_num_before,因为它只用于 set_chat_state 的限制检查
+ else:
+ logger.warning(
+ f"{log_prefix_manager} {log_prefix_flow} 尝试停用到 ABSENT 后状态仍为 {final_state.value}"
+ )
+ else:
+ logger.warning(f"{log_prefix_manager} {log_prefix_flow} 停用后验证时子心流 {flow_id} 消失")
+
+ except Exception as e:
+ logger.error(f"{log_prefix_manager} 随机停用周期出错: {e}", exc_info=True)
+
+ if deactivated_count > 0:
+ logger.info(f"{log_prefix_manager} 随机停用周期结束, 成功停用 {deactivated_count} 个子心流。")
+ else:
+ logger.debug(f"{log_prefix_manager} 随机停用周期结束, 未停用任何子心流。")
+
def count_subflows_by_state(self, state: ChatState) -> int:
"""统计指定状态的子心流数量
diff --git a/src/plugins/heartFC_chat/heartFC_chat.py b/src/plugins/heartFC_chat/heartFC_chat.py
index b3478866..371c817e 100644
--- a/src/plugins/heartFC_chat/heartFC_chat.py
+++ b/src/plugins/heartFC_chat/heartFC_chat.py
@@ -68,9 +68,8 @@ PLANNER_TOOL_DEFINITION = [
class HeartFChatting:
"""
管理一个连续的Plan-Replier-Sender循环
- 用于在特定聊天流中生成回复,由计时器控制。
- 只要计时器>0,循环就会继续。
- 现在由其关联的 SubHeartflow 管理生命周期。
+ 用于在特定聊天流中生成回复。
+ 其生命周期现在由其关联的 SubHeartflow 的 FOCUSED 状态控制。
"""
def __init__(self, chat_id: str):
@@ -79,9 +78,6 @@ class HeartFChatting:
参数:
chat_id: 聊天流唯一标识符(如stream_id)
- gpt_instance: 文本回复生成器实例
- tool_user_instance: 工具使用实例
- emoji_manager_instance: 表情管理实例
"""
# 基础属性
self.stream_id: str = chat_id # 聊天流ID
@@ -91,7 +87,6 @@ class HeartFChatting:
# 初始化状态控制
self._initialized = False # 是否已初始化标志
self._processing_lock = asyncio.Lock() # 处理锁(确保单次Plan-Replier-Sender周期)
- self._timer_lock = asyncio.Lock() # 计时器锁(安全更新计时器)
# 依赖注入存储
self.gpt_instance = HeartFCGenerator() # 文本回复生成器
@@ -106,11 +101,8 @@ class HeartFChatting:
)
# 循环控制内部状态
- self._loop_timer: float = 0.0 # 循环剩余时间(秒)
self._loop_active: bool = False # 循环是否正在运行
self._loop_task: Optional[asyncio.Task] = None # 主循环任务
- self._initial_duration: float = INITIAL_DURATION # 首次触发增加的时间
- self._last_added_duration: float = self._initial_duration # 上次增加的时间
def _get_log_prefix(self) -> str:
"""获取日志前缀,包含可读的流名称"""
@@ -147,34 +139,10 @@ class HeartFChatting:
logger.error(traceback.format_exc())
return False
- async def add_time(self):
- """
- 为麦麦添加时间,麦麦有兴趣时,固定增加15秒
- """
- log_prefix = self._get_log_prefix()
- if not self._initialized:
- if not await self._initialize():
- logger.error(f"{log_prefix} 无法添加时间: 未初始化。")
- return
-
- async with self._timer_lock:
- duration_to_add: float = 15.0 # 固定增加15秒
- if not self._loop_active: # 首次触发
- logger.info(f"{log_prefix} 麦麦有兴趣! 打算聊:15s.")
- else: # 循环已激活
- logger.info(f"{log_prefix} 麦麦想继续聊:15s, 还能聊: {self._loop_timer:.1f}s.")
-
- # 添加固定时间
- new_timer_value = self._loop_timer + duration_to_add
- self._loop_timer = max(0, new_timer_value)
-
- # 添加时间后,检查是否需要启动循环
- await self._start_loop_if_needed()
-
async def start(self):
"""
显式尝试启动 HeartFChatting 的主循环。
- 如果循环未激活且计时器 > 0,则启动循环。
+ 如果循环未激活,则启动循环。
"""
log_prefix = self._get_log_prefix()
if not self._initialized:
@@ -185,14 +153,13 @@ class HeartFChatting:
await self._start_loop_if_needed()
async def _start_loop_if_needed(self):
- """检查是否需要启动主循环,如果未激活且计时器大于0,则启动。"""
+ """检查是否需要启动主循环,如果未激活则启动。"""
log_prefix = self._get_log_prefix()
should_start_loop = False
- async with self._timer_lock:
- # 检查是否满足启动条件:未激活且计时器有时间
- if not self._loop_active and self._loop_timer > 0:
- should_start_loop = True
- self._loop_active = True # 在锁内标记为活动,防止重复启动
+ # 直接检查是否激活,无需检查计时器
+ if not self._loop_active:
+ should_start_loop = True
+ self._loop_active = True # 标记为活动,防止重复启动
if should_start_loop:
# 检查是否已有任务在运行(理论上不应该,因为 _loop_active=False)
@@ -206,13 +173,13 @@ class HeartFChatting:
pass # 忽略取消或超时错误
self._loop_task = None # 清理旧任务引用
- logger.info(f"{log_prefix} 计时器 > 0 且循环未激活,启动主循环...")
+ logger.info(f"{log_prefix} 循环未激活,启动主循环...")
# 创建新的循环任务
self._loop_task = asyncio.create_task(self._run_pf_loop())
# 添加完成回调
self._loop_task.add_done_callback(self._handle_loop_completion)
# else:
- # logger.trace(f"{log_prefix} 不需要启动循环(已激活或计时器为0)") # 可以取消注释以进行调试
+ # logger.trace(f"{log_prefix} 不需要启动循环(已激活)") # 可以取消注释以进行调试
def _handle_loop_completion(self, task: asyncio.Task):
"""当 _run_pf_loop 任务完成时执行的回调。"""
@@ -223,47 +190,38 @@ class HeartFChatting:
logger.error(f"{log_prefix} HeartFChatting: 麦麦脱离了聊天(异常): {exception}")
logger.error(traceback.format_exc()) # Log full traceback for exceptions
else:
- logger.debug(f"{log_prefix} HeartFChatting: 麦麦脱离了聊天 (正常完成)")
+ # Loop completing normally now means it was cancelled/shutdown externally
+ logger.info(f"{log_prefix} HeartFChatting: 麦麦脱离了聊天 (外部停止)")
except asyncio.CancelledError:
logger.info(f"{log_prefix} HeartFChatting: 麦麦脱离了聊天(任务取消)")
finally:
self._loop_active = False
self._loop_task = None
- self._last_added_duration = self._initial_duration
- self._trigger_count_this_activation = 0
if self._processing_lock.locked():
logger.warning(f"{log_prefix} HeartFChatting: 处理锁在循环结束时仍被锁定,强制释放。")
self._processing_lock.release()
async def _run_pf_loop(self):
"""
- 主循环,当计时器>0时持续进行计划并可能回复消息
- 管理每个循环周期的处理锁
+ 主循环,持续进行计划并可能回复消息,直到被外部取消。
+ 管理每个循环周期的处理锁。
"""
log_prefix = self._get_log_prefix()
- logger.info(f"{log_prefix} HeartFChatting: 麦麦打算好好聊聊 (定时器: {self._loop_timer:.1f}s)")
+ logger.info(f"{log_prefix} HeartFChatting: 麦麦打算好好聊聊 (进入专注模式)")
try:
thinking_id = ""
- while True:
+ while True: # Loop indefinitely until cancelled
cycle_timers = {} # <--- Initialize timers dict for this cycle
# Access MessageManager directly
if message_manager.check_if_sending_message_exist(self.stream_id, thinking_id):
- # logger.info(f"{log_prefix} HeartFChatting: 11111111111111111111111111111111麦麦还在发消息,等会再规划")
+ # logger.info(f"{log_prefix} HeartFChatting: 麦麦还在发消息,等会再规划")
await asyncio.sleep(1)
continue
else:
- # logger.info(f"{log_prefix} HeartFChatting: 11111111111111111111111111111111麦麦不发消息了,开始规划")
+ # logger.info(f"{log_prefix} HeartFChatting: 麦麦不发消息了,开始规划")
pass
- async with self._timer_lock:
- current_timer = self._loop_timer
- if current_timer <= 0:
- logger.info(
- f"{log_prefix} HeartFChatting: 聊太久了,麦麦打算休息一下 (计时器为 {current_timer:.1f}s)。退出HeartFChatting。"
- )
- break
-
# 记录循环周期开始时间,用于计时和休眠计算
loop_cycle_start_time = time.monotonic()
action_taken_this_cycle = False
@@ -296,7 +254,7 @@ class HeartFChatting:
logger.error(f"{log_prefix} Planner LLM 失败,跳过本周期回复尝试。理由: {reasoning}")
# Optionally add a longer sleep?
action_taken_this_cycle = False # Ensure no action is counted
- # Continue to timer decrement and sleep
+ # Continue to sleep logic
elif action == "text_reply":
logger.debug(f"{log_prefix} HeartFChatting: 麦麦决定回复文本. 理由: {reasoning}")
@@ -371,11 +329,11 @@ class HeartFChatting:
with Timer("Wait New Msg", cycle_timers): # <--- Start Wait timer
wait_start_time = time.monotonic()
while True:
- # 检查计时器是否耗尽
- async with self._timer_lock:
- if self._loop_timer <= 0:
- logger.info(f"{log_prefix} HeartFChatting: 等待新消息时计时器耗尽。")
- break # 计时器耗尽,退出等待
+ # Removed timer check within wait loop
+ # async with self._timer_lock:
+ # if self._loop_timer <= 0:
+ # logger.info(f"{log_prefix} HeartFChatting: 等待新消息时计时器耗尽。")
+ # break # 计时器耗尽,退出等待
# 检查是否有新消息
has_new = await observation.has_new_messages_since(planner_start_db_time)
@@ -421,7 +379,7 @@ class HeartFChatting:
if timer_strings: # 如果有有效计时器数据才打印
logger.debug(f"{log_prefix} 该次决策耗时: {'; '.join(timer_strings)}")
- # --- Timer Decrement --- #
+ # --- Timer Decrement Removed --- #
cycle_duration = time.monotonic() - loop_cycle_start_time
except Exception as e_cycle:
@@ -437,20 +395,24 @@ class HeartFChatting:
self._processing_lock.release()
# logger.trace(f"{log_prefix} 循环释放了处理锁.") # Reduce noise
- async with self._timer_lock:
- self._loop_timer -= cycle_duration
- # Log timer decrement less aggressively
- if cycle_duration > 0.1 or not action_taken_this_cycle:
- logger.debug(
- f"{log_prefix} HeartFChatting: 周期耗时 {cycle_duration:.2f}s. 剩余时间: {self._loop_timer:.1f}s."
- )
+ # --- Timer Decrement Logging Removed ---
+ # async with self._timer_lock:
+ # self._loop_timer -= cycle_duration
+ # # Log timer decrement less aggressively
+ # if cycle_duration > 0.1 or not action_taken_this_cycle:
+ # logger.debug(
+ # f"{log_prefix} HeartFChatting: 周期耗时 {cycle_duration:.2f}s. 剩余时间: {self._loop_timer:.1f}s."
+ # )
+ if cycle_duration > 0.1:
+ logger.debug(f"{log_prefix} HeartFChatting: 周期耗时 {cycle_duration:.2f}s.")
+
# --- Delay --- #
try:
sleep_duration = 0.0
if not action_taken_this_cycle and cycle_duration < 1.5:
sleep_duration = 1.5 - cycle_duration
- elif cycle_duration < 0.2:
+ elif cycle_duration < 0.2: # Keep minimal sleep even after action
sleep_duration = 0.2
if sleep_duration > 0:
@@ -459,7 +421,7 @@ class HeartFChatting:
except asyncio.CancelledError:
logger.info(f"{log_prefix} Sleep interrupted, loop likely cancelling.")
- break
+ break # Exit loop immediately on cancellation
except asyncio.CancelledError:
logger.info(f"{log_prefix} HeartFChatting: 麦麦的聊天主循环被取消了")
From 30fde099b610e0bf796d14a1f373460ce0af1989 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 16:12:44 +0000
Subject: [PATCH 26/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/heart_flow/background_tasks.py | 7 ++++---
src/heart_flow/sub_heartflow.py | 8 ++++----
src/plugins/heartFC_chat/heartFC_chat.py | 9 ++++-----
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/heart_flow/background_tasks.py b/src/heart_flow/background_tasks.py
index 2bfd7420..85fb6c50 100644
--- a/src/heart_flow/background_tasks.py
+++ b/src/heart_flow/background_tasks.py
@@ -51,14 +51,14 @@ class BackgroundTaskManager:
self.log_interval = log_interval
self.inactive_threshold = inactive_threshold # For cleanup task
self.interest_eval_interval = interest_eval_interval # 存储兴趣评估间隔
- self.random_deactivation_interval = random_deactivation_interval # 存储随机停用间隔
+ self.random_deactivation_interval = random_deactivation_interval # 存储随机停用间隔
# Task references
self._state_update_task: Optional[asyncio.Task] = None
self._cleanup_task: Optional[asyncio.Task] = None
self._logging_task: Optional[asyncio.Task] = None
self._interest_eval_task: Optional[asyncio.Task] = None # 新增兴趣评估任务引用
- self._random_deactivation_task: Optional[asyncio.Task] = None # 新增随机停用任务引用
+ self._random_deactivation_task: Optional[asyncio.Task] = None # 新增随机停用任务引用
self._tasks: List[Optional[asyncio.Task]] = [] # Keep track of all tasks
async def start_tasks(self):
@@ -110,7 +110,7 @@ class BackgroundTaskManager:
self._random_deactivation_task,
self._run_random_deactivation_cycle,
"hf_random_deactivation",
- "debug", # 设为debug,避免过多日志
+ "debug", # 设为debug,避免过多日志
f"随机停用任务已启动 间隔:{self.random_deactivation_interval}s",
"_random_deactivation_task",
),
@@ -249,6 +249,7 @@ class BackgroundTaskManager:
async def _perform_random_deactivation_work(self):
"""执行一轮子心流随机停用检查。"""
await self.subheartflow_manager.randomly_deactivate_subflows()
+
# --- 结束新增 ---
# --- Specific Task Runners --- #
diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py
index 85ceaf76..76d60b14 100644
--- a/src/heart_flow/sub_heartflow.py
+++ b/src/heart_flow/sub_heartflow.py
@@ -364,17 +364,17 @@ class SubHeartflow:
if self.heart_fc_instance._loop_task is None or self.heart_fc_instance._loop_task.done():
logger.info(f"{log_prefix} HeartFChatting 实例存在但循环未运行,尝试启动...")
try:
- await self.heart_fc_instance.start() # 启动循环
+ await self.heart_fc_instance.start() # 启动循环
logger.info(f"{log_prefix} HeartFChatting 循环已启动。")
return True
except Exception as e:
logger.error(f"{log_prefix} 尝试启动现有 HeartFChatting 循环时出错: {e}")
logger.error(traceback.format_exc())
- return False # 启动失败
+ return False # 启动失败
else:
# 任务正在运行
logger.debug(f"{log_prefix} HeartFChatting 已在运行中。")
- return True # 已经在运行
+ return True # 已经在运行
# 如果实例不存在,则创建并启动
logger.info(f"{log_prefix} 麦麦准备开始专注聊天 (创建新实例)...")
@@ -383,7 +383,7 @@ class SubHeartflow:
chat_id=self.chat_id,
)
if await self.heart_fc_instance._initialize():
- await self.heart_fc_instance.start() # 初始化成功后启动循环
+ await self.heart_fc_instance.start() # 初始化成功后启动循环
logger.info(f"{log_prefix} 麦麦已成功进入专注聊天模式 (新实例已启动)。")
return True
else:
diff --git a/src/plugins/heartFC_chat/heartFC_chat.py b/src/plugins/heartFC_chat/heartFC_chat.py
index 371c817e..ac8030f0 100644
--- a/src/plugins/heartFC_chat/heartFC_chat.py
+++ b/src/plugins/heartFC_chat/heartFC_chat.py
@@ -159,7 +159,7 @@ class HeartFChatting:
# 直接检查是否激活,无需检查计时器
if not self._loop_active:
should_start_loop = True
- self._loop_active = True # 标记为活动,防止重复启动
+ self._loop_active = True # 标记为活动,防止重复启动
if should_start_loop:
# 检查是否已有任务在运行(理论上不应该,因为 _loop_active=False)
@@ -210,7 +210,7 @@ class HeartFChatting:
logger.info(f"{log_prefix} HeartFChatting: 麦麦打算好好聊聊 (进入专注模式)")
try:
thinking_id = ""
- while True: # Loop indefinitely until cancelled
+ while True: # Loop indefinitely until cancelled
cycle_timers = {} # <--- Initialize timers dict for this cycle
# Access MessageManager directly
@@ -406,13 +406,12 @@ class HeartFChatting:
if cycle_duration > 0.1:
logger.debug(f"{log_prefix} HeartFChatting: 周期耗时 {cycle_duration:.2f}s.")
-
# --- Delay --- #
try:
sleep_duration = 0.0
if not action_taken_this_cycle and cycle_duration < 1.5:
sleep_duration = 1.5 - cycle_duration
- elif cycle_duration < 0.2: # Keep minimal sleep even after action
+ elif cycle_duration < 0.2: # Keep minimal sleep even after action
sleep_duration = 0.2
if sleep_duration > 0:
@@ -421,7 +420,7 @@ class HeartFChatting:
except asyncio.CancelledError:
logger.info(f"{log_prefix} Sleep interrupted, loop likely cancelling.")
- break # Exit loop immediately on cancellation
+ break # Exit loop immediately on cancellation
except asyncio.CancelledError:
logger.info(f"{log_prefix} HeartFChatting: 麦麦的聊天主循环被取消了")
From dc452fa69eb3e2d31ddf72b7b94003f93248af72 Mon Sep 17 00:00:00 2001
From: SengokuCola <1026294844@qq.com>
Date: Thu, 24 Apr 2025 00:14:09 +0800
Subject: [PATCH 27/28] Update mai_state_manager.py
---
src/heart_flow/mai_state_manager.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/heart_flow/mai_state_manager.py b/src/heart_flow/mai_state_manager.py
index 174f31be..20e60053 100644
--- a/src/heart_flow/mai_state_manager.py
+++ b/src/heart_flow/mai_state_manager.py
@@ -141,7 +141,7 @@ class MaiStateManager:
logger.info("当前在[正常聊天]思考要不要继续聊下去......")
elif current_status == MaiState.FOCUSED_CHAT:
logger.info("当前在[专心聊天]思考要不要继续聊下去......")
-
+
# 1. 麦麦每分钟都有概率离线
if time_since_last_min_check >= 60:
if current_status != MaiState.OFFLINE:
@@ -188,6 +188,11 @@ class MaiStateManager:
choices_list = [MaiState.OFFLINE, MaiState.NORMAL_CHAT]
next_state = random.choices(choices_list, weights=weights, k=1)[0]
logger.debug(f"深入聊天结束,接下来 {next_state.value}")
+
+
+ if enable_unlimited_hfc_chat:
+ logger.debug("调试用:开挂了,强制切换到专注聊天")
+ next_state = MaiState.FOCUSED_CHAT
# 如果决定了下一个状态,且这个状态与当前状态不同,则返回下一个状态
if next_state is not None and next_state != current_status:
From 39905074d1097d0f76ab749babe4b2fb79e2cbf5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 23 Apr 2025 16:14:33 +0000
Subject: [PATCH 28/28] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/heart_flow/mai_state_manager.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/heart_flow/mai_state_manager.py b/src/heart_flow/mai_state_manager.py
index 20e60053..740b715f 100644
--- a/src/heart_flow/mai_state_manager.py
+++ b/src/heart_flow/mai_state_manager.py
@@ -141,7 +141,7 @@ class MaiStateManager:
logger.info("当前在[正常聊天]思考要不要继续聊下去......")
elif current_status == MaiState.FOCUSED_CHAT:
logger.info("当前在[专心聊天]思考要不要继续聊下去......")
-
+
# 1. 麦麦每分钟都有概率离线
if time_since_last_min_check >= 60:
if current_status != MaiState.OFFLINE:
@@ -188,8 +188,7 @@ class MaiStateManager:
choices_list = [MaiState.OFFLINE, MaiState.NORMAL_CHAT]
next_state = random.choices(choices_list, weights=weights, k=1)[0]
logger.debug(f"深入聊天结束,接下来 {next_state.value}")
-
-
+
if enable_unlimited_hfc_chat:
logger.debug("调试用:开挂了,强制切换到专注聊天")
next_state = MaiState.FOCUSED_CHAT