diff --git a/plugins/example_plugin/plugin.py b/plugins/example_plugin/plugin.py index bd46e3121..18c1156c9 100644 --- a/plugins/example_plugin/plugin.py +++ b/plugins/example_plugin/plugin.py @@ -668,7 +668,9 @@ class ExampleComprehensivePlugin(BasePlugin): "name": ConfigField(type=str, default="example_plugin", description="插件名称", required=True), "version": ConfigField(type=str, default="2.0.0", 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_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_echo": ConfigField(type=bool, default=True, description="是否启用'/echo'命令"), "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": { - "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_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成个性化问候语") + "enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成个性化问候语"), }, "helpful": { "enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成帮助内容"), "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": { "show_extended_help": ConfigField(type=bool, default=True, description="是否显示扩展帮助信息"), "include_action_info": ConfigField(type=bool, default=True, description="帮助信息中是否包含Action的信息"), "include_config_info": ConfigField(type=bool, default=True, description="帮助信息中是否包含配置相关信息"), "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": { "max_message_length": ConfigField(type=int, default=500, 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": { "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": { "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": { "show_detailed_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": { - "level": ConfigField(type=str, default="INFO", description="日志级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]), - "prefix": ConfigField(type=str, default="[ExampleComprehensive]", description="日志前缀") - } + "level": ConfigField( + 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]]: diff --git a/scripts/log_viewer.py b/scripts/log_viewer.py index 30f9cebf0..248919fa8 100644 --- a/scripts/log_viewer.py +++ b/scripts/log_viewer.py @@ -1183,4 +1183,3 @@ def main(): if __name__ == "__main__": main() - diff --git a/scripts/log_viewer_optimized.py b/scripts/log_viewer_optimized.py index 115b49fab..bb1954515 100644 --- a/scripts/log_viewer_optimized.py +++ b/scripts/log_viewer_optimized.py @@ -1,62 +1,59 @@ import tkinter as tk -from tkinter import ttk, colorchooser, messagebox, filedialog +from tkinter import ttk, messagebox, filedialog import json from pathlib import Path import threading -import queue -import time import toml from datetime import datetime -import bisect from collections import defaultdict import os class LogIndex: """日志索引,用于快速检索和过滤""" - + def __init__(self): self.entries = [] # 所有日志条目 self.module_index = defaultdict(list) # 按模块索引 self.level_index = defaultdict(list) # 按级别索引 self.filtered_indices = [] # 当前过滤结果的索引 self.total_entries = 0 - + def add_entry(self, index, entry): """添加日志条目到索引""" if index >= len(self.entries): self.entries.extend([None] * (index - len(self.entries) + 1)) - + self.entries[index] = entry self.total_entries = max(self.total_entries, index + 1) - + # 更新各种索引 logger_name = entry.get("logger_name", "") level = entry.get("level", "") - + self.module_index[logger_name].append(index) self.level_index[level].append(index) - + def filter_entries(self, modules=None, level=None, search_text=None): """根据条件过滤日志条目""" if not modules and not level and not search_text: self.filtered_indices = list(range(self.total_entries)) return self.filtered_indices - + candidate_indices = set(range(self.total_entries)) - + # 模块过滤 if modules and "全部" not in modules: module_indices = set() for module in modules: module_indices.update(self.module_index.get(module, [])) candidate_indices &= module_indices - + # 级别过滤 if level and level != "全部": level_indices = set(self.level_index.get(level, [])) candidate_indices &= level_indices - + # 文本搜索过滤 if search_text: search_text = search_text.lower() @@ -68,14 +65,14 @@ class LogIndex: if search_text in text_content: text_indices.add(i) candidate_indices &= text_indices - + self.filtered_indices = sorted(list(candidate_indices)) return self.filtered_indices - + def get_filtered_count(self): """获取过滤后的条目数量""" return len(self.filtered_indices) - + def get_entry_at_filtered_position(self, position): """获取过滤结果中指定位置的条目""" if 0 <= position < len(self.filtered_indices): @@ -242,16 +239,16 @@ class LogFormatter: class VirtualLogDisplay: """虚拟滚动日志显示组件""" - + def __init__(self, parent, formatter): self.parent = parent self.formatter = formatter self.line_height = 20 # 每行高度(像素) self.visible_lines = 30 # 可见行数 - + # 创建主框架 self.main_frame = ttk.Frame(parent) - + # 创建文本框和滚动条 self.scrollbar = ttk.Scrollbar(self.main_frame) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) @@ -264,24 +261,24 @@ class VirtualLogDisplay: foreground="#ffffff", insertbackground="#ffffff", selectbackground="#404040", - font=("Consolas", 10) + font=("Consolas", 10), ) self.text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.scrollbar.config(command=self.text_widget.yview) - + # 配置文本标签样式 self.configure_text_tags() - + # 数据源 self.log_index = None self.current_page = 0 self.page_size = 500 # 每页显示条数 self.max_display_lines = 2000 # 最大显示行数 - + def pack(self, **kwargs): """包装pack方法""" self.main_frame.pack(**kwargs) - + def configure_text_tags(self): """配置文本标签样式""" # 基础标签 @@ -289,78 +286,84 @@ class VirtualLogDisplay: self.text_widget.tag_configure("level", foreground="#808080") self.text_widget.tag_configure("module", foreground="#808080") self.text_widget.tag_configure("message", foreground="#ffffff") - + # 日志级别颜色标签 for level, color in self.formatter.level_colors.items(): self.text_widget.tag_configure(f"level_{level}", foreground=color) - + # 模块颜色标签 for module, color in self.formatter.module_colors.items(): self.text_widget.tag_configure(f"module_{module}", foreground=color) - + def set_log_index(self, log_index): """设置日志索引数据源""" self.log_index = log_index self.current_page = 0 self.refresh_display() - + def refresh_display(self): """刷新显示""" if not self.log_index: self.text_widget.delete(1.0, tk.END) return - + # 清空显示 self.text_widget.delete(1.0, tk.END) - + # 批量加载和显示日志 total_count = self.log_index.get_filtered_count() if total_count == 0: self.text_widget.insert(tk.END, "没有符合条件的日志记录\n") return - + # 计算显示范围 start_index = 0 end_index = min(total_count, self.max_display_lines) - + # 批量处理和显示 batch_size = 100 for batch_start in range(start_index, end_index, batch_size): batch_end = min(batch_start + batch_size, end_index) self.display_batch(batch_start, batch_end) - + # 让UI有机会响应 self.parent.update_idletasks() - + # 滚动到底部(如果需要) self.text_widget.see(tk.END) - + def display_batch(self, start_index, end_index): """批量显示日志条目""" batch_text = [] batch_tags = [] - + for i in range(start_index, end_index): log_entry = self.log_index.get_entry_at_filtered_position(i) if log_entry: parts, tags = self.formatter.format_log_entry(log_entry) - + # 合并部分为单行文本 line_text = " ".join(parts) + "\n" batch_text.append(line_text) - + # 记录标签信息(简化处理) if tags and self.formatter.enable_level_colors: level = log_entry.get("level", "info") - batch_tags.append(("line", len("".join(batch_text)) - len(line_text), - len("".join(batch_text)) - 1, f"level_{level}")) - + batch_tags.append( + ( + "line", + len("".join(batch_text)) - len(line_text), + len("".join(batch_text)) - 1, + f"level_{level}", + ) + ) + # 一次性插入所有文本 if batch_text: start_pos = self.text_widget.index(tk.END) all_text = "".join(batch_text) self.text_widget.insert(tk.END, all_text) - + # 应用标签(可选,为了性能可以考虑简化) for tag_info in batch_tags: try: @@ -372,35 +375,35 @@ class VirtualLogDisplay: class AsyncLogLoader: """异步日志加载器""" - + def __init__(self, callback): self.callback = callback self.loading = False self.should_stop = False - + def load_file_async(self, file_path, progress_callback=None): """异步加载日志文件""" if self.loading: return - + self.loading = True self.should_stop = False - + def load_worker(): try: log_index = LogIndex() - + if not os.path.exists(file_path): self.callback(log_index, "文件不存在") return - + file_size = os.path.getsize(file_path) processed_size = 0 - + with open(file_path, "r", encoding="utf-8") as f: line_count = 0 batch_size = 1000 # 批量处理 - + while not self.should_stop: lines = [] for _ in range(batch_size): @@ -408,11 +411,11 @@ class AsyncLogLoader: if not line: break lines.append(line) - processed_size += len(line.encode('utf-8')) - + processed_size += len(line.encode("utf-8")) + if not lines: break - + # 处理这批数据 for line in lines: try: @@ -421,24 +424,24 @@ class AsyncLogLoader: line_count += 1 except json.JSONDecodeError: continue - + # 更新进度 if progress_callback: progress = min(100, (processed_size / file_size) * 100) progress_callback(progress, line_count) - + if not self.should_stop: self.callback(log_index, None) - + except Exception as e: self.callback(None, str(e)) finally: self.loading = False - + thread = threading.Thread(target=load_worker) thread.daemon = True thread.start() - + def stop_loading(self): """停止加载""" self.should_stop = True @@ -462,7 +465,7 @@ class LogViewer: # 初始化异步加载器 self.async_loader = AsyncLogLoader(self.on_file_loaded) - + # 初始化日志索引 self.log_index = LogIndex() @@ -472,7 +475,7 @@ class LogViewer: # 创建控制面板 self.create_control_panel() - + # 创建虚拟滚动日志显示区域 self.log_display = VirtualLogDisplay(self.main_frame, self.formatter) self.log_display.pack(fill=tk.BOTH, expand=True) @@ -503,9 +506,9 @@ class LogViewer: self.default_config = { "log": {"date_style": "m-d H:i:s", "log_level_style": "lite", "color_text": "full"}, } - + self.log_config = self.default_config["log"].copy() - + config_path = Path("config/bot_config.toml") try: if config_path.exists(): @@ -576,20 +579,20 @@ class LogViewer: def on_file_loaded(self, log_index, error): """文件加载完成回调""" self.progress_bar.pack_forget() - + if error: self.status_var.set(f"加载失败: {error}") messagebox.showerror("错误", f"加载日志文件失败: {error}") return - + self.log_index = log_index self.status_var.set(f"已加载 {log_index.total_entries} 条日志") - + # 更新模块列表 self.modules = set(log_index.module_index.keys()) module_values = ["全部"] + sorted(list(self.modules)) self.module_combo["values"] = module_values - + # 应用过滤并显示 self.filter_logs() @@ -607,22 +610,19 @@ class LogViewer: if not self.current_log_file.exists(): self.status_var.set("文件不存在") return - + # 显示进度条 self.progress_bar.pack(side=tk.LEFT, padx=5, pady=2, before=self.status_label) self.progress_var.set(0) self.status_var.set("正在加载...") - + # 清空当前数据 self.log_index = LogIndex() self.modules.clear() self.selected_modules.clear() - + # 开始异步加载 - self.async_loader.load_file_async( - str(self.current_log_file), - self.on_loading_progress - ) + self.async_loader.load_file_async(str(self.current_log_file), self.on_loading_progress) def on_module_selected(self, event=None): """模块选择事件""" @@ -637,18 +637,18 @@ class LogViewer: """过滤日志""" if not self.log_index: return - + # 获取过滤条件 selected_modules = self.selected_modules if self.selected_modules else None level = self.level_var.get() if self.level_var.get() != "全部" else None search_text = self.search_var.get().strip() if self.search_var.get().strip() else None - + # 应用过滤 self.log_index.filter_entries(selected_modules, level, search_text) - + # 更新显示 self.log_display.set_log_index(self.log_index) - + # 更新状态 filtered_count = self.log_index.get_filtered_count() total_count = self.log_index.total_entries @@ -683,4 +683,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index b5a644db9..bf70a2e41 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -341,7 +341,12 @@ class HeartFChatting: }, "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.complete_cycle() @@ -420,7 +425,12 @@ class HeartFChatting: }, "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: self._current_cycle_detail.set_loop_info(error_loop_info) diff --git a/src/chat/focus_chat/info_processors/relationship_processor.py b/src/chat/focus_chat/info_processors/relationship_processor.py index 0ad919820..6e856708d 100644 --- a/src/chat/focus_chat/info_processors/relationship_processor.py +++ b/src/chat/focus_chat/info_processors/relationship_processor.py @@ -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.observation import Observation 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 import json 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 pickle @@ -106,18 +110,18 @@ class RelationshipProcessor(BaseProcessor): self.info_fetched_cache: Dict[ str, Dict[str, any] ] = {} # {person_id: {"info": str, "ttl": int, "start_time": float}} - + # 新的消息段缓存结构: # {person_id: [{"start_time": float, "end_time": float, "last_msg_time": float, "message_count": int}, ...]} self.person_engaged_cache: Dict[str, List[Dict[str, any]]] = {} - + # 持久化存储文件路径 self.cache_file_path = os.path.join("data", f"relationship_cache_{self.subheartflow_id}.pkl") - + # 最后处理的消息时间,避免重复处理相同消息 current_time = time.time() self.last_processed_message_time = current_time - + # 最后清理时间,用于定期清理老消息段 self.last_cleanup_time = 0.0 @@ -135,7 +139,7 @@ class RelationshipProcessor(BaseProcessor): name = get_chat_manager().get_stream_name(self.subheartflow_id) self.log_prefix = f"[{name}] " - + # 加载持久化的缓存 self._load_cache() @@ -143,19 +147,21 @@ class RelationshipProcessor(BaseProcessor): # 缓存管理模块 # 负责持久化存储、状态管理、缓存读写 # ================================ - + def _load_cache(self): """从文件加载持久化的缓存""" if os.path.exists(self.cache_file_path): try: - with open(self.cache_file_path, 'rb') as f: + with open(self.cache_file_path, "rb") as f: cache_data = pickle.load(f) - # 新格式:包含额外信息的缓存 - 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_cleanup_time = cache_data.get('last_cleanup_time', 0.0) + # 新格式:包含额外信息的缓存 + 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_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: logger.error(f"{self.log_prefix} 加载关系缓存失败: {e}") self.person_engaged_cache = {} @@ -168,63 +174,65 @@ class RelationshipProcessor(BaseProcessor): try: os.makedirs(os.path.dirname(self.cache_file_path), exist_ok=True) cache_data = { - 'person_engaged_cache': self.person_engaged_cache, - 'last_processed_message_time': self.last_processed_message_time, - 'last_cleanup_time': self.last_cleanup_time + "person_engaged_cache": self.person_engaged_cache, + "last_processed_message_time": self.last_processed_message_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) logger.debug(f"{self.log_prefix} 成功保存关系缓存") except Exception as e: logger.error(f"{self.log_prefix} 保存关系缓存失败: {e}") # ================================ - # 消息段管理模块 + # 消息段管理模块 # 负责跟踪用户消息活动、管理消息段、清理过期数据 # ================================ - + def _update_message_segments(self, person_id: str, message_time: float): """更新用户的消息段 - + Args: person_id: 用户ID message_time: 消息时间戳 """ if person_id not in self.person_engaged_cache: self.person_engaged_cache[person_id] = [] - + segments = self.person_engaged_cache[person_id] current_time = time.time() - + # 获取该消息前5条消息的时间作为潜在的开始时间 before_messages = get_raw_msg_before_timestamp_with_chat(self.subheartflow_id, message_time, limit=5) if before_messages: # 由于get_raw_msg_before_timestamp_with_chat返回按时间升序排序的消息,最后一个是最接近message_time的 # 我们需要第一个消息作为开始时间,但应该确保至少包含5条消息或该用户之前的消息 - potential_start_time = before_messages[0]['time'] + potential_start_time = before_messages[0]["time"] else: # 如果没有前面的消息,就从当前消息开始 potential_start_time = message_time - + # 如果没有现有消息段,创建新的 if not segments: new_segment = { "start_time": potential_start_time, "end_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) - 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() return - + # 获取最后一个消息段 last_segment = segments[-1] - + # 计算从最后一条消息到当前消息之间的消息数量(不包含边界) messages_between = self._count_messages_between(last_segment["last_msg_time"], message_time) - + if messages_between <= 10: # 在10条消息内,延伸当前消息段 last_segment["end_time"] = message_time @@ -242,82 +250,82 @@ class RelationshipProcessor(BaseProcessor): ) if after_messages and len(after_messages) >= 5: # 如果有足够的后续消息,使用第5条消息的时间作为结束时间 - last_segment["end_time"] = after_messages[4]['time'] + last_segment["end_time"] = after_messages[4]["time"] else: # 如果没有足够的后续消息,保持原有的结束时间 pass - + # 重新计算当前消息段的消息数量 last_segment["message_count"] = self._count_messages_in_timerange( last_segment["start_time"], last_segment["end_time"] ) - + # 创建新的消息段 new_segment = { "start_time": potential_start_time, "end_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) logger.info(f"{self.log_prefix} 为用户 {person_id} 创建新消息段(超过10条消息间隔): {new_segment}") - + self._save_cache() - + def _count_messages_in_timerange(self, start_time: float, end_time: float) -> int: """计算指定时间范围内的消息数量(包含边界)""" messages = get_raw_msg_by_timestamp_with_chat_inclusive(self.subheartflow_id, start_time, end_time) return len(messages) - + def _count_messages_between(self, start_time: float, end_time: float) -> int: """计算两个时间点之间的消息数量(不包含边界),用于间隔检查""" return num_new_messages_since(self.subheartflow_id, start_time, end_time) - + def _get_total_message_count(self, person_id: str) -> int: """获取用户所有消息段的总消息数量""" if person_id not in self.person_engaged_cache: return 0 - + total_count = 0 for segment in self.person_engaged_cache[person_id]: total_count += segment["message_count"] - + return total_count - + def _cleanup_old_segments(self) -> bool: """清理老旧的消息段 - + Returns: bool: 是否执行了清理操作 """ if not SEGMENT_CLEANUP_CONFIG["enable_cleanup"]: return False - + current_time = time.time() - + # 检查是否需要执行清理(基于时间间隔) cleanup_interval_seconds = SEGMENT_CLEANUP_CONFIG["cleanup_interval_hours"] * 3600 if current_time - self.last_cleanup_time < cleanup_interval_seconds: return False - + logger.info(f"{self.log_prefix} 开始执行老消息段清理...") - + cleanup_stats = { "users_cleaned": 0, "segments_removed": 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_segments_per_user = SEGMENT_CLEANUP_CONFIG["max_segments_per_user"] - + users_to_remove = [] - + for person_id, segments in self.person_engaged_cache.items(): cleanup_stats["total_segments_before"] += len(segments) original_segment_count = len(segments) - + # 1. 按时间清理:移除过期的消息段 segments_after_age_cleanup = [] for segment in segments: @@ -326,8 +334,10 @@ class RelationshipProcessor(BaseProcessor): segments_after_age_cleanup.append(segment) else: 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. 按数量清理:如果消息段数量仍然过多,保留最新的 if len(segments_after_age_cleanup) > max_segments_per_user: # 按end_time排序,保留最新的 @@ -335,10 +345,12 @@ class RelationshipProcessor(BaseProcessor): segments_removed_count = len(segments_after_age_cleanup) - max_segments_per_user cleanup_stats["segments_removed"] += segments_removed_count 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} 个最老的消息段" + ) + # 使用清理后的消息段 - + # 更新缓存 if len(segments_after_age_cleanup) == 0: # 如果没有剩余消息段,标记用户为待移除 @@ -346,34 +358,38 @@ class RelationshipProcessor(BaseProcessor): else: self.person_engaged_cache[person_id] = segments_after_age_cleanup cleanup_stats["total_segments_after"] += len(segments_after_age_cleanup) - + if original_segment_count != len(segments_after_age_cleanup): cleanup_stats["users_cleaned"] += 1 - + # 移除没有消息段的用户 for person_id in users_to_remove: del self.person_engaged_cache[person_id] logger.debug(f"{self.log_prefix} 移除用户 {person_id}:没有剩余消息段") - + # 更新最后清理时间 self.last_cleanup_time = current_time - + # 保存缓存 if cleanup_stats["segments_removed"] > 0 or len(users_to_remove) > 0: self._save_cache() - logger.info(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']}") + logger.info( + 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: logger.debug(f"{self.log_prefix} 清理完成 - 无需清理任何内容") - + return cleanup_stats["segments_removed"] > 0 or len(users_to_remove) > 0 - + def force_cleanup_user_segments(self, person_id: str) -> bool: """强制清理指定用户的所有消息段 - + Args: person_id: 用户ID - + Returns: bool: 是否成功清理 """ @@ -389,34 +405,42 @@ class RelationshipProcessor(BaseProcessor): """获取缓存状态信息,用于调试和监控""" if not self.person_engaged_cache: return 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(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"最后处理消息时间:{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"清理配置:{'启用' 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("") - + for person_id, segments in self.person_engaged_cache.items(): total_count = self._get_total_message_count(person_id) status_lines.append(f"用户 {person_id}:") status_lines.append(f" 总消息数:{total_count} ({total_count}/45)") status_lines.append(f" 消息段数:{len(segments)}") - + for i, segment in enumerate(segments): - 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'])) - 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']})") + 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"])) + 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("") - + return "\n".join(status_lines) # ================================ # 主要处理流程 # 统筹各模块协作、对外提供服务接口 # ================================ - + async def process_info(self, observations: List[Observation] = None, *infos) -> List[InfoBase]: """处理信息对象 @@ -446,7 +470,7 @@ class RelationshipProcessor(BaseProcessor): """ # 0. 执行定期清理 self._cleanup_old_segments() - + # 1. 从观察信息中提取所需数据 # 需要兼容私聊 @@ -456,24 +480,35 @@ class RelationshipProcessor(BaseProcessor): for observation in observations: if isinstance(observation, ChattingObservation): chat_observe_info = observation.get_observe_info() - + # 从聊天观察中提取用户信息并更新消息段 # 获取最新的非bot消息来更新消息段 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: # 处理所有新的非bot消息 for latest_msg in latest_messages: - user_id = latest_msg.get('user_id') - platform = latest_msg.get('user_platform') or latest_msg.get('chat_info_platform') - 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: + user_id = latest_msg.get("user_id") + platform = latest_msg.get("user_platform") or latest_msg.get("chat_info_platform") + 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 + ): from src.person_info.person_info import PersonInfoManager + person_id = PersonInfoManager.get_person_id(platform, user_id) 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) break @@ -496,9 +531,7 @@ class RelationshipProcessor(BaseProcessor): for person_id in users_to_build_relationship: segments = self.person_engaged_cache[person_id] # 异步执行关系构建 - asyncio.create_task( - self.update_impression_on_segments(person_id, self.subheartflow_id, segments) - ) + asyncio.create_task(self.update_impression_on_segments(person_id, self.subheartflow_id, segments)) # 移除已处理的用户缓存 del self.person_engaged_cache[person_id] self._save_cache() @@ -659,11 +692,11 @@ class RelationshipProcessor(BaseProcessor): # 关系构建模块 # 负责触发关系构建、整合消息段、更新用户印象 # ================================ - + async def update_impression_on_segments(self, person_id: str, chat_id: str, segments: List[Dict[str, any]]): """ 基于消息段更新用户印象 - + Args: person_id: 用户ID chat_id: 聊天ID @@ -672,17 +705,21 @@ class RelationshipProcessor(BaseProcessor): logger.info(f"开始为 {person_id} 基于 {len(segments)} 个消息段更新印象") try: processed_messages = [] - + for i, segment in enumerate(segments): start_time = segment["start_time"] end_time = segment["end_time"] - message_count = segment["message_count"] - start_date = time.strftime('%Y-%m-%d %H:%M', time.localtime(start_time)) - + segment["message_count"] + 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) - logger.info(f"消息段 {i+1}: {start_date} - {time.strftime('%Y-%m-%d %H:%M', time.localtime(end_time))}, 消息数: {len(segment_messages)}") - + segment_messages = get_raw_msg_by_timestamp_with_chat_inclusive( + 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 i > 0: @@ -690,31 +727,29 @@ class RelationshipProcessor(BaseProcessor): gap_message = { "time": start_time - 0.1, # 稍微早于段开始时间 "user_id": "system", - "user_platform": "system", + "user_platform": "system", "user_nickname": "系统", "user_cardname": "", "display_message": f"...(中间省略一些消息){start_date} 之后的消息如下...", "is_action_record": True, "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.extend(segment_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)} 条消息(包含间隔标识)用于印象更新") relationship_manager = get_relationship_manager() - + # 调用原有的更新方法 await relationship_manager.update_person_impression( - person_id=person_id, - timestamp=time.time(), - bot_engaged_messages=processed_messages + person_id=person_id, timestamp=time.time(), bot_engaged_messages=processed_messages ) else: logger.info(f"没有找到 {person_id} 的消息段对应的消息,不更新印象") @@ -727,7 +762,7 @@ class RelationshipProcessor(BaseProcessor): # 信息调取模块 # 负责实时分析对话需求、提取用户信息、管理信息缓存 # ================================ - + async def _execute_instant_extraction_batch(self, instant_tasks: list): """ 批量执行即时提取任务 @@ -919,6 +954,4 @@ class RelationshipProcessor(BaseProcessor): logger.error(traceback.format_exc()) - - init_prompt() diff --git a/src/chat/heart_flow/observation/hfcloop_observation.py b/src/chat/heart_flow/observation/hfcloop_observation.py index 1a21d675c..c2834257b 100644 --- a/src/chat/heart_flow/observation/hfcloop_observation.py +++ b/src/chat/heart_flow/observation/hfcloop_observation.py @@ -47,7 +47,9 @@ class HFCloopObservation: action_reasoning = action_result.get("reasoning", "未提供理由") is_taken = cycle.loop_action_info.get("action_taken", False) 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_reasoning) # print(is_taken) diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index ece304e42..be2435c3a 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -2,7 +2,7 @@ import asyncio import time import traceback from random import random -from typing import List, Optional, Dict, Any # 导入类型提示 +from typing import List, Optional, Dict # 导入类型提示 import os import pickle 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.focus_chat.replyer.default_replyer import DefaultReplyer 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 willing_manager = get_willing_manager() @@ -80,13 +85,13 @@ class NormalChat: # 新的消息段缓存结构: # {person_id: [{"start_time": float, "end_time": float, "last_msg_time": float, "message_count": int}, ...]} self.person_engaged_cache: Dict[str, List[Dict[str, any]]] = {} - + # 持久化存储文件路径 self.cache_file_path = os.path.join("data", f"relationship_cache_{self.stream_id}.pkl") - + # 最后处理的消息时间,避免重复处理相同消息 self.last_processed_message_time = 0.0 - + # 最后清理时间,用于定期清理老消息段 self.last_cleanup_time = 0.0 @@ -104,19 +109,21 @@ class NormalChat: # 缓存管理模块 # 负责持久化存储、状态管理、缓存读写 # ================================ - + def _load_cache(self): """从文件加载持久化的缓存""" if os.path.exists(self.cache_file_path): try: - with open(self.cache_file_path, 'rb') as f: + with open(self.cache_file_path, "rb") as f: cache_data = pickle.load(f) # 新格式:包含额外信息的缓存 - 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_cleanup_time = cache_data.get('last_cleanup_time', 0.0) + 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_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: logger.error(f"[{self.stream_name}] 加载关系缓存失败: {e}") self.person_engaged_cache = {} @@ -129,63 +136,65 @@ class NormalChat: try: os.makedirs(os.path.dirname(self.cache_file_path), exist_ok=True) cache_data = { - 'person_engaged_cache': self.person_engaged_cache, - 'last_processed_message_time': self.last_processed_message_time, - 'last_cleanup_time': self.last_cleanup_time + "person_engaged_cache": self.person_engaged_cache, + "last_processed_message_time": self.last_processed_message_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) logger.debug(f"[{self.stream_name}] 成功保存关系缓存") except Exception as e: logger.error(f"[{self.stream_name}] 保存关系缓存失败: {e}") # ================================ - # 消息段管理模块 + # 消息段管理模块 # 负责跟踪用户消息活动、管理消息段、清理过期数据 # ================================ - + def _update_message_segments(self, person_id: str, message_time: float): """更新用户的消息段 - + Args: person_id: 用户ID message_time: 消息时间戳 """ if person_id not in self.person_engaged_cache: self.person_engaged_cache[person_id] = [] - + segments = self.person_engaged_cache[person_id] current_time = time.time() - + # 获取该消息前5条消息的时间作为潜在的开始时间 before_messages = get_raw_msg_before_timestamp_with_chat(self.stream_id, message_time, limit=5) if before_messages: # 由于get_raw_msg_before_timestamp_with_chat返回按时间升序排序的消息,最后一个是最接近message_time的 # 我们需要第一个消息作为开始时间,但应该确保至少包含5条消息或该用户之前的消息 - potential_start_time = before_messages[0]['time'] + potential_start_time = before_messages[0]["time"] else: # 如果没有前面的消息,就从当前消息开始 potential_start_time = message_time - + # 如果没有现有消息段,创建新的 if not segments: new_segment = { "start_time": potential_start_time, "end_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) - 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() return - + # 获取最后一个消息段 last_segment = segments[-1] - + # 计算从最后一条消息到当前消息之间的消息数量(不包含边界) messages_between = self._count_messages_between(last_segment["last_msg_time"], message_time) - + if messages_between <= 10: # 在10条消息内,延伸当前消息段 last_segment["end_time"] = message_time @@ -203,82 +212,82 @@ class NormalChat: ) if after_messages and len(after_messages) >= 5: # 如果有足够的后续消息,使用第5条消息的时间作为结束时间 - last_segment["end_time"] = after_messages[4]['time'] + last_segment["end_time"] = after_messages[4]["time"] else: # 如果没有足够的后续消息,保持原有的结束时间 pass - + # 重新计算当前消息段的消息数量 last_segment["message_count"] = self._count_messages_in_timerange( last_segment["start_time"], last_segment["end_time"] ) - + # 创建新的消息段 new_segment = { "start_time": potential_start_time, "end_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) logger.info(f"[{self.stream_name}] 为用户 {person_id} 创建新消息段(超过10条消息间隔): {new_segment}") - + self._save_cache() - + def _count_messages_in_timerange(self, start_time: float, end_time: float) -> int: """计算指定时间范围内的消息数量(包含边界)""" messages = get_raw_msg_by_timestamp_with_chat_inclusive(self.stream_id, start_time, end_time) return len(messages) - + def _count_messages_between(self, start_time: float, end_time: float) -> int: """计算两个时间点之间的消息数量(不包含边界),用于间隔检查""" return num_new_messages_since(self.stream_id, start_time, end_time) - + def _get_total_message_count(self, person_id: str) -> int: """获取用户所有消息段的总消息数量""" if person_id not in self.person_engaged_cache: return 0 - + total_count = 0 for segment in self.person_engaged_cache[person_id]: total_count += segment["message_count"] - + return total_count - + def _cleanup_old_segments(self) -> bool: """清理老旧的消息段 - + Returns: bool: 是否执行了清理操作 """ if not SEGMENT_CLEANUP_CONFIG["enable_cleanup"]: return False - + current_time = time.time() - + # 检查是否需要执行清理(基于时间间隔) cleanup_interval_seconds = SEGMENT_CLEANUP_CONFIG["cleanup_interval_hours"] * 3600 if current_time - self.last_cleanup_time < cleanup_interval_seconds: return False - + logger.info(f"[{self.stream_name}] 开始执行老消息段清理...") - + cleanup_stats = { "users_cleaned": 0, "segments_removed": 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_segments_per_user = SEGMENT_CLEANUP_CONFIG["max_segments_per_user"] - + users_to_remove = [] - + for person_id, segments in self.person_engaged_cache.items(): cleanup_stats["total_segments_before"] += len(segments) original_segment_count = len(segments) - + # 1. 按时间清理:移除过期的消息段 segments_after_age_cleanup = [] for segment in segments: @@ -287,8 +296,10 @@ class NormalChat: segments_after_age_cleanup.append(segment) else: 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. 按数量清理:如果消息段数量仍然过多,保留最新的 if len(segments_after_age_cleanup) > max_segments_per_user: # 按end_time排序,保留最新的 @@ -296,10 +307,12 @@ class NormalChat: segments_removed_count = len(segments_after_age_cleanup) - max_segments_per_user cleanup_stats["segments_removed"] += segments_removed_count 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} 个最老的消息段" + ) + # 使用清理后的消息段 - + # 更新缓存 if len(segments_after_age_cleanup) == 0: # 如果没有剩余消息段,标记用户为待移除 @@ -307,76 +320,90 @@ class NormalChat: else: self.person_engaged_cache[person_id] = segments_after_age_cleanup cleanup_stats["total_segments_after"] += len(segments_after_age_cleanup) - + if original_segment_count != len(segments_after_age_cleanup): cleanup_stats["users_cleaned"] += 1 - + # 移除没有消息段的用户 for person_id in users_to_remove: del self.person_engaged_cache[person_id] logger.debug(f"[{self.stream_name}] 移除用户 {person_id}:没有剩余消息段") - + # 更新最后清理时间 self.last_cleanup_time = current_time - + # 保存缓存 if cleanup_stats["segments_removed"] > 0 or len(users_to_remove) > 0: self._save_cache() - logger.info(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']}") + logger.info( + 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: logger.debug(f"[{self.stream_name}] 清理完成 - 无需清理任何内容") - + return cleanup_stats["segments_removed"] > 0 or len(users_to_remove) > 0 def get_cache_status(self) -> str: """获取缓存状态信息,用于调试和监控""" if not self.person_engaged_cache: return 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(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"最后处理消息时间:{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"清理配置:{'启用' 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("") - + for person_id, segments in self.person_engaged_cache.items(): total_count = self._get_total_message_count(person_id) status_lines.append(f"用户 {person_id}:") status_lines.append(f" 总消息数:{total_count} ({total_count}/45)") status_lines.append(f" 消息段数:{len(segments)}") - + for i, segment in enumerate(segments): - 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'])) - 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']})") + 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"])) + 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("") - + return "\n".join(status_lines) def _update_user_message_segments(self, message: MessageRecv): """更新用户消息段信息""" - current_time = time.time() + time.time() user_id = message.message_info.user_info.user_id platform = message.message_info.platform msg_time = message.message_info.time - + # 跳过机器人自己的消息 if user_id == global_config.bot.qq_account: return - + # 只处理新消息(避免重复处理) if msg_time <= self.last_processed_message_time: return - + person_id = PersonInfoManager.get_person_id(platform, user_id) self._update_message_segments(person_id, 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: @@ -585,7 +612,7 @@ class NormalChat: # 执行定期清理 self._cleanup_old_segments() - + # 更新消息段信息 self._update_user_message_segments(message) @@ -1072,12 +1099,10 @@ class NormalChat: """获取动作管理器实例""" return self.action_manager - - async def _check_relation_building_conditions(self): """检查person_engaged_cache中是否有满足关系构建条件的用户""" users_to_build_relationship = [] - + for person_id, segments in list(self.person_engaged_cache.items()): total_message_count = self._get_total_message_count(person_id) if total_message_count >= 45: @@ -1095,9 +1120,7 @@ class NormalChat: for person_id in users_to_build_relationship: segments = self.person_engaged_cache[person_id] # 异步执行关系构建 - asyncio.create_task( - self._build_relation_for_person_segments(person_id, segments) - ) + asyncio.create_task(self._build_relation_for_person_segments(person_id, segments)) # 移除已处理的用户缓存 del self.person_engaged_cache[person_id] self._save_cache() @@ -1108,17 +1131,19 @@ class NormalChat: logger.info(f"[{self.stream_name}] 开始为 {person_id} 基于 {len(segments)} 个消息段更新印象") try: processed_messages = [] - + for i, segment in enumerate(segments): start_time = segment["start_time"] end_time = segment["end_time"] - message_count = segment["message_count"] - start_date = time.strftime('%Y-%m-%d %H:%M', time.localtime(start_time)) - + segment["message_count"] + 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) - 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 i > 0: @@ -1126,33 +1151,33 @@ class NormalChat: gap_message = { "time": start_time - 0.1, # 稍微早于段开始时间 "user_id": "system", - "user_platform": "system", + "user_platform": "system", "user_nickname": "系统", "user_cardname": "", "display_message": f"...(中间省略一些消息){start_date} 之后的消息如下...", "is_action_record": True, "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.extend(segment_messages) if processed_messages: # 按时间排序所有消息(包括间隔标识) - processed_messages.sort(key=lambda x: x['time']) - - logger.info(f"[{self.stream_name}] 为 {person_id} 获取到总共 {len(processed_messages)} 条消息(包含间隔标识)用于印象更新") + processed_messages.sort(key=lambda x: x["time"]) + + logger.info( + f"[{self.stream_name}] 为 {person_id} 获取到总共 {len(processed_messages)} 条消息(包含间隔标识)用于印象更新" + ) relationship_manager = get_relationship_manager() - + # 调用原有的更新方法 await relationship_manager.update_person_impression( - person_id=person_id, - timestamp=time.time(), - bot_engaged_messages=processed_messages + person_id=person_id, timestamp=time.time(), bot_engaged_messages=processed_messages ) - + logger.info(f"[{self.stream_name}] 用户 {person_id} 关系构建完成") else: logger.warning(f"[{self.stream_name}] 没有找到 {person_id} 的消息段对应的消息,不更新印象") diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index d65d27db9..3a30f4ba6 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -210,7 +210,7 @@ class RelationshipManager: if not readable_messages: return - + for original_name, mapped_name in name_mapping.items(): # print(f"original_name: {original_name}, mapped_name: {mapped_name}") readable_messages = readable_messages.replace(f"{original_name}", f"{mapped_name}") diff --git a/src/plugin_system/base/base_plugin.py b/src/plugin_system/base/base_plugin.py index 21d1658fe..e445d99cc 100644 --- a/src/plugin_system/base/base_plugin.py +++ b/src/plugin_system/base/base_plugin.py @@ -120,7 +120,7 @@ class BasePlugin(ABC): if isinstance(value, str): toml_str += f'{field_name} = "{value}"\n' elif isinstance(value, bool): - toml_str += f'{field_name} = {str(value).lower()}\n' + toml_str += f"{field_name} = {str(value).lower()}\n" else: toml_str += f"{field_name} = {value}\n" @@ -173,7 +173,7 @@ class BasePlugin(ABC): with open(config_file_path, "r", encoding="utf-8") as f: self.config = toml.load(f) or {} logger.debug(f"{self.log_prefix} 配置已从 {config_file_path} 加载") - + # 从配置中更新 enable_plugin if "plugin" in self.config and "enabled" in self.config["plugin"]: self.enable_plugin = self.config["plugin"]["enabled"] diff --git a/src/plugin_system/base/config_types.py b/src/plugin_system/base/config_types.py index c3f997282..752b33453 100644 --- a/src/plugin_system/base/config_types.py +++ b/src/plugin_system/base/config_types.py @@ -15,4 +15,4 @@ class ConfigField: description: str # 字段描述 example: Optional[str] = None # 示例值 required: bool = False # 是否必需 - choices: Optional[List[Any]] = field(default_factory=list) # 可选值列表 \ No newline at end of file + choices: Optional[List[Any]] = field(default_factory=list) # 可选值列表 diff --git a/src/plugin_system/core/plugin_manager.py b/src/plugin_system/core/plugin_manager.py index 6509660d7..559dec1c6 100644 --- a/src/plugin_system/core/plugin_manager.py +++ b/src/plugin_system/core/plugin_manager.py @@ -93,12 +93,12 @@ class PluginManager: self.plugin_paths[plugin_name] = plugin_dir plugin_instance = plugin_class(plugin_dir=plugin_dir) - + # 检查插件是否启用 if not plugin_instance.enable_plugin: logger.info(f"插件 {plugin_name} 已禁用,跳过加载") continue - + if plugin_instance.register_plugin(): total_registered += 1 self.loaded_plugins[plugin_name] = plugin_instance diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 67ea65fd6..e854a0b2a 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -427,7 +427,9 @@ class CoreActionsPlugin(BasePlugin): "name": ConfigField(type=str, default="core_actions", description="插件名称", required=True), "version": ConfigField(type=str, default="1.0.0", 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_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_exit_focus": ConfigField(type=bool, default=True, description="是否启用'退出专注模式'动作"), "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": { - "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_2_wait": ConfigField(type=int, default=60, description="第2次连续不回复的等待时间(秒)"), "stage_3_wait": ConfigField(type=int, default=600, description="第3次连续不回复的等待时间(秒)"), }, "emoji": { "random_probability": ConfigField( - type=float, - default=0.1, - description="Normal模式下,随机发送表情的概率(0.0到1.0)", - example=0.15 + type=float, default=0.1, description="Normal模式下,随机发送表情的概率(0.0到1.0)", example=0.15 ) - } + }, } 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): components.append((ChangeToFocusChatAction.get_action_info(), ChangeToFocusChatAction)) 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): - components.append((LogCommand.get_command_info(name="log", description="记录消息到日志,不拦截后续处理"), LogCommand)) + components.append( + (LogCommand.get_command_info(name="log", description="记录消息到日志,不拦截后续处理"), LogCommand) + ) return components diff --git a/src/plugins/built_in/doubao_pic_plugin/plugin.py b/src/plugins/built_in/doubao_pic_plugin/plugin.py index 9f7ae8aa4..c8bafd262 100644 --- a/src/plugins/built_in/doubao_pic_plugin/plugin.py +++ b/src/plugins/built_in/doubao_pic_plugin/plugin.py @@ -412,7 +412,7 @@ class DoubaoImagePlugin(BasePlugin): "api": "API相关配置,包含火山引擎API的访问信息", "generation": "图片生成参数配置,控制生成图片的各种参数", "cache": "结果缓存配置", - "components": "组件启用配置" + "components": "组件启用配置", } # 配置Schema定义 @@ -422,56 +422,47 @@ class DoubaoImagePlugin(BasePlugin): "version": ConfigField(type=str, default="2.0.0", description="插件版本号"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"), "description": ConfigField( - type=str, - default="基于火山引擎豆包模型的AI图片生成插件", - description="插件描述", - required=True - ) + type=str, default="基于火山引擎豆包模型的AI图片生成插件", description="插件描述", required=True + ), }, "api": { "base_url": ConfigField( type=str, default="https://ark.cn-beijing.volces.com/api/v3", description="API基础URL", - example="https://api.example.com/v1" + example="https://api.example.com/v1", ), "volcano_generate_api_key": ConfigField( - type=str, - default="YOUR_DOUBAO_API_KEY_HERE", - description="火山引擎豆包API密钥", - required=True - ) + type=str, default="YOUR_DOUBAO_API_KEY_HERE", description="火山引擎豆包API密钥", required=True + ), }, "generation": { "default_model": ConfigField( type=str, default="doubao-seedream-3-0-t2i-250415", 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( type=str, default="1024x1024", description="默认图片尺寸", example="1024x1024", - choices=["1024x1024", "1024x1280", "1280x1024", "1024x1536", "1536x1024"] + choices=["1024x1024", "1024x1280", "1280x1024", "1024x1536", "1536x1024"], ), "default_watermark": ConfigField(type=bool, default=True, description="是否默认添加水印"), "default_guidance_scale": ConfigField( - type=float, - default=2.5, - description="模型指导强度,影响图片与提示的关联性", - example="2.0" + type=float, default=2.5, description="模型指导强度,影响图片与提示的关联性", example="2.0" ), - "default_seed": ConfigField(type=int, default=42, description="随机种子,用于复现图片") + "default_seed": ConfigField(type=int, default=42, description="随机种子,用于复现图片"), }, "cache": { "enabled": ConfigField(type=bool, default=True, description="是否启用请求缓存"), - "max_size": ConfigField(type=int, default=10, description="最大缓存数量") + "max_size": ConfigField(type=int, default=10, description="最大缓存数量"), }, "components": { "enable_image_generation": ConfigField(type=bool, default=True, description="是否启用图片生成Action") - } + }, } def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: diff --git a/src/plugins/built_in/mute_plugin/plugin.py b/src/plugins/built_in/mute_plugin/plugin.py index fcdeae071..90822fb9c 100644 --- a/src/plugins/built_in/mute_plugin/plugin.py +++ b/src/plugins/built_in/mute_plugin/plugin.py @@ -164,7 +164,7 @@ class MuteAction(BaseAction): success = await self.send_command( command_name="GROUP_BAN", args={"qq_id": str(user_id), "duration": str(duration_int)}, - display_message=f"发送禁言命令", + display_message="发送禁言命令", ) if success: @@ -180,8 +180,8 @@ class MuteAction(BaseAction): "user_id": user_id, "duration": duration_int, "duration_str": time_str, - "reason": reason - } + "reason": reason, + }, ) return True, f"成功禁言 {target},时长 {time_str}" else: @@ -389,7 +389,7 @@ class MutePlugin(BasePlugin): "mute": "核心禁言功能配置", "smart_mute": "智能禁言Action的专属配置", "mute_command": "禁言命令Command的专属配置", - "logging": "日志记录相关配置" + "logging": "日志记录相关配置", } # 配置Schema定义 @@ -398,17 +398,21 @@ class MutePlugin(BasePlugin): "name": ConfigField(type=str, default="mute_plugin", description="插件名称", required=True), "version": ConfigField(type=str, default="2.0.0", 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": { "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": { "min_duration": ConfigField(type=int, default=60, description="最短禁言时长(秒)"), "max_duration": ConfigField(type=int, default=2592000, description="最长禁言时长(秒),默认30天"), "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="是否记录禁言历史(未来功能)"), "templates": ConfigField( type=list, @@ -418,9 +422,9 @@ class MutePlugin(BasePlugin): "明白了,禁言 {target} {duration},原因是{reason}", "哇哈哈哈哈哈,已禁言 {target} {duration},理由:{reason}", "哎呦我去,对 {target} 执行禁言 {duration},因为{reason}", - "{target},你完蛋了,我要禁言你 {duration} 秒,原因:{reason}" + "{target},你完蛋了,我要禁言你 {duration} 秒,原因:{reason}", ], - description="成功禁言后发送的随机消息模板" + description="成功禁言后发送的随机消息模板", ), "error_messages": ConfigField( type=list, @@ -430,26 +434,30 @@ class MutePlugin(BasePlugin): "禁言时长必须是正数哦~", "禁言时长必须是数字哦~", "找不到 {target} 这个人呢~", - "查找用户信息时出现问题~" + "查找用户信息时出现问题~", ], - description="执行禁言过程中发生错误时发送的随机消息模板" - ) + description="执行禁言过程中发生错误时发送的随机消息模板", + ), }, "smart_mute": { "strict_mode": ConfigField(type=bool, default=True, description="LLM判定的严格模式"), - "keyword_sensitivity": ConfigField(type=str, default="normal", description="关键词激活的敏感度", choices=["low", "normal", "high"]), - "allow_parallel": ConfigField(type=bool, default=False, description="是否允许并行执行(暂未启用)") + "keyword_sensitivity": ConfigField( + type=str, default="normal", description="关键词激活的敏感度", choices=["low", "normal", "high"] + ), + "allow_parallel": ConfigField(type=bool, default=False, description="是否允许并行执行(暂未启用)"), }, "mute_command": { "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": { - "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="日志记录前缀"), "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]]: diff --git a/src/plugins/built_in/tts_plugin/plugin.py b/src/plugins/built_in/tts_plugin/plugin.py index 632e25efc..34eedfa21 100644 --- a/src/plugins/built_in/tts_plugin/plugin.py +++ b/src/plugins/built_in/tts_plugin/plugin.py @@ -112,7 +112,7 @@ class TTSPlugin(BasePlugin): config_section_descriptions = { "plugin": "插件基本信息配置", "components": "组件启用控制", - "logging": "日志记录相关配置" + "logging": "日志记录相关配置", } # 配置Schema定义 @@ -121,15 +121,15 @@ class TTSPlugin(BasePlugin): "name": ConfigField(type=str, default="tts_plugin", description="插件名称", required=True), "version": ConfigField(type=str, default="0.1.0", description="插件版本号"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"), - "description": ConfigField(type=str, default="文字转语音插件", description="插件描述", required=True) - }, - "components": { - "enable_tts": ConfigField(type=bool, default=True, description="是否启用TTS Action") + "description": ConfigField(type=str, default="文字转语音插件", description="插件描述", required=True), }, + "components": {"enable_tts": ConfigField(type=bool, default=True, description="是否启用TTS Action")}, "logging": { - "level": ConfigField(type=str, default="INFO", description="日志记录级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]), - "prefix": ConfigField(type=str, default="[TTS]", description="日志记录前缀") - } + "level": ConfigField( + 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]]: diff --git a/src/plugins/built_in/vtb_plugin/plugin.py b/src/plugins/built_in/vtb_plugin/plugin.py index 881958a14..c290687b9 100644 --- a/src/plugins/built_in/vtb_plugin/plugin.py +++ b/src/plugins/built_in/vtb_plugin/plugin.py @@ -128,25 +128,22 @@ class VTBPlugin(BasePlugin): "name": ConfigField(type=str, default="vtb_plugin", description="插件名称", required=True), "version": ConfigField(type=str, default="0.1.0", description="插件版本号"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"), - "description": ConfigField(type=str, default="虚拟主播情感表达插件", description="插件描述", required=True) - }, - "components": { - "enable_vtb": ConfigField(type=bool, default=True, description="是否启用VTB动作") + "description": ConfigField(type=str, default="虚拟主播情感表达插件", description="插件描述", required=True), }, + "components": {"enable_vtb": ConfigField(type=bool, default=True, description="是否启用VTB动作")}, "vtb_action": { "random_activation_probability": ConfigField( - type=float, - default=0.08, - description="Normal模式下,随机触发VTB动作的概率(0.0到1.0)", - example=0.1 + type=float, default=0.08, description="Normal模式下,随机触发VTB动作的概率(0.0到1.0)", example=0.1 ), "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": { - "level": ConfigField(type=str, default="INFO", description="日志级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]), - "prefix": ConfigField(type=str, default="[VTB]", description="日志记录前缀") - } + "level": ConfigField( + 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]]: