🤖 自动格式化代码 [skip ci]

This commit is contained in:
github-actions[bot]
2025-06-16 14:10:49 +00:00
parent f6215cd560
commit ee005456ea
16 changed files with 461 additions and 383 deletions

View File

@@ -668,7 +668,9 @@ class ExampleComprehensivePlugin(BasePlugin):
"name": ConfigField(type=str, default="example_plugin", description="插件名称", required=True), "name": ConfigField(type=str, default="example_plugin", description="插件名称", required=True),
"version": ConfigField(type=str, default="2.0.0", description="插件版本号"), "version": ConfigField(type=str, default="2.0.0", description="插件版本号"),
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
"description": ConfigField(type=str, default="综合示例插件,展示新插件系统的完整功能", description="插件描述", required=True) "description": ConfigField(
type=str, default="综合示例插件,展示新插件系统的完整功能", description="插件描述", required=True
),
}, },
"components": { "components": {
"enable_greeting": ConfigField(type=bool, default=True, description="是否启用'智能问候'Action"), "enable_greeting": ConfigField(type=bool, default=True, description="是否启用'智能问候'Action"),
@@ -677,47 +679,53 @@ class ExampleComprehensivePlugin(BasePlugin):
"enable_send": ConfigField(type=bool, default=True, description="是否启用'/send'命令"), "enable_send": ConfigField(type=bool, default=True, description="是否启用'/send'命令"),
"enable_echo": ConfigField(type=bool, default=True, description="是否启用'/echo'命令"), "enable_echo": ConfigField(type=bool, default=True, description="是否启用'/echo'命令"),
"enable_info": ConfigField(type=bool, default=True, description="是否启用'/info'命令"), "enable_info": ConfigField(type=bool, default=True, description="是否启用'/info'命令"),
"enable_dice": ConfigField(type=bool, default=True, description="是否启用'!dice'命令") "enable_dice": ConfigField(type=bool, default=True, description="是否启用'!dice'命令"),
}, },
"greeting": { "greeting": {
"template": ConfigField(type=str, default="你好,{username}欢迎使用MaiBot综合插件系统", description="问候消息模板"), "template": ConfigField(
type=str, default="你好,{username}欢迎使用MaiBot综合插件系统", description="问候消息模板"
),
"enable_emoji": ConfigField(type=bool, default=True, description="问候时是否附带表情"), "enable_emoji": ConfigField(type=bool, default=True, description="问候时是否附带表情"),
"enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成个性化问候语") "enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成个性化问候语"),
}, },
"helpful": { "helpful": {
"enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成帮助内容"), "enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成帮助内容"),
"enable_emoji": ConfigField(type=bool, default=True, description="提供帮助时是否附带表情"), "enable_emoji": ConfigField(type=bool, default=True, description="提供帮助时是否附带表情"),
"random_activation_probability": ConfigField(type=float, default=0.15, description="Normal模式下随机触发帮助的概率") "random_activation_probability": ConfigField(
type=float, default=0.15, description="Normal模式下随机触发帮助的概率"
),
}, },
"help": { "help": {
"show_extended_help": ConfigField(type=bool, default=True, description="是否显示扩展帮助信息"), "show_extended_help": ConfigField(type=bool, default=True, description="是否显示扩展帮助信息"),
"include_action_info": ConfigField(type=bool, default=True, description="帮助信息中是否包含Action的信息"), "include_action_info": ConfigField(type=bool, default=True, description="帮助信息中是否包含Action的信息"),
"include_config_info": ConfigField(type=bool, default=True, description="帮助信息中是否包含配置相关信息"), "include_config_info": ConfigField(type=bool, default=True, description="帮助信息中是否包含配置相关信息"),
"enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成帮助摘要"), "enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成帮助摘要"),
"enable_emoji": ConfigField(type=bool, default=True, description="帮助信息中是否使用表情符号") "enable_emoji": ConfigField(type=bool, default=True, description="帮助信息中是否使用表情符号"),
}, },
"send": { "send": {
"max_message_length": ConfigField(type=int, default=500, description="发送消息的最大长度限制"), "max_message_length": ConfigField(type=int, default=500, description="发送消息的最大长度限制"),
"enable_length_check": ConfigField(type=bool, default=True, description="是否启用消息长度检查"), "enable_length_check": ConfigField(type=bool, default=True, description="是否启用消息长度检查"),
"default_platform": ConfigField(type=str, default="qq", description="默认发送平台") "default_platform": ConfigField(type=str, default="qq", description="默认发送平台"),
}, },
"echo": { "echo": {
"max_length": ConfigField(type=int, default=200, description="回声消息的最大长度"), "max_length": ConfigField(type=int, default=200, description="回声消息的最大长度"),
"enable_formatting": ConfigField(type=bool, default=True, description="是否为回声消息添加'🔊 回声: '前缀") "enable_formatting": ConfigField(type=bool, default=True, description="是否为回声消息添加'🔊 回声: '前缀"),
}, },
"dice": { "dice": {
"enable_dice": ConfigField(type=bool, default=True, description="是否启用骰子功能"), "enable_dice": ConfigField(type=bool, default=True, description="是否启用骰子功能"),
"max_dice_count": ConfigField(type=int, default=10, description="一次最多可以掷的骰子数量") "max_dice_count": ConfigField(type=int, default=10, description="一次最多可以掷的骰子数量"),
}, },
"info": { "info": {
"show_detailed_info": ConfigField(type=bool, default=True, description="是否显示详细信息"), "show_detailed_info": ConfigField(type=bool, default=True, description="是否显示详细信息"),
"include_stream_info": ConfigField(type=bool, default=True, description="是否包含聊天流信息"), "include_stream_info": ConfigField(type=bool, default=True, description="是否包含聊天流信息"),
"max_content_preview": ConfigField(type=int, default=100, description="消息内容预览的最大长度") "max_content_preview": ConfigField(type=int, default=100, description="消息内容预览的最大长度"),
}, },
"logging": { "logging": {
"level": ConfigField(type=str, default="INFO", description="日志级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]), "level": ConfigField(
"prefix": ConfigField(type=str, default="[ExampleComprehensive]", description="日志前缀") type=str, default="INFO", description="日志级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]
} ),
"prefix": ConfigField(type=str, default="[ExampleComprehensive]", description="日志前缀"),
},
} }
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:

View File

@@ -1183,4 +1183,3 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,13 +1,10 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk, colorchooser, messagebox, filedialog from tkinter import ttk, messagebox, filedialog
import json import json
from pathlib import Path from pathlib import Path
import threading import threading
import queue
import time
import toml import toml
from datetime import datetime from datetime import datetime
import bisect
from collections import defaultdict from collections import defaultdict
import os import os
@@ -264,7 +261,7 @@ class VirtualLogDisplay:
foreground="#ffffff", foreground="#ffffff",
insertbackground="#ffffff", insertbackground="#ffffff",
selectbackground="#404040", selectbackground="#404040",
font=("Consolas", 10) font=("Consolas", 10),
) )
self.text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.scrollbar.config(command=self.text_widget.yview) self.scrollbar.config(command=self.text_widget.yview)
@@ -352,8 +349,14 @@ class VirtualLogDisplay:
# 记录标签信息(简化处理) # 记录标签信息(简化处理)
if tags and self.formatter.enable_level_colors: if tags and self.formatter.enable_level_colors:
level = log_entry.get("level", "info") level = log_entry.get("level", "info")
batch_tags.append(("line", len("".join(batch_text)) - len(line_text), batch_tags.append(
len("".join(batch_text)) - 1, f"level_{level}")) (
"line",
len("".join(batch_text)) - len(line_text),
len("".join(batch_text)) - 1,
f"level_{level}",
)
)
# 一次性插入所有文本 # 一次性插入所有文本
if batch_text: if batch_text:
@@ -408,7 +411,7 @@ class AsyncLogLoader:
if not line: if not line:
break break
lines.append(line) lines.append(line)
processed_size += len(line.encode('utf-8')) processed_size += len(line.encode("utf-8"))
if not lines: if not lines:
break break
@@ -619,10 +622,7 @@ class LogViewer:
self.selected_modules.clear() self.selected_modules.clear()
# 开始异步加载 # 开始异步加载
self.async_loader.load_file_async( self.async_loader.load_file_async(str(self.current_log_file), self.on_loading_progress)
str(self.current_log_file),
self.on_loading_progress
)
def on_module_selected(self, event=None): def on_module_selected(self, event=None):
"""模块选择事件""" """模块选择事件"""

View File

@@ -341,7 +341,12 @@ class HeartFChatting:
}, },
"observed_messages": "", "observed_messages": "",
}, },
"loop_action_info": {"action_taken": False, "reply_text": "", "command": "", "taken_time": time.time()}, "loop_action_info": {
"action_taken": False,
"reply_text": "",
"command": "",
"taken_time": time.time(),
},
} }
self._current_cycle_detail.set_loop_info(error_loop_info) self._current_cycle_detail.set_loop_info(error_loop_info)
self._current_cycle_detail.complete_cycle() self._current_cycle_detail.complete_cycle()
@@ -420,7 +425,12 @@ class HeartFChatting:
}, },
"observed_messages": "", "observed_messages": "",
}, },
"loop_action_info": {"action_taken": False, "reply_text": "", "command": "", "taken_time": time.time()}, "loop_action_info": {
"action_taken": False,
"reply_text": "",
"command": "",
"taken_time": time.time(),
},
} }
try: try:
self._current_cycle_detail.set_loop_info(error_loop_info) self._current_cycle_detail.set_loop_info(error_loop_info)

View File

@@ -1,4 +1,3 @@
from reportportal_client import current
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
from src.chat.heart_flow.observation.observation import Observation from src.chat.heart_flow.observation.observation import Observation
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
@@ -18,7 +17,12 @@ from json_repair import repair_json
from src.person_info.person_info import get_person_info_manager from src.person_info.person_info import get_person_info_manager
import json import json
import asyncio import asyncio
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat, get_raw_msg_by_timestamp_with_chat_inclusive, get_raw_msg_before_timestamp_with_chat, num_new_messages_since from src.chat.utils.chat_message_builder import (
get_raw_msg_by_timestamp_with_chat,
get_raw_msg_by_timestamp_with_chat_inclusive,
get_raw_msg_before_timestamp_with_chat,
num_new_messages_since,
)
import os import os
import pickle import pickle
@@ -148,14 +152,16 @@ class RelationshipProcessor(BaseProcessor):
"""从文件加载持久化的缓存""" """从文件加载持久化的缓存"""
if os.path.exists(self.cache_file_path): if os.path.exists(self.cache_file_path):
try: try:
with open(self.cache_file_path, 'rb') as f: with open(self.cache_file_path, "rb") as f:
cache_data = pickle.load(f) cache_data = pickle.load(f)
# 新格式:包含额外信息的缓存 # 新格式:包含额外信息的缓存
self.person_engaged_cache = cache_data.get('person_engaged_cache', {}) self.person_engaged_cache = cache_data.get("person_engaged_cache", {})
self.last_processed_message_time = cache_data.get('last_processed_message_time', 0.0) self.last_processed_message_time = cache_data.get("last_processed_message_time", 0.0)
self.last_cleanup_time = cache_data.get('last_cleanup_time', 0.0) self.last_cleanup_time = cache_data.get("last_cleanup_time", 0.0)
logger.info(f"{self.log_prefix} 成功加载关系缓存,包含 {len(self.person_engaged_cache)} 个用户,最后处理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '未设置'}") logger.info(
f"{self.log_prefix} 成功加载关系缓存,包含 {len(self.person_engaged_cache)} 个用户,最后处理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '未设置'}"
)
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 加载关系缓存失败: {e}") logger.error(f"{self.log_prefix} 加载关系缓存失败: {e}")
self.person_engaged_cache = {} self.person_engaged_cache = {}
@@ -168,11 +174,11 @@ class RelationshipProcessor(BaseProcessor):
try: try:
os.makedirs(os.path.dirname(self.cache_file_path), exist_ok=True) os.makedirs(os.path.dirname(self.cache_file_path), exist_ok=True)
cache_data = { cache_data = {
'person_engaged_cache': self.person_engaged_cache, "person_engaged_cache": self.person_engaged_cache,
'last_processed_message_time': self.last_processed_message_time, "last_processed_message_time": self.last_processed_message_time,
'last_cleanup_time': self.last_cleanup_time "last_cleanup_time": self.last_cleanup_time,
} }
with open(self.cache_file_path, 'wb') as f: with open(self.cache_file_path, "wb") as f:
pickle.dump(cache_data, f) pickle.dump(cache_data, f)
logger.debug(f"{self.log_prefix} 成功保存关系缓存") logger.debug(f"{self.log_prefix} 成功保存关系缓存")
except Exception as e: except Exception as e:
@@ -201,7 +207,7 @@ class RelationshipProcessor(BaseProcessor):
if before_messages: if before_messages:
# 由于get_raw_msg_before_timestamp_with_chat返回按时间升序排序的消息最后一个是最接近message_time的 # 由于get_raw_msg_before_timestamp_with_chat返回按时间升序排序的消息最后一个是最接近message_time的
# 我们需要第一个消息作为开始时间但应该确保至少包含5条消息或该用户之前的消息 # 我们需要第一个消息作为开始时间但应该确保至少包含5条消息或该用户之前的消息
potential_start_time = before_messages[0]['time'] potential_start_time = before_messages[0]["time"]
else: else:
# 如果没有前面的消息,就从当前消息开始 # 如果没有前面的消息,就从当前消息开始
potential_start_time = message_time potential_start_time = message_time
@@ -212,10 +218,12 @@ class RelationshipProcessor(BaseProcessor):
"start_time": potential_start_time, "start_time": potential_start_time,
"end_time": message_time, "end_time": message_time,
"last_msg_time": message_time, "last_msg_time": message_time,
"message_count": self._count_messages_in_timerange(potential_start_time, message_time) "message_count": self._count_messages_in_timerange(potential_start_time, message_time),
} }
segments.append(new_segment) segments.append(new_segment)
logger.info(f"{self.log_prefix} 为用户 {person_id} 创建新消息段: 时间范围 {time.strftime('%H:%M:%S', time.localtime(potential_start_time))} - {time.strftime('%H:%M:%S', time.localtime(message_time))}, 消息数: {new_segment['message_count']}") logger.info(
f"{self.log_prefix} 为用户 {person_id} 创建新消息段: 时间范围 {time.strftime('%H:%M:%S', time.localtime(potential_start_time))} - {time.strftime('%H:%M:%S', time.localtime(message_time))}, 消息数: {new_segment['message_count']}"
)
self._save_cache() self._save_cache()
return return
@@ -242,7 +250,7 @@ class RelationshipProcessor(BaseProcessor):
) )
if after_messages and len(after_messages) >= 5: if after_messages and len(after_messages) >= 5:
# 如果有足够的后续消息使用第5条消息的时间作为结束时间 # 如果有足够的后续消息使用第5条消息的时间作为结束时间
last_segment["end_time"] = after_messages[4]['time'] last_segment["end_time"] = after_messages[4]["time"]
else: else:
# 如果没有足够的后续消息,保持原有的结束时间 # 如果没有足够的后续消息,保持原有的结束时间
pass pass
@@ -257,7 +265,7 @@ class RelationshipProcessor(BaseProcessor):
"start_time": potential_start_time, "start_time": potential_start_time,
"end_time": message_time, "end_time": message_time,
"last_msg_time": message_time, "last_msg_time": message_time,
"message_count": self._count_messages_in_timerange(potential_start_time, message_time) "message_count": self._count_messages_in_timerange(potential_start_time, message_time),
} }
segments.append(new_segment) segments.append(new_segment)
logger.info(f"{self.log_prefix} 为用户 {person_id} 创建新消息段超过10条消息间隔: {new_segment}") logger.info(f"{self.log_prefix} 为用户 {person_id} 创建新消息段超过10条消息间隔: {new_segment}")
@@ -306,7 +314,7 @@ class RelationshipProcessor(BaseProcessor):
"users_cleaned": 0, "users_cleaned": 0,
"segments_removed": 0, "segments_removed": 0,
"total_segments_before": 0, "total_segments_before": 0,
"total_segments_after": 0 "total_segments_after": 0,
} }
max_age_seconds = SEGMENT_CLEANUP_CONFIG["max_segment_age_days"] * 24 * 3600 max_age_seconds = SEGMENT_CLEANUP_CONFIG["max_segment_age_days"] * 24 * 3600
@@ -326,7 +334,9 @@ class RelationshipProcessor(BaseProcessor):
segments_after_age_cleanup.append(segment) segments_after_age_cleanup.append(segment)
else: else:
cleanup_stats["segments_removed"] += 1 cleanup_stats["segments_removed"] += 1
logger.debug(f"{self.log_prefix} 移除用户 {person_id} 的过期消息段: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['start_time']))} - {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['end_time']))}") logger.debug(
f"{self.log_prefix} 移除用户 {person_id} 的过期消息段: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['start_time']))} - {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['end_time']))}"
)
# 2. 按数量清理:如果消息段数量仍然过多,保留最新的 # 2. 按数量清理:如果消息段数量仍然过多,保留最新的
if len(segments_after_age_cleanup) > max_segments_per_user: if len(segments_after_age_cleanup) > max_segments_per_user:
@@ -335,7 +345,9 @@ class RelationshipProcessor(BaseProcessor):
segments_removed_count = len(segments_after_age_cleanup) - max_segments_per_user segments_removed_count = len(segments_after_age_cleanup) - max_segments_per_user
cleanup_stats["segments_removed"] += segments_removed_count cleanup_stats["segments_removed"] += segments_removed_count
segments_after_age_cleanup = segments_after_age_cleanup[:max_segments_per_user] segments_after_age_cleanup = segments_after_age_cleanup[:max_segments_per_user]
logger.debug(f"{self.log_prefix} 用户 {person_id} 消息段数量过多,移除 {segments_removed_count} 个最老的消息段") logger.debug(
f"{self.log_prefix} 用户 {person_id} 消息段数量过多,移除 {segments_removed_count} 个最老的消息段"
)
# 使用清理后的消息段 # 使用清理后的消息段
@@ -361,8 +373,12 @@ class RelationshipProcessor(BaseProcessor):
# 保存缓存 # 保存缓存
if cleanup_stats["segments_removed"] > 0 or len(users_to_remove) > 0: if cleanup_stats["segments_removed"] > 0 or len(users_to_remove) > 0:
self._save_cache() self._save_cache()
logger.info(f"{self.log_prefix} 清理完成 - 影响用户: {cleanup_stats['users_cleaned']}, 移除消息段: {cleanup_stats['segments_removed']}, 移除用户: {len(users_to_remove)}") logger.info(
logger.info(f"{self.log_prefix} 消息段统计 - 清理前: {cleanup_stats['total_segments_before']}, 清理后: {cleanup_stats['total_segments_after']}") f"{self.log_prefix} 清理完成 - 影响用户: {cleanup_stats['users_cleaned']}, 移除消息段: {cleanup_stats['segments_removed']}, 移除用户: {len(users_to_remove)}"
)
logger.info(
f"{self.log_prefix} 消息段统计 - 清理前: {cleanup_stats['total_segments_before']}, 清理后: {cleanup_stats['total_segments_after']}"
)
else: else:
logger.debug(f"{self.log_prefix} 清理完成 - 无需清理任何内容") logger.debug(f"{self.log_prefix} 清理完成 - 无需清理任何内容")
@@ -391,10 +407,16 @@ class RelationshipProcessor(BaseProcessor):
return f"{self.log_prefix} 关系缓存为空" return f"{self.log_prefix} 关系缓存为空"
status_lines = [f"{self.log_prefix} 关系缓存状态:"] status_lines = [f"{self.log_prefix} 关系缓存状态:"]
status_lines.append(f"最后处理消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '未设置'}") status_lines.append(
status_lines.append(f"最后清理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_cleanup_time)) if self.last_cleanup_time > 0 else '执行'}") f"最后处理消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '设置'}"
)
status_lines.append(
f"最后清理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_cleanup_time)) if self.last_cleanup_time > 0 else '未执行'}"
)
status_lines.append(f"总用户数:{len(self.person_engaged_cache)}") status_lines.append(f"总用户数:{len(self.person_engaged_cache)}")
status_lines.append(f"清理配置:{'启用' if SEGMENT_CLEANUP_CONFIG['enable_cleanup'] else '禁用'} (最大保存{SEGMENT_CLEANUP_CONFIG['max_segment_age_days']}天, 每用户最多{SEGMENT_CLEANUP_CONFIG['max_segments_per_user']}段)") status_lines.append(
f"清理配置:{'启用' if SEGMENT_CLEANUP_CONFIG['enable_cleanup'] else '禁用'} (最大保存{SEGMENT_CLEANUP_CONFIG['max_segment_age_days']}天, 每用户最多{SEGMENT_CLEANUP_CONFIG['max_segments_per_user']}段)"
)
status_lines.append("") status_lines.append("")
for person_id, segments in self.person_engaged_cache.items(): for person_id, segments in self.person_engaged_cache.items():
@@ -404,10 +426,12 @@ class RelationshipProcessor(BaseProcessor):
status_lines.append(f" 消息段数:{len(segments)}") status_lines.append(f" 消息段数:{len(segments)}")
for i, segment in enumerate(segments): for i, segment in enumerate(segments):
start_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['start_time'])) start_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(segment["start_time"]))
end_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['end_time'])) end_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(segment["end_time"]))
last_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['last_msg_time'])) last_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(segment["last_msg_time"]))
status_lines.append(f"{i+1}: {start_str} -> {end_str} (最后消息: {last_str}, 消息数: {segment['message_count']})") status_lines.append(
f"{i + 1}: {start_str} -> {end_str} (最后消息: {last_str}, 消息数: {segment['message_count']})"
)
status_lines.append("") status_lines.append("")
return "\n".join(status_lines) return "\n".join(status_lines)
@@ -460,20 +484,31 @@ class RelationshipProcessor(BaseProcessor):
# 从聊天观察中提取用户信息并更新消息段 # 从聊天观察中提取用户信息并更新消息段
# 获取最新的非bot消息来更新消息段 # 获取最新的非bot消息来更新消息段
latest_messages = get_raw_msg_by_timestamp_with_chat( latest_messages = get_raw_msg_by_timestamp_with_chat(
self.subheartflow_id, self.last_processed_message_time, current_time, limit=50 # 获取自上次处理后的消息 self.subheartflow_id,
self.last_processed_message_time,
current_time,
limit=50, # 获取自上次处理后的消息
) )
if latest_messages: if latest_messages:
# 处理所有新的非bot消息 # 处理所有新的非bot消息
for latest_msg in latest_messages: for latest_msg in latest_messages:
user_id = latest_msg.get('user_id') user_id = latest_msg.get("user_id")
platform = latest_msg.get('user_platform') or latest_msg.get('chat_info_platform') platform = latest_msg.get("user_platform") or latest_msg.get("chat_info_platform")
msg_time = latest_msg.get('time', 0) msg_time = latest_msg.get("time", 0)
if user_id and platform and user_id != global_config.bot.qq_account and msg_time > self.last_processed_message_time: if (
user_id
and platform
and user_id != global_config.bot.qq_account
and msg_time > self.last_processed_message_time
):
from src.person_info.person_info import PersonInfoManager from src.person_info.person_info import PersonInfoManager
person_id = PersonInfoManager.get_person_id(platform, user_id) person_id = PersonInfoManager.get_person_id(platform, user_id)
self._update_message_segments(person_id, msg_time) self._update_message_segments(person_id, msg_time)
logger.debug(f"{self.log_prefix} 更新用户 {person_id} 的消息段,消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(msg_time))}") logger.debug(
f"{self.log_prefix} 更新用户 {person_id} 的消息段,消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(msg_time))}"
)
self.last_processed_message_time = max(self.last_processed_message_time, msg_time) self.last_processed_message_time = max(self.last_processed_message_time, msg_time)
break break
@@ -496,9 +531,7 @@ class RelationshipProcessor(BaseProcessor):
for person_id in users_to_build_relationship: for person_id in users_to_build_relationship:
segments = self.person_engaged_cache[person_id] segments = self.person_engaged_cache[person_id]
# 异步执行关系构建 # 异步执行关系构建
asyncio.create_task( asyncio.create_task(self.update_impression_on_segments(person_id, self.subheartflow_id, segments))
self.update_impression_on_segments(person_id, self.subheartflow_id, segments)
)
# 移除已处理的用户缓存 # 移除已处理的用户缓存
del self.person_engaged_cache[person_id] del self.person_engaged_cache[person_id]
self._save_cache() self._save_cache()
@@ -676,12 +709,16 @@ class RelationshipProcessor(BaseProcessor):
for i, segment in enumerate(segments): for i, segment in enumerate(segments):
start_time = segment["start_time"] start_time = segment["start_time"]
end_time = segment["end_time"] end_time = segment["end_time"]
message_count = segment["message_count"] segment["message_count"]
start_date = time.strftime('%Y-%m-%d %H:%M', time.localtime(start_time)) start_date = time.strftime("%Y-%m-%d %H:%M", time.localtime(start_time))
# 获取该段的消息(包含边界) # 获取该段的消息(包含边界)
segment_messages = get_raw_msg_by_timestamp_with_chat_inclusive(self.subheartflow_id, start_time, end_time) segment_messages = get_raw_msg_by_timestamp_with_chat_inclusive(
logger.info(f"消息段 {i+1}: {start_date} - {time.strftime('%Y-%m-%d %H:%M', time.localtime(end_time))}, 消息数: {len(segment_messages)}") self.subheartflow_id, start_time, end_time
)
logger.info(
f"消息段 {i + 1}: {start_date} - {time.strftime('%Y-%m-%d %H:%M', time.localtime(end_time))}, 消息数: {len(segment_messages)}"
)
if segment_messages: if segment_messages:
# 如果不是第一个消息段,在消息列表前添加间隔标识 # 如果不是第一个消息段,在消息列表前添加间隔标识
@@ -696,7 +733,7 @@ class RelationshipProcessor(BaseProcessor):
"display_message": f"...(中间省略一些消息){start_date} 之后的消息如下...", "display_message": f"...(中间省略一些消息){start_date} 之后的消息如下...",
"is_action_record": True, "is_action_record": True,
"chat_info_platform": segment_messages[0].get("chat_info_platform", ""), "chat_info_platform": segment_messages[0].get("chat_info_platform", ""),
"chat_id": chat_id "chat_id": chat_id,
} }
processed_messages.append(gap_message) processed_messages.append(gap_message)
@@ -705,16 +742,14 @@ class RelationshipProcessor(BaseProcessor):
if processed_messages: if processed_messages:
# 按时间排序所有消息(包括间隔标识) # 按时间排序所有消息(包括间隔标识)
processed_messages.sort(key=lambda x: x['time']) processed_messages.sort(key=lambda x: x["time"])
logger.info(f"{person_id} 获取到总共 {len(processed_messages)} 条消息(包含间隔标识)用于印象更新") logger.info(f"{person_id} 获取到总共 {len(processed_messages)} 条消息(包含间隔标识)用于印象更新")
relationship_manager = get_relationship_manager() relationship_manager = get_relationship_manager()
# 调用原有的更新方法 # 调用原有的更新方法
await relationship_manager.update_person_impression( await relationship_manager.update_person_impression(
person_id=person_id, person_id=person_id, timestamp=time.time(), bot_engaged_messages=processed_messages
timestamp=time.time(),
bot_engaged_messages=processed_messages
) )
else: else:
logger.info(f"没有找到 {person_id} 的消息段对应的消息,不更新印象") logger.info(f"没有找到 {person_id} 的消息段对应的消息,不更新印象")
@@ -919,6 +954,4 @@ class RelationshipProcessor(BaseProcessor):
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
init_prompt() init_prompt()

View File

@@ -47,7 +47,9 @@ class HFCloopObservation:
action_reasoning = action_result.get("reasoning", "未提供理由") action_reasoning = action_result.get("reasoning", "未提供理由")
is_taken = cycle.loop_action_info.get("action_taken", False) is_taken = cycle.loop_action_info.get("action_taken", False)
action_taken_time = cycle.loop_action_info.get("taken_time", 0) action_taken_time = cycle.loop_action_info.get("taken_time", 0)
action_taken_time_str = datetime.fromtimestamp(action_taken_time).strftime("%H:%M:%S") if action_taken_time > 0 else "未知时间" action_taken_time_str = (
datetime.fromtimestamp(action_taken_time).strftime("%H:%M:%S") if action_taken_time > 0 else "未知时间"
)
# print(action_type) # print(action_type)
# print(action_reasoning) # print(action_reasoning)
# print(is_taken) # print(is_taken)

View File

@@ -2,7 +2,7 @@ import asyncio
import time import time
import traceback import traceback
from random import random from random import random
from typing import List, Optional, Dict, Any # 导入类型提示 from typing import List, Optional, Dict # 导入类型提示
import os import os
import pickle import pickle
from maim_message import UserInfo, Seg from maim_message import UserInfo, Seg
@@ -24,7 +24,12 @@ from src.chat.normal_chat.normal_chat_action_modifier import NormalChatActionMod
from src.chat.normal_chat.normal_chat_expressor import NormalChatExpressor from src.chat.normal_chat.normal_chat_expressor import NormalChatExpressor
from src.chat.focus_chat.replyer.default_replyer import DefaultReplyer from src.chat.focus_chat.replyer.default_replyer import DefaultReplyer
from src.person_info.person_info import PersonInfoManager from src.person_info.person_info import PersonInfoManager
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat, get_raw_msg_by_timestamp_with_chat_inclusive, get_raw_msg_before_timestamp_with_chat, num_new_messages_since from src.chat.utils.chat_message_builder import (
get_raw_msg_by_timestamp_with_chat,
get_raw_msg_by_timestamp_with_chat_inclusive,
get_raw_msg_before_timestamp_with_chat,
num_new_messages_since,
)
from src.person_info.relationship_manager import get_relationship_manager from src.person_info.relationship_manager import get_relationship_manager
willing_manager = get_willing_manager() willing_manager = get_willing_manager()
@@ -109,14 +114,16 @@ class NormalChat:
"""从文件加载持久化的缓存""" """从文件加载持久化的缓存"""
if os.path.exists(self.cache_file_path): if os.path.exists(self.cache_file_path):
try: try:
with open(self.cache_file_path, 'rb') as f: with open(self.cache_file_path, "rb") as f:
cache_data = pickle.load(f) cache_data = pickle.load(f)
# 新格式:包含额外信息的缓存 # 新格式:包含额外信息的缓存
self.person_engaged_cache = cache_data.get('person_engaged_cache', {}) self.person_engaged_cache = cache_data.get("person_engaged_cache", {})
self.last_processed_message_time = cache_data.get('last_processed_message_time', 0.0) self.last_processed_message_time = cache_data.get("last_processed_message_time", 0.0)
self.last_cleanup_time = cache_data.get('last_cleanup_time', 0.0) self.last_cleanup_time = cache_data.get("last_cleanup_time", 0.0)
logger.info(f"[{self.stream_name}] 成功加载关系缓存,包含 {len(self.person_engaged_cache)} 个用户,最后处理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '未设置'}") logger.info(
f"[{self.stream_name}] 成功加载关系缓存,包含 {len(self.person_engaged_cache)} 个用户,最后处理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '未设置'}"
)
except Exception as e: except Exception as e:
logger.error(f"[{self.stream_name}] 加载关系缓存失败: {e}") logger.error(f"[{self.stream_name}] 加载关系缓存失败: {e}")
self.person_engaged_cache = {} self.person_engaged_cache = {}
@@ -129,11 +136,11 @@ class NormalChat:
try: try:
os.makedirs(os.path.dirname(self.cache_file_path), exist_ok=True) os.makedirs(os.path.dirname(self.cache_file_path), exist_ok=True)
cache_data = { cache_data = {
'person_engaged_cache': self.person_engaged_cache, "person_engaged_cache": self.person_engaged_cache,
'last_processed_message_time': self.last_processed_message_time, "last_processed_message_time": self.last_processed_message_time,
'last_cleanup_time': self.last_cleanup_time "last_cleanup_time": self.last_cleanup_time,
} }
with open(self.cache_file_path, 'wb') as f: with open(self.cache_file_path, "wb") as f:
pickle.dump(cache_data, f) pickle.dump(cache_data, f)
logger.debug(f"[{self.stream_name}] 成功保存关系缓存") logger.debug(f"[{self.stream_name}] 成功保存关系缓存")
except Exception as e: except Exception as e:
@@ -162,7 +169,7 @@ class NormalChat:
if before_messages: if before_messages:
# 由于get_raw_msg_before_timestamp_with_chat返回按时间升序排序的消息最后一个是最接近message_time的 # 由于get_raw_msg_before_timestamp_with_chat返回按时间升序排序的消息最后一个是最接近message_time的
# 我们需要第一个消息作为开始时间但应该确保至少包含5条消息或该用户之前的消息 # 我们需要第一个消息作为开始时间但应该确保至少包含5条消息或该用户之前的消息
potential_start_time = before_messages[0]['time'] potential_start_time = before_messages[0]["time"]
else: else:
# 如果没有前面的消息,就从当前消息开始 # 如果没有前面的消息,就从当前消息开始
potential_start_time = message_time potential_start_time = message_time
@@ -173,10 +180,12 @@ class NormalChat:
"start_time": potential_start_time, "start_time": potential_start_time,
"end_time": message_time, "end_time": message_time,
"last_msg_time": message_time, "last_msg_time": message_time,
"message_count": self._count_messages_in_timerange(potential_start_time, message_time) "message_count": self._count_messages_in_timerange(potential_start_time, message_time),
} }
segments.append(new_segment) segments.append(new_segment)
logger.info(f"[{self.stream_name}] 为用户 {person_id} 创建新消息段: 时间范围 {time.strftime('%H:%M:%S', time.localtime(potential_start_time))} - {time.strftime('%H:%M:%S', time.localtime(message_time))}, 消息数: {new_segment['message_count']}") logger.info(
f"[{self.stream_name}] 为用户 {person_id} 创建新消息段: 时间范围 {time.strftime('%H:%M:%S', time.localtime(potential_start_time))} - {time.strftime('%H:%M:%S', time.localtime(message_time))}, 消息数: {new_segment['message_count']}"
)
self._save_cache() self._save_cache()
return return
@@ -203,7 +212,7 @@ class NormalChat:
) )
if after_messages and len(after_messages) >= 5: if after_messages and len(after_messages) >= 5:
# 如果有足够的后续消息使用第5条消息的时间作为结束时间 # 如果有足够的后续消息使用第5条消息的时间作为结束时间
last_segment["end_time"] = after_messages[4]['time'] last_segment["end_time"] = after_messages[4]["time"]
else: else:
# 如果没有足够的后续消息,保持原有的结束时间 # 如果没有足够的后续消息,保持原有的结束时间
pass pass
@@ -218,7 +227,7 @@ class NormalChat:
"start_time": potential_start_time, "start_time": potential_start_time,
"end_time": message_time, "end_time": message_time,
"last_msg_time": message_time, "last_msg_time": message_time,
"message_count": self._count_messages_in_timerange(potential_start_time, message_time) "message_count": self._count_messages_in_timerange(potential_start_time, message_time),
} }
segments.append(new_segment) segments.append(new_segment)
logger.info(f"[{self.stream_name}] 为用户 {person_id} 创建新消息段超过10条消息间隔: {new_segment}") logger.info(f"[{self.stream_name}] 为用户 {person_id} 创建新消息段超过10条消息间隔: {new_segment}")
@@ -267,7 +276,7 @@ class NormalChat:
"users_cleaned": 0, "users_cleaned": 0,
"segments_removed": 0, "segments_removed": 0,
"total_segments_before": 0, "total_segments_before": 0,
"total_segments_after": 0 "total_segments_after": 0,
} }
max_age_seconds = SEGMENT_CLEANUP_CONFIG["max_segment_age_days"] * 24 * 3600 max_age_seconds = SEGMENT_CLEANUP_CONFIG["max_segment_age_days"] * 24 * 3600
@@ -287,7 +296,9 @@ class NormalChat:
segments_after_age_cleanup.append(segment) segments_after_age_cleanup.append(segment)
else: else:
cleanup_stats["segments_removed"] += 1 cleanup_stats["segments_removed"] += 1
logger.debug(f"[{self.stream_name}] 移除用户 {person_id} 的过期消息段: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['start_time']))} - {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['end_time']))}") logger.debug(
f"[{self.stream_name}] 移除用户 {person_id} 的过期消息段: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['start_time']))} - {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['end_time']))}"
)
# 2. 按数量清理:如果消息段数量仍然过多,保留最新的 # 2. 按数量清理:如果消息段数量仍然过多,保留最新的
if len(segments_after_age_cleanup) > max_segments_per_user: if len(segments_after_age_cleanup) > max_segments_per_user:
@@ -296,7 +307,9 @@ class NormalChat:
segments_removed_count = len(segments_after_age_cleanup) - max_segments_per_user segments_removed_count = len(segments_after_age_cleanup) - max_segments_per_user
cleanup_stats["segments_removed"] += segments_removed_count cleanup_stats["segments_removed"] += segments_removed_count
segments_after_age_cleanup = segments_after_age_cleanup[:max_segments_per_user] segments_after_age_cleanup = segments_after_age_cleanup[:max_segments_per_user]
logger.debug(f"[{self.stream_name}] 用户 {person_id} 消息段数量过多,移除 {segments_removed_count} 个最老的消息段") logger.debug(
f"[{self.stream_name}] 用户 {person_id} 消息段数量过多,移除 {segments_removed_count} 个最老的消息段"
)
# 使用清理后的消息段 # 使用清理后的消息段
@@ -322,8 +335,12 @@ class NormalChat:
# 保存缓存 # 保存缓存
if cleanup_stats["segments_removed"] > 0 or len(users_to_remove) > 0: if cleanup_stats["segments_removed"] > 0 or len(users_to_remove) > 0:
self._save_cache() self._save_cache()
logger.info(f"[{self.stream_name}] 清理完成 - 影响用户: {cleanup_stats['users_cleaned']}, 移除消息段: {cleanup_stats['segments_removed']}, 移除用户: {len(users_to_remove)}") logger.info(
logger.info(f"[{self.stream_name}] 消息段统计 - 清理前: {cleanup_stats['total_segments_before']}, 清理后: {cleanup_stats['total_segments_after']}") f"[{self.stream_name}] 清理完成 - 影响用户: {cleanup_stats['users_cleaned']}, 移除消息段: {cleanup_stats['segments_removed']}, 移除用户: {len(users_to_remove)}"
)
logger.info(
f"[{self.stream_name}] 消息段统计 - 清理前: {cleanup_stats['total_segments_before']}, 清理后: {cleanup_stats['total_segments_after']}"
)
else: else:
logger.debug(f"[{self.stream_name}] 清理完成 - 无需清理任何内容") logger.debug(f"[{self.stream_name}] 清理完成 - 无需清理任何内容")
@@ -335,10 +352,16 @@ class NormalChat:
return f"[{self.stream_name}] 关系缓存为空" return f"[{self.stream_name}] 关系缓存为空"
status_lines = [f"[{self.stream_name}] 关系缓存状态:"] status_lines = [f"[{self.stream_name}] 关系缓存状态:"]
status_lines.append(f"最后处理消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '未设置'}") status_lines.append(
status_lines.append(f"最后清理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_cleanup_time)) if self.last_cleanup_time > 0 else '执行'}") f"最后处理消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_processed_message_time)) if self.last_processed_message_time > 0 else '设置'}"
)
status_lines.append(
f"最后清理时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.last_cleanup_time)) if self.last_cleanup_time > 0 else '未执行'}"
)
status_lines.append(f"总用户数:{len(self.person_engaged_cache)}") status_lines.append(f"总用户数:{len(self.person_engaged_cache)}")
status_lines.append(f"清理配置:{'启用' if SEGMENT_CLEANUP_CONFIG['enable_cleanup'] else '禁用'} (最大保存{SEGMENT_CLEANUP_CONFIG['max_segment_age_days']}天, 每用户最多{SEGMENT_CLEANUP_CONFIG['max_segments_per_user']}段)") status_lines.append(
f"清理配置:{'启用' if SEGMENT_CLEANUP_CONFIG['enable_cleanup'] else '禁用'} (最大保存{SEGMENT_CLEANUP_CONFIG['max_segment_age_days']}天, 每用户最多{SEGMENT_CLEANUP_CONFIG['max_segments_per_user']}段)"
)
status_lines.append("") status_lines.append("")
for person_id, segments in self.person_engaged_cache.items(): for person_id, segments in self.person_engaged_cache.items():
@@ -348,17 +371,19 @@ class NormalChat:
status_lines.append(f" 消息段数:{len(segments)}") status_lines.append(f" 消息段数:{len(segments)}")
for i, segment in enumerate(segments): for i, segment in enumerate(segments):
start_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['start_time'])) start_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(segment["start_time"]))
end_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['end_time'])) end_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(segment["end_time"]))
last_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(segment['last_msg_time'])) last_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(segment["last_msg_time"]))
status_lines.append(f"{i+1}: {start_str} -> {end_str} (最后消息: {last_str}, 消息数: {segment['message_count']})") status_lines.append(
f"{i + 1}: {start_str} -> {end_str} (最后消息: {last_str}, 消息数: {segment['message_count']})"
)
status_lines.append("") status_lines.append("")
return "\n".join(status_lines) return "\n".join(status_lines)
def _update_user_message_segments(self, message: MessageRecv): def _update_user_message_segments(self, message: MessageRecv):
"""更新用户消息段信息""" """更新用户消息段信息"""
current_time = time.time() time.time()
user_id = message.message_info.user_info.user_id user_id = message.message_info.user_info.user_id
platform = message.message_info.platform platform = message.message_info.platform
msg_time = message.message_info.time msg_time = message.message_info.time
@@ -376,7 +401,9 @@ class NormalChat:
# 更新最后处理时间 # 更新最后处理时间
self.last_processed_message_time = max(self.last_processed_message_time, msg_time) self.last_processed_message_time = max(self.last_processed_message_time, msg_time)
logger.debug(f"[{self.stream_name}] 更新用户 {person_id} 的消息段,消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(msg_time))}") logger.debug(
f"[{self.stream_name}] 更新用户 {person_id} 的消息段,消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(msg_time))}"
)
# 改为实例方法 # 改为实例方法
async def _create_thinking_message(self, message: MessageRecv, timestamp: Optional[float] = None) -> str: async def _create_thinking_message(self, message: MessageRecv, timestamp: Optional[float] = None) -> str:
@@ -1072,8 +1099,6 @@ class NormalChat:
"""获取动作管理器实例""" """获取动作管理器实例"""
return self.action_manager return self.action_manager
async def _check_relation_building_conditions(self): async def _check_relation_building_conditions(self):
"""检查person_engaged_cache中是否有满足关系构建条件的用户""" """检查person_engaged_cache中是否有满足关系构建条件的用户"""
users_to_build_relationship = [] users_to_build_relationship = []
@@ -1095,9 +1120,7 @@ class NormalChat:
for person_id in users_to_build_relationship: for person_id in users_to_build_relationship:
segments = self.person_engaged_cache[person_id] segments = self.person_engaged_cache[person_id]
# 异步执行关系构建 # 异步执行关系构建
asyncio.create_task( asyncio.create_task(self._build_relation_for_person_segments(person_id, segments))
self._build_relation_for_person_segments(person_id, segments)
)
# 移除已处理的用户缓存 # 移除已处理的用户缓存
del self.person_engaged_cache[person_id] del self.person_engaged_cache[person_id]
self._save_cache() self._save_cache()
@@ -1112,12 +1135,14 @@ class NormalChat:
for i, segment in enumerate(segments): for i, segment in enumerate(segments):
start_time = segment["start_time"] start_time = segment["start_time"]
end_time = segment["end_time"] end_time = segment["end_time"]
message_count = segment["message_count"] segment["message_count"]
start_date = time.strftime('%Y-%m-%d %H:%M', time.localtime(start_time)) start_date = time.strftime("%Y-%m-%d %H:%M", time.localtime(start_time))
# 获取该段的消息(包含边界) # 获取该段的消息(包含边界)
segment_messages = get_raw_msg_by_timestamp_with_chat_inclusive(self.stream_id, start_time, end_time) segment_messages = get_raw_msg_by_timestamp_with_chat_inclusive(self.stream_id, start_time, end_time)
logger.info(f"[{self.stream_name}] 消息段 {i+1}: {start_date} - {time.strftime('%Y-%m-%d %H:%M', time.localtime(end_time))}, 消息数: {len(segment_messages)}") logger.info(
f"[{self.stream_name}] 消息段 {i + 1}: {start_date} - {time.strftime('%Y-%m-%d %H:%M', time.localtime(end_time))}, 消息数: {len(segment_messages)}"
)
if segment_messages: if segment_messages:
# 如果不是第一个消息段,在消息列表前添加间隔标识 # 如果不是第一个消息段,在消息列表前添加间隔标识
@@ -1132,7 +1157,7 @@ class NormalChat:
"display_message": f"...(中间省略一些消息){start_date} 之后的消息如下...", "display_message": f"...(中间省略一些消息){start_date} 之后的消息如下...",
"is_action_record": True, "is_action_record": True,
"chat_info_platform": segment_messages[0].get("chat_info_platform", ""), "chat_info_platform": segment_messages[0].get("chat_info_platform", ""),
"chat_id": self.stream_id "chat_id": self.stream_id,
} }
processed_messages.append(gap_message) processed_messages.append(gap_message)
@@ -1141,16 +1166,16 @@ class NormalChat:
if processed_messages: if processed_messages:
# 按时间排序所有消息(包括间隔标识) # 按时间排序所有消息(包括间隔标识)
processed_messages.sort(key=lambda x: x['time']) processed_messages.sort(key=lambda x: x["time"])
logger.info(f"[{self.stream_name}] 为 {person_id} 获取到总共 {len(processed_messages)} 条消息(包含间隔标识)用于印象更新") logger.info(
f"[{self.stream_name}] 为 {person_id} 获取到总共 {len(processed_messages)} 条消息(包含间隔标识)用于印象更新"
)
relationship_manager = get_relationship_manager() relationship_manager = get_relationship_manager()
# 调用原有的更新方法 # 调用原有的更新方法
await relationship_manager.update_person_impression( await relationship_manager.update_person_impression(
person_id=person_id, person_id=person_id, timestamp=time.time(), bot_engaged_messages=processed_messages
timestamp=time.time(),
bot_engaged_messages=processed_messages
) )
logger.info(f"[{self.stream_name}] 用户 {person_id} 关系构建完成") logger.info(f"[{self.stream_name}] 用户 {person_id} 关系构建完成")

View File

@@ -120,7 +120,7 @@ class BasePlugin(ABC):
if isinstance(value, str): if isinstance(value, str):
toml_str += f'{field_name} = "{value}"\n' toml_str += f'{field_name} = "{value}"\n'
elif isinstance(value, bool): elif isinstance(value, bool):
toml_str += f'{field_name} = {str(value).lower()}\n' toml_str += f"{field_name} = {str(value).lower()}\n"
else: else:
toml_str += f"{field_name} = {value}\n" toml_str += f"{field_name} = {value}\n"

View File

@@ -427,7 +427,9 @@ class CoreActionsPlugin(BasePlugin):
"name": ConfigField(type=str, default="core_actions", description="插件名称", required=True), "name": ConfigField(type=str, default="core_actions", description="插件名称", required=True),
"version": ConfigField(type=str, default="1.0.0", description="插件版本号"), "version": ConfigField(type=str, default="1.0.0", description="插件版本号"),
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
"description": ConfigField(type=str, default="系统核心动作插件,提供基础聊天交互功能", description="插件描述", required=True) "description": ConfigField(
type=str, default="系统核心动作插件,提供基础聊天交互功能", description="插件描述", required=True
),
}, },
"components": { "components": {
"enable_reply": ConfigField(type=bool, default=True, description="是否启用'回复'动作"), "enable_reply": ConfigField(type=bool, default=True, description="是否启用'回复'动作"),
@@ -436,22 +438,21 @@ class CoreActionsPlugin(BasePlugin):
"enable_change_to_focus": ConfigField(type=bool, default=True, description="是否启用'切换到专注模式'动作"), "enable_change_to_focus": ConfigField(type=bool, default=True, description="是否启用'切换到专注模式'动作"),
"enable_exit_focus": ConfigField(type=bool, default=True, description="是否启用'退出专注模式'动作"), "enable_exit_focus": ConfigField(type=bool, default=True, description="是否启用'退出专注模式'动作"),
"enable_ping_command": ConfigField(type=bool, default=True, description="是否启用'/ping'测试命令"), "enable_ping_command": ConfigField(type=bool, default=True, description="是否启用'/ping'测试命令"),
"enable_log_command": ConfigField(type=bool, default=True, description="是否启用'/log'日志命令") "enable_log_command": ConfigField(type=bool, default=True, description="是否启用'/log'日志命令"),
}, },
"no_reply": { "no_reply": {
"waiting_timeout": ConfigField(type=int, default=1200, description="连续不回复时,最长的等待超时时间(秒)"), "waiting_timeout": ConfigField(
type=int, default=1200, description="连续不回复时,最长的等待超时时间(秒)"
),
"stage_1_wait": ConfigField(type=int, default=10, description="第1次连续不回复的等待时间"), "stage_1_wait": ConfigField(type=int, default=10, description="第1次连续不回复的等待时间"),
"stage_2_wait": ConfigField(type=int, default=60, description="第2次连续不回复的等待时间"), "stage_2_wait": ConfigField(type=int, default=60, description="第2次连续不回复的等待时间"),
"stage_3_wait": ConfigField(type=int, default=600, description="第3次连续不回复的等待时间"), "stage_3_wait": ConfigField(type=int, default=600, description="第3次连续不回复的等待时间"),
}, },
"emoji": { "emoji": {
"random_probability": ConfigField( "random_probability": ConfigField(
type=float, type=float, default=0.1, description="Normal模式下随机发送表情的概率0.0到1.0", example=0.15
default=0.1,
description="Normal模式下随机发送表情的概率0.0到1.0",
example=0.15
) )
} },
} }
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
@@ -482,9 +483,13 @@ class CoreActionsPlugin(BasePlugin):
if self.get_config("components.enable_change_to_focus", True): if self.get_config("components.enable_change_to_focus", True):
components.append((ChangeToFocusChatAction.get_action_info(), ChangeToFocusChatAction)) components.append((ChangeToFocusChatAction.get_action_info(), ChangeToFocusChatAction))
if self.get_config("components.enable_ping_command", True): if self.get_config("components.enable_ping_command", True):
components.append((PingCommand.get_command_info(name="ping", description="测试机器人响应,拦截后续处理"), PingCommand)) components.append(
(PingCommand.get_command_info(name="ping", description="测试机器人响应,拦截后续处理"), PingCommand)
)
if self.get_config("components.enable_log_command", True): if self.get_config("components.enable_log_command", True):
components.append((LogCommand.get_command_info(name="log", description="记录消息到日志,不拦截后续处理"), LogCommand)) components.append(
(LogCommand.get_command_info(name="log", description="记录消息到日志,不拦截后续处理"), LogCommand)
)
return components return components

View File

@@ -412,7 +412,7 @@ class DoubaoImagePlugin(BasePlugin):
"api": "API相关配置包含火山引擎API的访问信息", "api": "API相关配置包含火山引擎API的访问信息",
"generation": "图片生成参数配置,控制生成图片的各种参数", "generation": "图片生成参数配置,控制生成图片的各种参数",
"cache": "结果缓存配置", "cache": "结果缓存配置",
"components": "组件启用配置" "components": "组件启用配置",
} }
# 配置Schema定义 # 配置Schema定义
@@ -422,56 +422,47 @@ class DoubaoImagePlugin(BasePlugin):
"version": ConfigField(type=str, default="2.0.0", description="插件版本号"), "version": ConfigField(type=str, default="2.0.0", description="插件版本号"),
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
"description": ConfigField( "description": ConfigField(
type=str, type=str, default="基于火山引擎豆包模型的AI图片生成插件", description="插件描述", required=True
default="基于火山引擎豆包模型的AI图片生成插件", ),
description="插件描述",
required=True
)
}, },
"api": { "api": {
"base_url": ConfigField( "base_url": ConfigField(
type=str, type=str,
default="https://ark.cn-beijing.volces.com/api/v3", default="https://ark.cn-beijing.volces.com/api/v3",
description="API基础URL", description="API基础URL",
example="https://api.example.com/v1" example="https://api.example.com/v1",
), ),
"volcano_generate_api_key": ConfigField( "volcano_generate_api_key": ConfigField(
type=str, type=str, default="YOUR_DOUBAO_API_KEY_HERE", description="火山引擎豆包API密钥", required=True
default="YOUR_DOUBAO_API_KEY_HERE", ),
description="火山引擎豆包API密钥",
required=True
)
}, },
"generation": { "generation": {
"default_model": ConfigField( "default_model": ConfigField(
type=str, type=str,
default="doubao-seedream-3-0-t2i-250415", default="doubao-seedream-3-0-t2i-250415",
description="默认使用的文生图模型", description="默认使用的文生图模型",
choices=["doubao-seedream-3-0-t2i-250415", "doubao-seedream-2-0-t2i"] choices=["doubao-seedream-3-0-t2i-250415", "doubao-seedream-2-0-t2i"],
), ),
"default_size": ConfigField( "default_size": ConfigField(
type=str, type=str,
default="1024x1024", default="1024x1024",
description="默认图片尺寸", description="默认图片尺寸",
example="1024x1024", example="1024x1024",
choices=["1024x1024", "1024x1280", "1280x1024", "1024x1536", "1536x1024"] choices=["1024x1024", "1024x1280", "1280x1024", "1024x1536", "1536x1024"],
), ),
"default_watermark": ConfigField(type=bool, default=True, description="是否默认添加水印"), "default_watermark": ConfigField(type=bool, default=True, description="是否默认添加水印"),
"default_guidance_scale": ConfigField( "default_guidance_scale": ConfigField(
type=float, type=float, default=2.5, description="模型指导强度,影响图片与提示的关联性", example="2.0"
default=2.5,
description="模型指导强度,影响图片与提示的关联性",
example="2.0"
), ),
"default_seed": ConfigField(type=int, default=42, description="随机种子,用于复现图片") "default_seed": ConfigField(type=int, default=42, description="随机种子,用于复现图片"),
}, },
"cache": { "cache": {
"enabled": ConfigField(type=bool, default=True, description="是否启用请求缓存"), "enabled": ConfigField(type=bool, default=True, description="是否启用请求缓存"),
"max_size": ConfigField(type=int, default=10, description="最大缓存数量") "max_size": ConfigField(type=int, default=10, description="最大缓存数量"),
}, },
"components": { "components": {
"enable_image_generation": ConfigField(type=bool, default=True, description="是否启用图片生成Action") "enable_image_generation": ConfigField(type=bool, default=True, description="是否启用图片生成Action")
} },
} }
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:

View File

@@ -164,7 +164,7 @@ class MuteAction(BaseAction):
success = await self.send_command( success = await self.send_command(
command_name="GROUP_BAN", command_name="GROUP_BAN",
args={"qq_id": str(user_id), "duration": str(duration_int)}, args={"qq_id": str(user_id), "duration": str(duration_int)},
display_message=f"发送禁言命令", display_message="发送禁言命令",
) )
if success: if success:
@@ -180,8 +180,8 @@ class MuteAction(BaseAction):
"user_id": user_id, "user_id": user_id,
"duration": duration_int, "duration": duration_int,
"duration_str": time_str, "duration_str": time_str,
"reason": reason "reason": reason,
} },
) )
return True, f"成功禁言 {target},时长 {time_str}" return True, f"成功禁言 {target},时长 {time_str}"
else: else:
@@ -389,7 +389,7 @@ class MutePlugin(BasePlugin):
"mute": "核心禁言功能配置", "mute": "核心禁言功能配置",
"smart_mute": "智能禁言Action的专属配置", "smart_mute": "智能禁言Action的专属配置",
"mute_command": "禁言命令Command的专属配置", "mute_command": "禁言命令Command的专属配置",
"logging": "日志记录相关配置" "logging": "日志记录相关配置",
} }
# 配置Schema定义 # 配置Schema定义
@@ -398,17 +398,21 @@ class MutePlugin(BasePlugin):
"name": ConfigField(type=str, default="mute_plugin", description="插件名称", required=True), "name": ConfigField(type=str, default="mute_plugin", description="插件名称", required=True),
"version": ConfigField(type=str, default="2.0.0", description="插件版本号"), "version": ConfigField(type=str, default="2.0.0", description="插件版本号"),
"enabled": ConfigField(type=bool, default=False, description="是否启用插件"), "enabled": ConfigField(type=bool, default=False, description="是否启用插件"),
"description": ConfigField(type=str, default="群聊禁言管理插件,提供智能禁言功能", description="插件描述", required=True) "description": ConfigField(
type=str, default="群聊禁言管理插件,提供智能禁言功能", description="插件描述", required=True
),
}, },
"components": { "components": {
"enable_smart_mute": ConfigField(type=bool, default=True, description="是否启用智能禁言Action"), "enable_smart_mute": ConfigField(type=bool, default=True, description="是否启用智能禁言Action"),
"enable_mute_command": ConfigField(type=bool, default=False, description="是否启用禁言命令Command") "enable_mute_command": ConfigField(type=bool, default=False, description="是否启用禁言命令Command"),
}, },
"mute": { "mute": {
"min_duration": ConfigField(type=int, default=60, description="最短禁言时长(秒)"), "min_duration": ConfigField(type=int, default=60, description="最短禁言时长(秒)"),
"max_duration": ConfigField(type=int, default=2592000, description="最长禁言时长默认30天"), "max_duration": ConfigField(type=int, default=2592000, description="最长禁言时长默认30天"),
"default_duration": ConfigField(type=int, default=300, description="默认禁言时长默认5分钟"), "default_duration": ConfigField(type=int, default=300, description="默认禁言时长默认5分钟"),
"enable_duration_formatting": ConfigField(type=bool, default=True, description="是否启用人性化的时长显示(如 '5分钟' 而非 '300秒'"), "enable_duration_formatting": ConfigField(
type=bool, default=True, description="是否启用人性化的时长显示(如 '5分钟' 而非 '300秒'"
),
"log_mute_history": ConfigField(type=bool, default=True, description="是否记录禁言历史(未来功能)"), "log_mute_history": ConfigField(type=bool, default=True, description="是否记录禁言历史(未来功能)"),
"templates": ConfigField( "templates": ConfigField(
type=list, type=list,
@@ -418,9 +422,9 @@ class MutePlugin(BasePlugin):
"明白了,禁言 {target} {duration},原因是{reason}", "明白了,禁言 {target} {duration},原因是{reason}",
"哇哈哈哈哈哈,已禁言 {target} {duration},理由:{reason}", "哇哈哈哈哈哈,已禁言 {target} {duration},理由:{reason}",
"哎呦我去,对 {target} 执行禁言 {duration},因为{reason}", "哎呦我去,对 {target} 执行禁言 {duration},因为{reason}",
"{target},你完蛋了,我要禁言你 {duration} 秒,原因:{reason}" "{target},你完蛋了,我要禁言你 {duration} 秒,原因:{reason}",
], ],
description="成功禁言后发送的随机消息模板" description="成功禁言后发送的随机消息模板",
), ),
"error_messages": ConfigField( "error_messages": ConfigField(
type=list, type=list,
@@ -430,26 +434,30 @@ class MutePlugin(BasePlugin):
"禁言时长必须是正数哦~", "禁言时长必须是正数哦~",
"禁言时长必须是数字哦~", "禁言时长必须是数字哦~",
"找不到 {target} 这个人呢~", "找不到 {target} 这个人呢~",
"查找用户信息时出现问题~" "查找用户信息时出现问题~",
], ],
description="执行禁言过程中发生错误时发送的随机消息模板" description="执行禁言过程中发生错误时发送的随机消息模板",
) ),
}, },
"smart_mute": { "smart_mute": {
"strict_mode": ConfigField(type=bool, default=True, description="LLM判定的严格模式"), "strict_mode": ConfigField(type=bool, default=True, description="LLM判定的严格模式"),
"keyword_sensitivity": ConfigField(type=str, default="normal", description="关键词激活的敏感度", choices=["low", "normal", "high"]), "keyword_sensitivity": ConfigField(
"allow_parallel": ConfigField(type=bool, default=False, description="是否允许并行执行(暂未启用)") type=str, default="normal", description="关键词激活的敏感度", choices=["low", "normal", "high"]
),
"allow_parallel": ConfigField(type=bool, default=False, description="是否允许并行执行(暂未启用)"),
}, },
"mute_command": { "mute_command": {
"max_batch_size": ConfigField(type=int, default=5, description="最大批量禁言数量(未来功能)"), "max_batch_size": ConfigField(type=int, default=5, description="最大批量禁言数量(未来功能)"),
"cooldown_seconds": ConfigField(type=int, default=3, description="命令冷却时间(秒)") "cooldown_seconds": ConfigField(type=int, default=3, description="命令冷却时间(秒)"),
}, },
"logging": { "logging": {
"level": ConfigField(type=str, default="INFO", description="日志记录级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]), "level": ConfigField(
type=str, default="INFO", description="日志记录级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]
),
"prefix": ConfigField(type=str, default="[MutePlugin]", description="日志记录前缀"), "prefix": ConfigField(type=str, default="[MutePlugin]", description="日志记录前缀"),
"include_user_info": ConfigField(type=bool, default=True, description="日志中是否包含用户信息"), "include_user_info": ConfigField(type=bool, default=True, description="日志中是否包含用户信息"),
"include_duration_info": ConfigField(type=bool, default=True, description="日志中是否包含禁言时长信息") "include_duration_info": ConfigField(type=bool, default=True, description="日志中是否包含禁言时长信息"),
} },
} }
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:

View File

@@ -112,7 +112,7 @@ class TTSPlugin(BasePlugin):
config_section_descriptions = { config_section_descriptions = {
"plugin": "插件基本信息配置", "plugin": "插件基本信息配置",
"components": "组件启用控制", "components": "组件启用控制",
"logging": "日志记录相关配置" "logging": "日志记录相关配置",
} }
# 配置Schema定义 # 配置Schema定义
@@ -121,15 +121,15 @@ class TTSPlugin(BasePlugin):
"name": ConfigField(type=str, default="tts_plugin", description="插件名称", required=True), "name": ConfigField(type=str, default="tts_plugin", description="插件名称", required=True),
"version": ConfigField(type=str, default="0.1.0", description="插件版本号"), "version": ConfigField(type=str, default="0.1.0", description="插件版本号"),
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
"description": ConfigField(type=str, default="文字转语音插件", description="插件描述", required=True) "description": ConfigField(type=str, default="文字转语音插件", description="插件描述", required=True),
},
"components": {
"enable_tts": ConfigField(type=bool, default=True, description="是否启用TTS Action")
}, },
"components": {"enable_tts": ConfigField(type=bool, default=True, description="是否启用TTS Action")},
"logging": { "logging": {
"level": ConfigField(type=str, default="INFO", description="日志记录级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]), "level": ConfigField(
"prefix": ConfigField(type=str, default="[TTS]", description="日志记录前缀") type=str, default="INFO", description="日志记录级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]
} ),
"prefix": ConfigField(type=str, default="[TTS]", description="日志记录前缀"),
},
} }
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:

View File

@@ -128,25 +128,22 @@ class VTBPlugin(BasePlugin):
"name": ConfigField(type=str, default="vtb_plugin", description="插件名称", required=True), "name": ConfigField(type=str, default="vtb_plugin", description="插件名称", required=True),
"version": ConfigField(type=str, default="0.1.0", description="插件版本号"), "version": ConfigField(type=str, default="0.1.0", description="插件版本号"),
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
"description": ConfigField(type=str, default="虚拟主播情感表达插件", description="插件描述", required=True) "description": ConfigField(type=str, default="虚拟主播情感表达插件", description="插件描述", required=True),
},
"components": {
"enable_vtb": ConfigField(type=bool, default=True, description="是否启用VTB动作")
}, },
"components": {"enable_vtb": ConfigField(type=bool, default=True, description="是否启用VTB动作")},
"vtb_action": { "vtb_action": {
"random_activation_probability": ConfigField( "random_activation_probability": ConfigField(
type=float, type=float, default=0.08, description="Normal模式下随机触发VTB动作的概率0.0到1.0", example=0.1
default=0.08,
description="Normal模式下随机触发VTB动作的概率0.0到1.0",
example=0.1
), ),
"max_text_length": ConfigField(type=int, default=100, description="用于VTB动作的情感描述文本的最大长度"), "max_text_length": ConfigField(type=int, default=100, description="用于VTB动作的情感描述文本的最大长度"),
"default_emotion": ConfigField(type=str, default="平静", description="当没有有效输入时,默认表达的情感") "default_emotion": ConfigField(type=str, default="平静", description="当没有有效输入时,默认表达的情感"),
}, },
"logging": { "logging": {
"level": ConfigField(type=str, default="INFO", description="日志级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]), "level": ConfigField(
"prefix": ConfigField(type=str, default="[VTB]", description="日志记录前缀") type=str, default="INFO", description="日志级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]
} ),
"prefix": ConfigField(type=str, default="[VTB]", description="日志记录前缀"),
},
} }
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: