diff --git a/scripts/070configexe.py b/scripts/070configexe.py deleted file mode 100644 index 340e9fa3c..000000000 --- a/scripts/070configexe.py +++ /dev/null @@ -1,1136 +0,0 @@ -import tkinter as tk -from tkinter import ttk, messagebox, filedialog -import tomli -import tomli_w -import os -from typing import Any, Dict, List -import threading -import time -import sys - - -class ConfigEditor: - def __init__(self, root): - self.root = root - self.root.title("麦麦配置编辑器") - - # 加载编辑器配置 - self.load_editor_config() - - # 设置窗口大小 - self.root.geometry(f"{self.window_width}x{self.window_height}") - - # 加载配置 - self.load_config() - - # 加载环境变量 - self.load_env_vars() - - # 自动保存相关 - self.last_save_time = time.time() - self.save_timer = None - self.save_lock = threading.Lock() - self.current_section = None # 当前编辑的节 - self.pending_save = False # 是否有待保存的更改 - - # 存储控件的字典 - self.widgets = {} - - # 创建主框架 - self.main_frame = ttk.Frame(self.root, padding="10") - self.main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) - - # 创建版本号显示 - self.create_version_label() - - # 创建左侧导航栏 - self.create_navbar() - - # 创建右侧编辑区 - self.create_editor() - - # 创建底部按钮 - self.create_buttons() - - # 配置网格权重 - self.root.columnconfigure(0, weight=1) - self.root.rowconfigure(0, weight=1) - self.main_frame.columnconfigure(1, weight=1) - self.main_frame.rowconfigure(1, weight=1) # 修改为1,因为第0行是版本号 - - # 默认选择快捷设置栏 - self.current_section = "quick_settings" - self.create_quick_settings_widgets() - # 选中导航树中的快捷设置项 - for item in self.tree.get_children(): - if self.tree.item(item)["values"][0] == "quick_settings": - self.tree.selection_set(item) - break - - def load_editor_config(self): - """加载编辑器配置""" - try: - editor_config_path = os.path.join(os.path.dirname(__file__), "configexe.toml") - with open(editor_config_path, "rb") as f: - self.editor_config = tomli.load(f) # 保存整个配置对象 - - # 设置配置路径 - self.config_path = self.editor_config["config"]["bot_config_path"] - # 如果路径是相对路径,转换为绝对路径 - if not os.path.isabs(self.config_path): - self.config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), self.config_path) - - # 设置编辑器参数 - self.window_width = self.editor_config["editor"]["window_width"] - self.window_height = self.editor_config["editor"]["window_height"] - self.save_delay = self.editor_config["editor"]["save_delay"] - - # 加载翻译 - self.translations = self.editor_config.get("translations", {}) - - except Exception as e: - messagebox.showerror("错误", f"加载编辑器配置失败: {str(e)}") - # 使用默认值 - self.editor_config = {} # 初始化空配置 - self.config_path = "config/bot_config.toml" - self.window_width = 1000 - self.window_height = 800 - self.save_delay = 1.0 - self.translations = {} - - def load_config(self): - try: - with open(self.config_path, "rb") as f: - self.config = tomli.load(f) - except Exception as e: - messagebox.showerror("错误", f"加载配置文件失败: {str(e)}") - self.config = {} - # 自动打开配置路径窗口 - self.open_path_config() - - def load_env_vars(self): - """加载并解析环境变量文件""" - try: - # 从配置中获取环境文件路径 - env_path = self.config.get("inner", {}).get("env_file", ".env") - if not os.path.isabs(env_path): - env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), env_path) - - if not os.path.exists(env_path): - print(f"环境文件不存在: {env_path}") - return - - # 读取环境文件 - with open(env_path, "r", encoding="utf-8") as f: - env_content = f.read() - - # 解析环境变量 - env_vars = {} - for line in env_content.split("\n"): - line = line.strip() - if not line or line.startswith("#"): - continue - - if "=" in line: - key, value = line.split("=", 1) - key = key.strip() - value = value.strip() - - # 检查是否是目标变量 - if key.endswith("_BASE_URL") or key.endswith("_KEY"): - # 提取前缀(去掉_BASE_URL或_KEY) - prefix = key[:-9] if key.endswith("_BASE_URL") else key[:-4] - if prefix not in env_vars: - env_vars[prefix] = {} - env_vars[prefix][key] = value - - # 将解析的环境变量添加到配置中 - if "env_vars" not in self.config: - self.config["env_vars"] = {} - self.config["env_vars"].update(env_vars) - - except Exception as e: - print(f"加载环境变量失败: {str(e)}") - - def create_version_label(self): - """创建版本号显示标签""" - version = self.config.get("inner", {}).get("version", "未知版本") - version_frame = ttk.Frame(self.main_frame) - version_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10)) - - # 添加配置按钮 - config_button = ttk.Button(version_frame, text="配置路径", command=self.open_path_config) - config_button.pack(side=tk.LEFT, padx=5) - - version_label = ttk.Label(version_frame, text=f"麦麦版本:{version}", font=("微软雅黑", 10, "bold")) - version_label.pack(side=tk.LEFT, padx=5) - - def create_navbar(self): - # 创建左侧导航栏 - self.nav_frame = ttk.Frame(self.main_frame, padding="5") - self.nav_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) - - # 创建导航树 - self.tree = ttk.Treeview(self.nav_frame) - self.tree.pack(fill=tk.BOTH, expand=True) - - # 添加快捷设置节 - self.tree.insert("", "end", text="快捷设置", values=("quick_settings",)) - - # 添加env_vars节,显示为"配置你的模型APIKEY" - self.tree.insert("", "end", text="配置你的模型APIKEY", values=("env_vars",)) - - # 只显示bot_config.toml实际存在的section - for section in self.config: - if section not in ( - "inner", - "env_vars", - "telemetry", - "experimental", - "maim_message", - "keyword_reaction", - "message_receive", - "relationship", - ): - section_trans = self.translations.get("sections", {}).get(section, {}) - section_name = section_trans.get("name", section) - self.tree.insert("", "end", text=section_name, values=(section,)) - # 绑定选择事件 - self.tree.bind("<>", self.on_section_select) - - def create_editor(self): - # 创建右侧编辑区 - self.editor_frame = ttk.Frame(self.main_frame, padding="5") - self.editor_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S)) - - # 创建编辑区标题 - # self.editor_title = ttk.Label(self.editor_frame, text="") - # self.editor_title.pack(fill=tk.X) - - # 创建编辑区内容 - self.editor_content = ttk.Frame(self.editor_frame) - self.editor_content.pack(fill=tk.BOTH, expand=True) - - # 创建滚动条 - self.scrollbar = ttk.Scrollbar(self.editor_content) - self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) - - # 创建画布和框架 - self.canvas = tk.Canvas(self.editor_content, yscrollcommand=self.scrollbar.set) - self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) - self.scrollbar.config(command=self.canvas.yview) - - # 创建内容框架 - self.content_frame = ttk.Frame(self.canvas) - self.canvas.create_window((0, 0), window=self.content_frame, anchor=tk.NW) - - # 绑定画布大小变化事件 - self.content_frame.bind("", self.on_frame_configure) - self.canvas.bind("", self.on_canvas_configure) - - def on_frame_configure(self, event=None): - self.canvas.configure(scrollregion=self.canvas.bbox("all")) - - def on_canvas_configure(self, event): - # 更新内容框架的宽度以适应画布 - self.canvas.itemconfig(self.canvas.find_withtag("all")[0], width=event.width) - - def create_buttons(self): - # 创建底部按钮区 - self.button_frame = ttk.Frame(self.main_frame, padding="5") - self.button_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E)) - - # 刷新按钮 - # self.refresh_button = ttk.Button(self.button_frame, text="刷新", command=self.refresh_config) - # self.refresh_button.pack(side=tk.RIGHT, padx=5) - - # 高级选项按钮(左下角) - self.advanced_button = ttk.Button(self.button_frame, text="高级选项", command=self.open_advanced_options) - self.advanced_button.pack(side=tk.LEFT, padx=5) - - def create_widget_for_value(self, parent: ttk.Frame, key: str, value: Any, path: List[str]) -> None: - """为不同类型的值创建对应的编辑控件""" - frame = ttk.Frame(parent) - frame.pack(fill=tk.X, padx=5, pady=2) - - # --- 修改开始: 改进翻译查找逻辑 --- - full_config_path_key = ".".join(path + [key]) # 例如 "chinese_typo.enable" - - model_item_translations = { - "name": ("模型名称", "模型的唯一标识或名称"), - "provider": ("模型提供商", "模型API的提供商"), - "pri_in": ("输入价格", "模型输入的价格/消耗"), - "pri_out": ("输出价格", "模型输出的价格/消耗"), - "temp": ("模型温度", "控制模型输出的多样性"), - } - - item_name_to_display = key # 默认显示原始键名 - item_desc_to_display = "" # 默认无描述 - - # 1. 尝试使用完整路径的特定翻译 - specific_translation = self.translations.get("items", {}).get(full_config_path_key) - if specific_translation and specific_translation.get("name"): - item_name_to_display = specific_translation.get("name") - item_desc_to_display = specific_translation.get("description", "") - else: - # 2. 如果特定翻译未找到或没有name,尝试使用通用键名的翻译 - generic_translation = self.translations.get("items", {}).get(key) - if generic_translation and generic_translation.get("name"): - item_name_to_display = generic_translation.get("name") - item_desc_to_display = generic_translation.get("description", "") - elif key in model_item_translations: - item_name_to_display, item_desc_to_display = model_item_translations[key] - # --- 修改结束 --- - - # 配置名(大号字体) - label = ttk.Label(frame, text=item_name_to_display, font=("微软雅黑", 16, "bold")) - label.grid(row=0, column=0, sticky=tk.W, padx=5, pady=(0, 0)) - - # 星星图标快捷设置(与配置名同一行) - content_col_offset_for_star = 1 # 星标按钮占一列 - quick_settings = self.editor_config.get("editor", {}).get("quick_settings", {}).get("items", []) - already_in_quick = any(item.get("path") == full_config_path_key for item in quick_settings) - icon = "★" if already_in_quick else "☆" - icon_fg = "#FFD600" # 始终金色 - - def on_star_click(): - self.toggle_quick_setting( - full_config_path_key, widget_type, item_name_to_display, item_desc_to_display, already_in_quick - ) - # 立即刷新本分组 - for widget in parent.winfo_children(): - widget.destroy() - self.widgets.clear() - # 判断parent是不是self.content_frame - if parent == self.content_frame: - # 主界面 - if ( - hasattr(self, "current_section") - and self.current_section - and self.current_section != "quick_settings" - ): - self.create_section_widgets( - parent, self.current_section, self.config[self.current_section], [self.current_section] - ) - elif hasattr(self, "current_section") and self.current_section == "quick_settings": - self.create_quick_settings_widgets() - else: - # 弹窗Tab - # 重新渲染当前Tab的内容 - if path: - section = path[0] - self.create_section_widgets(parent, section, self.config[section], path) - - pin_btn = ttk.Button(frame, text=icon, width=2, command=on_star_click) - pin_btn.grid(row=0, column=content_col_offset_for_star, sticky=tk.W, padx=5) - try: - pin_btn.configure(style="Pin.TButton") - style = ttk.Style() - style.configure("Pin.TButton", foreground=icon_fg) - except Exception: - pass - - # 配置项描述(第二行) - desc_row = 1 - if item_desc_to_display: - desc_label = ttk.Label(frame, text=item_desc_to_display, foreground="gray", font=("微软雅黑", 10)) - desc_label.grid( - row=desc_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5, pady=(0, 4) - ) - widget_row = desc_row + 1 # 内容控件在描述下方 - else: - widget_row = desc_row # 内容控件直接在第二行 - - # 配置内容控件(第三行或第二行) - if path[0] == "inner": - value_label = ttk.Label(frame, text=str(value), font=("微软雅黑", 16)) - value_label.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5) - return - - if isinstance(value, bool): - # 布尔值使用复选框 - var = tk.BooleanVar(value=value) - checkbox = ttk.Checkbutton(frame, variable=var, command=lambda: self.on_value_changed()) - checkbox.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5) - self.widgets[tuple(path + [key])] = var - widget_type = "bool" - - elif isinstance(value, (int, float)): - # 数字使用数字输入框 - var = tk.StringVar(value=str(value)) - entry = ttk.Entry(frame, textvariable=var, font=("微软雅黑", 16)) - entry.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5) - var.trace_add("write", lambda *args: self.on_value_changed()) - self.widgets[tuple(path + [key])] = var - widget_type = "number" - - elif isinstance(value, list): - # 列表使用每行一个输入框的形式 - frame_list = ttk.Frame(frame) - frame_list.grid( - row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5 - ) - - # 创建添加和删除按钮 - button_frame = ttk.Frame(frame_list) - button_frame.pack(side=tk.RIGHT, padx=5) - - add_button = ttk.Button( - button_frame, text="+", width=3, command=lambda p=path + [key]: self.add_list_item(frame_list, p) - ) - add_button.pack(side=tk.TOP, pady=2) - - # 创建列表项框架 - items_frame = ttk.Frame(frame_list) - items_frame.pack(side=tk.LEFT, fill=tk.X, expand=True) - - # 存储所有输入框的变量 - entry_vars = [] - - # 为每个列表项创建输入框 - for i, item in enumerate(value): - self.create_list_item(items_frame, item, i, entry_vars, path + [key]) - - # 存储控件引用 - self.widgets[tuple(path + [key])] = (items_frame, entry_vars) - widget_type = "list" - - else: - # 其他类型(字符串等)使用普通文本框 - var = tk.StringVar(value=str(value)) - - # 特殊处理provider字段 - full_path = ".".join(path + [key]) - if key == "provider" and full_path.startswith("model."): - # print(f"处理provider字段,完整路径: {full_path}") - # print(f"当前config中的env_vars: {self.config.get('env_vars', {})}") - # 获取所有可用的provider选项 - providers = [] - if "env_vars" in self.config: - # print(f"找到env_vars节,内容: {self.config['env_vars']}") - # 遍历env_vars中的所有配置对 - for prefix, values in self.config["env_vars"].items(): - # print(f"检查配置对 {prefix}: {values}") - # 检查是否同时有BASE_URL和KEY - if f"{prefix}_BASE_URL" in values and f"{prefix}_KEY" in values: - providers.append(prefix) - # print(f"添加provider: {prefix}") - - # print(f"最终providers列表: {providers}") - if providers: - # 创建模型名称标签(大字体) - # model_name = var.get() if var.get() else providers[0] - # section_translations = { - # "model.utils": "麦麦组件模型", - # "model.utils_small": "小型麦麦组件模型", - # "model.memory_summary": "记忆概括模型", - # "model.vlm": "图像识别模型", - # "model.embedding": "嵌入模型", - # "model.replyer_1": "普通聊天:主要聊天模型", - # "model.replyer_2": "普通聊天:次要聊天模型", - # "model.focus_working_memory": "专注模式:工作记忆模型", - # "model.focus_tool_use": "专注模式:工具调用模型", - # "model.focus_planner": "专注模式:决策模型", - # "model.focus_expressor": "专注模式:表达器模型", - # } - # 获取当前节的名称 - # current_section = ".".join(path[:-1]) # 去掉最后一个key - # section_name = section_translations.get(current_section, current_section) - - # 创建节名称标签(大字体) - # section_label = ttk.Label(frame, text="11", font=("微软雅黑", 24, "bold")) - # section_label.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star +1, sticky=tk.W, padx=5, pady=(0, 5)) - - # 创建下拉菜单(小字体) - combo = ttk.Combobox( - frame, textvariable=var, values=providers, font=("微软雅黑", 12), state="readonly" - ) - combo.grid( - row=widget_row + 1, - column=0, - columnspan=content_col_offset_for_star + 1, - sticky=tk.W + tk.E, - padx=5, - ) - combo.bind("<>", lambda e: self.on_value_changed()) - self.widgets[tuple(path + [key])] = var - widget_type = "provider" - # print(f"创建了下拉菜单,选项: {providers}") - else: - # 如果没有可用的provider,使用普通文本框 - # print(f"没有可用的provider,使用普通文本框") - entry = ttk.Entry(frame, textvariable=var, font=("微软雅黑", 16)) - entry.grid( - row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5 - ) - var.trace_add("write", lambda *args: self.on_value_changed()) - self.widgets[tuple(path + [key])] = var - widget_type = "text" - else: - # 普通文本框 - entry = ttk.Entry(frame, textvariable=var, font=("微软雅黑", 16)) - entry.grid( - row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5 - ) - var.trace_add("write", lambda *args: self.on_value_changed()) - self.widgets[tuple(path + [key])] = var - widget_type = "text" - - def create_section_widgets(self, parent: ttk.Frame, section: str, data: Dict, path=None) -> None: - """为配置节创建编辑控件""" - if path is None: - path = [section] - # section完整路径 - full_section_path = ".".join(path) - # 获取节的中文名称和描述 - section_translations = { - "model.utils": "工具模型", - "model.utils_small": "小型工具模型", - "model.memory_summary": "记忆概括模型", - "model.vlm": "图像识别模型", - "model.embedding": "嵌入模型", - "model.replyer_1": "主要聊天模型", - "model.replyer_2": "次要聊天模型", - "model.focus_working_memory": "工作记忆模型", - "model.focus_tool_use": "工具调用模型", - "model.focus_planner": "决策模型", - } - section_trans = self.translations.get("sections", {}).get(full_section_path, {}) - section_name = section_trans.get("name") or section_translations.get(full_section_path) or section - section_desc = section_trans.get("description", "") - - # 创建节的标签框架 - section_frame = ttk.Frame(parent) - section_frame.pack(fill=tk.X, padx=5, pady=10) - - # 创建节的名称标签 - section_label = ttk.Label(section_frame, text=f"[{section_name}]", font=("微软雅黑", 18, "bold")) - section_label.pack(side=tk.LEFT, padx=5) - - # 创建节的描述标签 - if isinstance(section_trans.get("description"), dict): - # 如果是多语言描述,优先取en,否则取第一个 - desc_en = section_trans["description"].get("en") or next(iter(section_trans["description"].values()), "") - desc_label = ttk.Label(section_frame, text=desc_en, foreground="gray", font=("微软雅黑", 10)) - else: - desc_label = ttk.Label(section_frame, text=section_desc, foreground="gray", font=("微软雅黑", 10)) - desc_label.pack(side=tk.LEFT, padx=5) - - # 为每个配置项创建对应的控件 - for key, value in data.items(): - if isinstance(value, dict): - self.create_section_widgets(parent, key, value, path + [key]) - else: - self.create_widget_for_value(parent, key, value, path) - - def on_value_changed(self): - """当值改变时触发自动保存""" - self.pending_save = True - current_time = time.time() - if current_time - self.last_save_time > self.save_delay: - if self.save_timer: - self.root.after_cancel(self.save_timer) - self.save_timer = self.root.after(int(self.save_delay * 1000), self.save_config) - - def on_section_select(self, event): - # 如果有待保存的更改,先保存 - if self.pending_save: - self.save_config() - - selection = self.tree.selection() - if not selection: - return - - section = self.tree.item(selection[0])["values"][0] # 使用values中的原始节名 - self.current_section = section - - # 清空编辑器 - for widget in self.content_frame.winfo_children(): - widget.destroy() - - # 清空控件字典 - self.widgets.clear() - - # 创建编辑控件 - if section == "quick_settings": - self.create_quick_settings_widgets() - elif section == "env_vars": - self.create_env_vars_section(self.content_frame) - elif section in self.config: - self.create_section_widgets(self.content_frame, section, self.config[section]) - - def create_quick_settings_widgets(self): - """创建快捷设置编辑界面""" - # 获取快捷设置配置 - quick_settings = self.editor_config.get("editor", {}).get("quick_settings", {}).get("items", []) - - # 创建快捷设置控件 - for setting in quick_settings: - frame = ttk.Frame(self.content_frame) - frame.pack(fill=tk.X, padx=5, pady=2) - - # 获取当前值 - path = setting["path"].split(".") - current = self.config - for key in path[:-1]: # 除了最后一个键 - current = current.get(key, {}) - value = current.get(path[-1]) # 获取最后一个键的值 - - # 创建名称标签(加粗) - name_label = ttk.Label(frame, text=setting["name"], font=("微软雅黑", 16, "bold")) - name_label.pack(fill=tk.X, padx=5, pady=(2, 0)) - - # 创建描述标签 - if setting.get("description"): - desc_label = ttk.Label(frame, text=setting["description"], foreground="gray", font=("微软雅黑", 10)) - desc_label.pack(fill=tk.X, padx=5, pady=(0, 2)) - - # 根据类型创建不同的控件 - setting_type = setting.get("type", "bool") - - if setting_type == "bool": - value = bool(value) if value is not None else False - var = tk.BooleanVar(value=value) - checkbox = ttk.Checkbutton( - frame, text="", variable=var, command=lambda p=path, v=var: self.on_quick_setting_changed(p, v) - ) - checkbox.pack(anchor=tk.W, padx=5, pady=(0, 5)) - - elif setting_type == "text": - value = str(value) if value is not None else "" - var = tk.StringVar(value=value) - entry = ttk.Entry(frame, textvariable=var, width=40, font=("微软雅黑", 12)) - entry.pack(fill=tk.X, padx=5, pady=(0, 5)) - var.trace_add("write", lambda *args, p=path, v=var: self.on_quick_setting_changed(p, v)) - - elif setting_type == "number": - value = str(value) if value is not None else "0" - var = tk.StringVar(value=value) - entry = ttk.Entry(frame, textvariable=var, width=10, font=("微软雅黑", 12)) - entry.pack(fill=tk.X, padx=5, pady=(0, 5)) - var.trace_add("write", lambda *args, p=path, v=var: self.on_quick_setting_changed(p, v)) - - elif setting_type == "list": - # 对于列表类型,创建一个按钮来打开编辑窗口 - button = ttk.Button( - frame, text="编辑列表", command=lambda p=path, s=setting: self.open_list_editor(p, s) - ) - button.pack(anchor=tk.W, padx=5, pady=(0, 5)) - - def create_list_item(self, parent, value, index, entry_vars, path): - """创建单个列表项的输入框""" - item_frame = ttk.Frame(parent) - item_frame.pack(fill=tk.X, pady=1) - - # 创建输入框 - var = tk.StringVar(value=str(value)) - entry = ttk.Entry(item_frame, textvariable=var) - entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) - var.trace_add("write", lambda *args: self.on_value_changed()) - - # 创建删除按钮 - del_button = ttk.Button( - item_frame, - text="-", - width=3, - command=lambda: self.remove_list_item(parent, item_frame, entry_vars, index, path), - ) - del_button.pack(side=tk.RIGHT, padx=5) - - # 存储变量引用 - entry_vars.append(var) - - def add_list_item(self, parent, path): - """添加新的列表项""" - items_frame = parent.winfo_children()[1] # 获取列表项框架 - entry_vars = self.widgets[tuple(path)][1] # 获取变量列表 - - # 创建新的列表项 - self.create_list_item(items_frame, "", len(entry_vars), entry_vars, path) - self.on_value_changed() - - def remove_list_item(self, parent, item_frame, entry_vars, index, path): - """删除列表项""" - item_frame.destroy() - entry_vars.pop(index) - self.on_value_changed() - - def get_widget_value(self, widget) -> Any: - """获取控件的值""" - if isinstance(widget, tk.BooleanVar): - return widget.get() - elif isinstance(widget, tk.StringVar): - value = widget.get() - try: - # 尝试转换为数字 - if "." in value: - return float(value) - return int(value) - except ValueError: - return value - elif isinstance(widget, tuple): # 列表类型 - items_frame, entry_vars = widget - # 获取所有非空输入框的值 - return [var.get() for var in entry_vars if var.get().strip()] - return None - - def save_config(self): - """保存配置到文件""" - if not self.pending_save: - return - - with self.save_lock: - try: - # 获取所有控件的值 - for path, widget in self.widgets.items(): - # 跳过 env_vars 的控件赋值(只用于.env,不写回config) - if len(path) >= 2 and path[0] == "env_vars": - continue - value = self.get_widget_value(widget) - current = self.config - for key in path[:-1]: - current = current[key] - final_key = path[-1] - current[final_key] = value - - # === 只保存 TOML,不包含 env_vars === - env_vars = self.config.pop("env_vars", None) - with open(self.config_path, "wb") as f: - tomli_w.dump(self.config, f) - if env_vars is not None: - self.config["env_vars"] = env_vars - - # === 保存 env_vars 到 .env 文件(只覆盖特定key,其他内容保留) === - env_path = self.editor_config["config"].get("env_file", ".env") - if not os.path.isabs(env_path): - env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), env_path) - # 1. 读取原有.env内容 - old_lines = [] - if os.path.exists(env_path): - with open(env_path, "r", encoding="utf-8") as f: - old_lines = f.readlines() - # 2. 收集所有目标key的新值(直接从widgets取) - new_env_dict = {} - for path, widget in self.widgets.items(): - if len(path) == 2 and path[0] == "env_vars": - k = path[1] - if k.endswith("_BASE_URL") or k.endswith("_KEY"): - new_env_dict[k] = self.get_widget_value(widget) - # 3. 遍历原有行,替换目标key,保留所有其他内容 - result_lines = [] - found_keys = set() - for line in old_lines: - if "=" in line and not line.strip().startswith("#"): - k = line.split("=", 1)[0].strip() - if k in new_env_dict: - result_lines.append(f"{k}={new_env_dict[k]}\n") - found_keys.add(k) - else: - result_lines.append(line) - else: - result_lines.append(line) - # 4. 新key如果原.env没有,则追加 - for k, v in new_env_dict.items(): - if k not in found_keys: - result_lines.append(f"{k}={v}\n") - # 5. 写回.env - with open(env_path, "w", encoding="utf-8") as f: - f.writelines(result_lines) - # === 结束 === - - # === 保存完 .env 后,同步 widgets 的值回 self.config['env_vars'] === - for path, widget in self.widgets.items(): - if len(path) == 2 and path[0] == "env_vars": - prefix_key = path[1] - if prefix_key.endswith("_BASE_URL") or prefix_key.endswith("_KEY"): - prefix = prefix_key[:-9] if prefix_key.endswith("_BASE_URL") else prefix_key[:-4] - if "env_vars" not in self.config: - self.config["env_vars"] = {} - if prefix not in self.config["env_vars"]: - self.config["env_vars"][prefix] = {} - self.config["env_vars"][prefix][prefix_key] = self.get_widget_value(widget) - - self.last_save_time = time.time() - self.pending_save = False - except Exception as e: - messagebox.showerror("错误", f"保存配置失败: {str(e)}") - - def refresh_config(self): - # 如果有待保存的更改,先保存 - if self.pending_save: - self.save_config() - - self.load_config() - self.tree.delete(*self.tree.get_children()) - for section in self.config: - # 获取节的中文名称 - section_trans = self.translations.get("sections", {}).get(section, {}) - section_name = section_trans.get("name", section) - self.tree.insert("", "end", text=section_name, values=(section,)) - messagebox.showinfo("成功", "配置已刷新") - - def open_list_editor(self, path, setting): - """打开列表编辑窗口""" - # 创建新窗口 - dialog = tk.Toplevel(self.root) - dialog.title(f"编辑 {setting['name']}") - dialog.geometry("400x300") - - # 获取当前值 - current = self.config - for key in path[:-1]: - current = current.get(key, {}) - value = current.get(path[-1], []) - - # 创建编辑区 - frame = ttk.Frame(dialog, padding="10") - frame.pack(fill=tk.BOTH, expand=True) - - # 创建列表项框架 - items_frame = ttk.Frame(frame) - items_frame.pack(fill=tk.BOTH, expand=True) - - # 存储所有输入框的变量 - entry_vars = [] - - # 为每个列表项创建输入框 - for i, item in enumerate(value): - self.create_list_item(items_frame, item, i, entry_vars, path) - - # 创建按钮框架 - button_frame = ttk.Frame(frame) - button_frame.pack(fill=tk.X, pady=10) - - # 添加按钮 - add_button = ttk.Button(button_frame, text="添加", command=lambda: self.add_list_item(items_frame, path)) - add_button.pack(side=tk.LEFT, padx=5) - - # 保存按钮 - save_button = ttk.Button( - button_frame, text="保存", command=lambda: self.save_list_editor(dialog, path, entry_vars) - ) - save_button.pack(side=tk.RIGHT, padx=5) - - def save_list_editor(self, dialog, path, entry_vars): - """保存列表编辑窗口的内容""" - # 获取所有非空输入框的值 - values = [var.get() for var in entry_vars if var.get().strip()] - - # 更新配置 - current = self.config - for key in path[:-1]: - if key not in current: - current[key] = {} - current = current[key] - current[path[-1]] = values - - # 触发保存 - self.on_value_changed() - - # 关闭窗口 - dialog.destroy() - - def on_quick_setting_changed(self, path, var): - """快捷设置值改变时的处理""" - # 更新配置 - current = self.config - for key in path[:-1]: - if key not in current: - current[key] = {} - current = current[key] - # 根据变量类型设置值 - if isinstance(var, tk.BooleanVar): - current[path[-1]] = var.get() - elif isinstance(var, tk.StringVar): - value = var.get() - try: - # 尝试转换为数字 - if "." in value: - current[path[-1]] = float(value) - else: - current[path[-1]] = int(value) - except ValueError: - current[path[-1]] = value - # 触发保存 - self.on_value_changed() - - def toggle_quick_setting(self, full_path, widget_type, name, desc, already_in_quick): - quick_settings = ( - self.editor_config.setdefault("editor", {}).setdefault("quick_settings", {}).setdefault("items", []) - ) - if already_in_quick: - # 移除 - self.editor_config["editor"]["quick_settings"]["items"] = [ - item for item in quick_settings if item.get("path") != full_path - ] - else: - # 添加 - quick_settings.append({"name": name, "description": desc, "path": full_path, "type": widget_type}) - # 保存到configexe.toml - import tomli_w - import os - - config_path = os.path.join(os.path.dirname(__file__), "configexe.toml") - with open(config_path, "wb") as f: - tomli_w.dump(self.editor_config, f) - self.refresh_quick_settings() - - def refresh_quick_settings(self): - # 重新渲染快捷设置栏(如果当前在快捷设置页) - if self.current_section == "quick_settings": - for widget in self.content_frame.winfo_children(): - widget.destroy() - self.widgets.clear() - self.create_quick_settings_widgets() - - def create_env_var_group(self, parent: ttk.Frame, prefix: str, values: Dict[str, str], path: List[str]) -> None: - """创建环境变量组""" - frame = ttk.Frame(parent) - frame.pack(fill=tk.X, padx=5, pady=2) - - # 创建组标题 - title_frame = ttk.Frame(frame) - title_frame.pack(fill=tk.X, pady=(5, 0)) - - title_label = ttk.Label(title_frame, text=f"API配置组: {prefix}", font=("微软雅黑", 16, "bold")) - title_label.pack(side=tk.LEFT, padx=5) - - # 删除按钮 - del_button = ttk.Button(title_frame, text="删除组", command=lambda: self.delete_env_var_group(prefix)) - del_button.pack(side=tk.RIGHT, padx=5) - - # 创建BASE_URL输入框 - base_url_frame = ttk.Frame(frame) - base_url_frame.pack(fill=tk.X, padx=5, pady=2) - - base_url_label = ttk.Label(base_url_frame, text="BASE_URL:", font=("微软雅黑", 12)) - base_url_label.pack(side=tk.LEFT, padx=5) - - base_url_var = tk.StringVar(value=values.get(f"{prefix}_BASE_URL", "")) - base_url_entry = ttk.Entry(base_url_frame, textvariable=base_url_var, font=("微软雅黑", 12)) - base_url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) - base_url_var.trace_add("write", lambda *args: self.on_value_changed()) - - # 创建KEY输入框 - key_frame = ttk.Frame(frame) - key_frame.pack(fill=tk.X, padx=5, pady=2) - - key_label = ttk.Label(key_frame, text="API KEY:", font=("微软雅黑", 12)) - key_label.pack(side=tk.LEFT, padx=5) - - key_var = tk.StringVar(value=values.get(f"{prefix}_KEY", "")) - key_entry = ttk.Entry(key_frame, textvariable=key_var, font=("微软雅黑", 12)) - key_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) - key_var.trace_add("write", lambda *args: self.on_value_changed()) - - # 存储变量引用 - self.widgets[tuple(path + [f"{prefix}_BASE_URL"])] = base_url_var - self.widgets[tuple(path + [f"{prefix}_KEY"])] = key_var - - # 添加分隔线 - separator = ttk.Separator(frame, orient="horizontal") - separator.pack(fill=tk.X, pady=5) - - def create_env_vars_section(self, parent: ttk.Frame) -> None: - """创建环境变量编辑区""" - # 创建添加新组的按钮 - add_button = ttk.Button(parent, text="添加新的API配置组", command=self.add_new_env_var_group) - add_button.pack(pady=10) - - # 创建现有组的编辑区 - if "env_vars" in self.config: - for prefix, values in self.config["env_vars"].items(): - self.create_env_var_group(parent, prefix, values, ["env_vars"]) - - def add_new_env_var_group(self): - """添加新的环境变量组""" - # 创建新窗口 - dialog = tk.Toplevel(self.root) - dialog.title("添加新的API配置组") - dialog.geometry("400x200") - - # 创建输入框架 - frame = ttk.Frame(dialog, padding="10") - frame.pack(fill=tk.BOTH, expand=True) - - # 前缀输入 - prefix_label = ttk.Label(frame, text="API前缀名称:", font=("微软雅黑", 12)) - prefix_label.pack(pady=5) - - prefix_var = tk.StringVar() - prefix_entry = ttk.Entry(frame, textvariable=prefix_var, font=("微软雅黑", 12)) - prefix_entry.pack(fill=tk.X, pady=5) - - # 确认按钮 - def on_confirm(): - prefix = prefix_var.get().strip() - if prefix: - if "env_vars" not in self.config: - self.config["env_vars"] = {} - self.config["env_vars"][prefix] = {f"{prefix}_BASE_URL": "", f"{prefix}_KEY": ""} - # 刷新显示 - self.refresh_env_vars_section() - self.on_value_changed() - dialog.destroy() - - confirm_button = ttk.Button(frame, text="确认", command=on_confirm) - confirm_button.pack(pady=10) - - def delete_env_var_group(self, prefix: str): - """删除环境变量组""" - if messagebox.askyesno("确认", f"确定要删除 {prefix} 配置组吗?"): - if "env_vars" in self.config: - del self.config["env_vars"][prefix] - # 刷新显示 - self.refresh_env_vars_section() - self.on_value_changed() - - def refresh_env_vars_section(self): - """刷新环境变量编辑区""" - # 清空当前显示 - for widget in self.content_frame.winfo_children(): - widget.destroy() - self.widgets.clear() - - # 重新创建编辑区 - self.create_env_vars_section(self.content_frame) - - def open_advanced_options(self): - """弹窗显示高级配置""" - dialog = tk.Toplevel(self.root) - dialog.title("高级选项") - dialog.geometry("700x800") - - notebook = ttk.Notebook(dialog) - notebook.pack(fill=tk.BOTH, expand=True) - - # 遥测栏 - if "telemetry" in self.config: - telemetry_frame = ttk.Frame(notebook) - notebook.add(telemetry_frame, text="遥测") - self.create_section_widgets(telemetry_frame, "telemetry", self.config["telemetry"], ["telemetry"]) - # 实验性功能栏 - if "experimental" in self.config: - exp_frame = ttk.Frame(notebook) - notebook.add(exp_frame, text="实验性功能") - self.create_section_widgets(exp_frame, "experimental", self.config["experimental"], ["experimental"]) - # 消息服务栏 - if "maim_message" in self.config: - msg_frame = ttk.Frame(notebook) - notebook.add(msg_frame, text="消息服务") - self.create_section_widgets(msg_frame, "maim_message", self.config["maim_message"], ["maim_message"]) - # 消息接收栏 - if "message_receive" in self.config: - recv_frame = ttk.Frame(notebook) - notebook.add(recv_frame, text="消息接收") - self.create_section_widgets( - recv_frame, "message_receive", self.config["message_receive"], ["message_receive"] - ) - # 关系栏 - if "relationship" in self.config: - rel_frame = ttk.Frame(notebook) - notebook.add(rel_frame, text="关系") - self.create_section_widgets(rel_frame, "relationship", self.config["relationship"], ["relationship"]) - - def open_path_config(self): - """打开路径配置对话框""" - dialog = tk.Toplevel(self.root) - dialog.title("配置路径") - dialog.geometry("600x200") - - # 创建输入框架 - frame = ttk.Frame(dialog, padding="10") - frame.pack(fill=tk.BOTH, expand=True) - - # bot_config.toml路径配置 - bot_config_frame = ttk.Frame(frame) - bot_config_frame.pack(fill=tk.X, pady=5) - - bot_config_label = ttk.Label(bot_config_frame, text="bot_config.toml路径:", font=("微软雅黑", 12)) - bot_config_label.pack(side=tk.LEFT, padx=5) - - bot_config_var = tk.StringVar(value=self.config_path) - bot_config_entry = ttk.Entry(bot_config_frame, textvariable=bot_config_var, font=("微软雅黑", 12)) - bot_config_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) - - def apply_config(): - new_bot_config_path = bot_config_var.get().strip() - new_env_path = env_var.get().strip() - - if not new_bot_config_path or not new_env_path: - messagebox.showerror("错误", "路径不能为空") - return - - if not os.path.exists(new_bot_config_path): - messagebox.showerror("错误", "bot_config.toml文件不存在") - return - - # 更新配置 - self.config_path = new_bot_config_path - self.editor_config["config"]["bot_config_path"] = new_bot_config_path - self.editor_config["config"]["env_file"] = new_env_path - - # 保存编辑器配置 - config_path = os.path.join(os.path.dirname(__file__), "configexe.toml") - with open(config_path, "wb") as f: - tomli_w.dump(self.editor_config, f) - - # 重新加载配置 - self.load_config() - self.load_env_vars() - - # 刷新显示 - self.refresh_config() - - messagebox.showinfo("成功", "路径配置已更新,程序将重新启动") - dialog.destroy() - - # 重启程序 - self.root.quit() - os.execv(sys.executable, ["python"] + sys.argv) - - def browse_bot_config(): - file_path = filedialog.askopenfilename( - title="选择bot_config.toml文件", filetypes=[("TOML文件", "*.toml"), ("所有文件", "*.*")] - ) - if file_path: - bot_config_var.set(file_path) - apply_config() - - browse_bot_config_btn = ttk.Button(bot_config_frame, text="浏览", command=browse_bot_config) - browse_bot_config_btn.pack(side=tk.LEFT, padx=5) - - # .env路径配置 - env_frame = ttk.Frame(frame) - env_frame.pack(fill=tk.X, pady=5) - - env_label = ttk.Label(env_frame, text=".env路径:", font=("微软雅黑", 12)) - env_label.pack(side=tk.LEFT, padx=5) - - env_path = self.editor_config["config"].get("env_file", ".env") - if not os.path.isabs(env_path): - env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), env_path) - env_var = tk.StringVar(value=env_path) - env_entry = ttk.Entry(env_frame, textvariable=env_var, font=("微软雅黑", 12)) - env_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) - - def browse_env(): - file_path = filedialog.askopenfilename( - title="选择.env文件", filetypes=[("环境变量文件", "*.env"), ("所有文件", "*.*")] - ) - if file_path: - env_var.set(file_path) - apply_config() - - browse_env_btn = ttk.Button(env_frame, text="浏览", command=browse_env) - browse_env_btn.pack(side=tk.LEFT, padx=5) - - -def main(): - root = tk.Tk() - _app = ConfigEditor(root) - root.mainloop() - - -if __name__ == "__main__": - main() diff --git a/scripts/cleanup_expressions.py b/scripts/cleanup_expressions.py deleted file mode 100644 index 3d7ba1b55..000000000 --- a/scripts/cleanup_expressions.py +++ /dev/null @@ -1,125 +0,0 @@ -import os -import json -import random -from typing import List, Dict, Tuple -import glob - -MAX_EXPRESSION_COUNT = 300 # 每个群最多保留的表达方式数量 -MIN_COUNT_THRESHOLD = 0.01 # 最小使用次数阈值 - - -def load_expressions(chat_id: str) -> Tuple[List[Dict], List[Dict]]: - """加载指定群聊的表达方式""" - style_file = os.path.join("data", "expression", "learnt_style", str(chat_id), "expressions.json") - grammar_file = os.path.join("data", "expression", "learnt_grammar", str(chat_id), "expressions.json") - - style_exprs = [] - grammar_exprs = [] - - if os.path.exists(style_file): - with open(style_file, "r", encoding="utf-8") as f: - style_exprs = json.load(f) - - if os.path.exists(grammar_file): - with open(grammar_file, "r", encoding="utf-8") as f: - grammar_exprs = json.load(f) - - return style_exprs, grammar_exprs - - -def save_expressions(chat_id: str, style_exprs: List[Dict], grammar_exprs: List[Dict]) -> None: - """保存表达方式到文件""" - style_file = os.path.join("data", "expression", "learnt_style", str(chat_id), "expressions.json") - grammar_file = os.path.join("data", "expression", "learnt_grammar", str(chat_id), "expressions.json") - - os.makedirs(os.path.dirname(style_file), exist_ok=True) - os.makedirs(os.path.dirname(grammar_file), exist_ok=True) - - with open(style_file, "w", encoding="utf-8") as f: - json.dump(style_exprs, f, ensure_ascii=False, indent=2) - - with open(grammar_file, "w", encoding="utf-8") as f: - json.dump(grammar_exprs, f, ensure_ascii=False, indent=2) - - -def cleanup_expressions(expressions: List[Dict]) -> List[Dict]: - """清理表达方式列表""" - if not expressions: - return [] - - # 1. 移除使用次数过低的表达方式 - expressions = [expr for expr in expressions if expr.get("count", 0) > MIN_COUNT_THRESHOLD] - - # 2. 如果数量超过限制,随机删除多余的 - if len(expressions) > MAX_EXPRESSION_COUNT: - # 按使用次数排序 - expressions.sort(key=lambda x: x.get("count", 0), reverse=True) - - # 保留前50%的高频表达方式 - keep_count = MAX_EXPRESSION_COUNT // 2 - keep_exprs = expressions[:keep_count] - - # 从剩余的表达方式中随机选择 - remaining_exprs = expressions[keep_count:] - random.shuffle(remaining_exprs) - keep_exprs.extend(remaining_exprs[: MAX_EXPRESSION_COUNT - keep_count]) - - expressions = keep_exprs - - return expressions - - -def main(): - # 获取所有群聊ID - style_dirs = glob.glob(os.path.join("data", "expression", "learnt_style", "*")) - chat_ids = [os.path.basename(d) for d in style_dirs] - - if not chat_ids: - print("没有找到任何群聊的表达方式数据") - return - - print(f"开始清理 {len(chat_ids)} 个群聊的表达方式数据...") - - total_style_before = 0 - total_style_after = 0 - total_grammar_before = 0 - total_grammar_after = 0 - - for chat_id in chat_ids: - print(f"\n处理群聊 {chat_id}:") - - # 加载表达方式 - style_exprs, grammar_exprs = load_expressions(chat_id) - - # 记录清理前的数量 - style_count_before = len(style_exprs) - grammar_count_before = len(grammar_exprs) - total_style_before += style_count_before - total_grammar_before += grammar_count_before - - # 清理表达方式 - style_exprs = cleanup_expressions(style_exprs) - grammar_exprs = cleanup_expressions(grammar_exprs) - - # 记录清理后的数量 - style_count_after = len(style_exprs) - grammar_count_after = len(grammar_exprs) - total_style_after += style_count_after - total_grammar_after += grammar_count_after - - # 保存清理后的表达方式 - save_expressions(chat_id, style_exprs, grammar_exprs) - - print(f"语言风格: {style_count_before} -> {style_count_after}") - print(f"句法特点: {grammar_count_before} -> {grammar_count_after}") - - print("\n清理完成!") - print(f"语言风格总数: {total_style_before} -> {total_style_after}") - print(f"句法特点总数: {total_grammar_before} -> {total_grammar_after}") - print( - f"总共清理了 {total_style_before + total_grammar_before - total_style_after - total_grammar_after} 条表达方式" - ) - - -if __name__ == "__main__": - main() diff --git a/scripts/configexe.toml b/scripts/configexe.toml deleted file mode 100644 index c2a39fac6..000000000 --- a/scripts/configexe.toml +++ /dev/null @@ -1,532 +0,0 @@ -[config] -bot_config_path = "C:/GitHub/MaiBot-Core/config/bot_config.toml" -env_path = "env.toml" -env_file = "c:\\GitHub\\MaiBot-Core\\.env" - -[editor] -window_width = 1000 -window_height = 800 -save_delay = 1.0 - -[[editor.quick_settings.items]] -name = "核心性格" -description = "麦麦的核心性格描述,建议50字以内" -path = "personality.personality_core" -type = "text" - -[[editor.quick_settings.items]] -name = "性格细节" -description = "麦麦性格的细节描述,条数任意,不能为0" -path = "personality.personality_sides" -type = "list" - -[[editor.quick_settings.items]] -name = "身份细节" -description = "麦麦的身份特征描述,可以描述外貌、性别、身高、职业、属性等" -path = "identity.identity_detail" -type = "list" - -[[editor.quick_settings.items]] -name = "表达风格" -description = "麦麦说话的表达风格,表达习惯" -path = "expression.expression_style" -type = "text" - -[[editor.quick_settings.items]] -name = "聊天模式" -description = "麦麦的聊天模式:normal(普通模式)、focus(专注模式)、auto(自动模式)" -path = "chat.chat_mode" -type = "text" - -[[editor.quick_settings.items]] -name = "回复频率(normal模式)" -description = "麦麦回复频率,一般为1,默认频率下,30分钟麦麦回复30条(约数)" -path = "normal_chat.talk_frequency" -type = "number" - -[[editor.quick_settings.items]] -name = "自动专注阈值(auto模式)" -description = "自动切换到专注聊天的阈值,越低越容易进入专注聊天" -path = "chat.auto_focus_threshold" -type = "number" - -[[editor.quick_settings.items]] -name = "退出专注阈值(auto模式)" -description = "自动退出专注聊天的阈值,越低越容易退出专注聊天" -path = "chat.exit_focus_threshold" -type = "number" - -[[editor.quick_settings.items]] -name = "思考间隔(focus模式)" -description = "思考的时间间隔(秒),可以有效减少消耗" -path = "focus_chat.think_interval" -type = "number" - -[[editor.quick_settings.items]] -name = "连续回复能力(focus模式)" -description = "连续回复能力,值越高,麦麦连续回复的概率越高" -path = "focus_chat.consecutive_replies" -type = "number" - -[[editor.quick_settings.items]] -name = "自我识别处理器(focus模式)" -description = "是否启用自我识别处理器" -path = "focus_chat_processor.self_identify_processor" -type = "bool" - -[[editor.quick_settings.items]] -name = "工具使用处理器(focus模式)" -description = "是否启用工具使用处理器" -path = "focus_chat_processor.tool_use_processor" -type = "bool" - -[[editor.quick_settings.items]] -name = "工作记忆处理器(focus模式)" -description = "是否启用工作记忆处理器,不稳定,消耗量大" -path = "focus_chat_processor.working_memory_processor" -type = "bool" - -[[editor.quick_settings.items]] -name = "显示聊天模式(debug模式)" -description = "是否在回复后显示当前聊天模式" -path = "experimental.debug_show_chat_mode" -type = "bool" - - - -[translations.sections.inner] -name = "版本" -description = "麦麦的内部配置,包含版本号等信息。此部分仅供显示,不可编辑。" - -[translations.sections.bot] -name = "麦麦bot配置" -description = "麦麦的基本配置,包括QQ号、昵称和别名等基础信息" - -[translations.sections.personality] -name = "人格" -description = "麦麦的性格设定,包括核心性格(建议50字以内)和细节描述" - -[translations.sections.identity] -name = "身份特点" -description = "麦麦的身份特征,包括年龄、性别、外貌等描述,可以描述外貌、性别、身高、职业、属性等" - -[translations.sections.expression] -name = "表达方式" -description = "麦麦的表达方式和学习设置,包括表达风格和表达学习功能" - -[translations.sections.relationship] -name = "关系" -description = "麦麦与用户的关系设置,包括取名功能等" - -[translations.sections.chat] -name = "聊天模式" -description = "麦麦的聊天模式和行为设置,包括普通模式、专注模式和自动模式" - -[translations.sections.message_receive] -name = "消息接收" -description = "消息过滤和接收设置,可以根据规则过滤特定消息" - -[translations.sections.normal_chat] -name = "普通聊天配置" -description = "普通聊天模式下的行为设置,包括回复概率、上下文长度、表情包使用等" - -[translations.sections.focus_chat] -name = "专注聊天配置" -description = "专注聊天模式下的行为设置,包括思考间隔、上下文大小等" - -[translations.sections.focus_chat_processor] -name = "专注聊天处理器" -description = "专注聊天模式下的处理器设置,包括自我识别、工具使用、工作记忆等功能" - -[translations.sections.emoji] -name = "表情包" -description = "表情包相关的设置,包括最大注册数量、替换策略、检查间隔等" - -[translations.sections.memory] -name = "记忆" -description = "麦麦的记忆系统设置,包括记忆构建、遗忘、整合等参数" - -[translations.sections.mood] -name = "情绪" -description = "麦麦的情绪系统设置,仅在普通聊天模式下有效" - -[translations.sections.keyword_reaction] -name = "关键词反应" -description = "针对特定关键词作出反应的设置,仅在普通聊天模式下有效" - -[translations.sections.chinese_typo] -name = "错别字生成器" -description = "中文错别字生成器的设置,可以控制错别字生成的概率" - -[translations.sections.response_splitter] -name = "回复分割器" -description = "回复分割器的设置,用于控制回复的长度和句子数量" - -[translations.sections.model] -name = "模型" -description = "各种AI模型的设置,包括组件模型、普通聊天模型、专注聊天模型等" - -[translations.sections.maim_message] -name = "消息服务" -description = "消息服务的设置,包括认证令牌、服务器配置等" - -[translations.sections.telemetry] -name = "遥测" -description = "统计信息发送设置,用于统计全球麦麦的数量" - -[translations.sections.experimental] -name = "实验功能" -description = "实验性功能的设置,包括调试显示、好友聊天等功能" - -[translations.items.version] -name = "版本号" -description = "麦麦的版本号,格式:主版本号.次版本号.修订号。主版本号用于不兼容的API修改,次版本号用于向下兼容的功能性新增,修订号用于向下兼容的问题修正" - -[translations.items.qq_account] -name = "QQ账号" -description = "麦麦的QQ账号" - -[translations.items.nickname] -name = "昵称" -description = "麦麦的昵称" - -[translations.items.alias_names] -name = "别名" -description = "麦麦的其他称呼" - -[translations.items.personality_core] -name = "核心性格" -description = "麦麦的核心性格描述,建议50字以内" - -[translations.items.personality_sides] -name = "性格细节" -description = "麦麦性格的细节描述,条数任意,不能为0" - -[translations.items.identity_detail] -name = "身份细节" -description = "麦麦的身份特征描述,可以描述外貌、性别、身高、职业、属性等,条数任意,不能为0" - -[translations.items.expression_style] -name = "表达风格" -description = "麦麦说话的表达风格,表达习惯" - -[translations.items.enable_expression_learning] -name = "启用表达学习" -description = "是否启用表达学习功能,麦麦会学习人类说话风格" - -[translations.items.learning_interval] -name = "学习间隔" -description = "表达学习的间隔时间(秒)" - -[translations.items.give_name] -name = "取名功能" -description = "麦麦是否给其他人取名,关闭后无法使用禁言功能" - -[translations.items.chat_mode] -name = "聊天模式" -description = "麦麦的聊天模式:normal(普通模式,token消耗较低)、focus(专注模式,token消耗较高)、auto(自动模式,根据消息内容自动切换)" - -[translations.items.auto_focus_threshold] -name = "自动专注阈值" -description = "自动切换到专注聊天的阈值,越低越容易进入专注聊天" - -[translations.items.exit_focus_threshold] -name = "退出专注阈值" -description = "自动退出专注聊天的阈值,越低越容易退出专注聊天" - -[translations.items.ban_words] -name = "禁用词" -description = "需要过滤的词语列表" - -[translations.items.ban_msgs_regex] -name = "禁用消息正则" -description = "需要过滤的消息正则表达式,匹配到的消息将被过滤" - -[translations.items.normal_chat_first_probability] -name = "首要模型概率" -description = "麦麦回答时选择首要模型的概率(与之相对的,次要模型的概率为1 - normal_chat_first_probability)" - -[translations.items.max_context_size] -name = "最大上下文长度" -description = "聊天上下文的最大长度" - -[translations.items.emoji_chance] -name = "表情包概率" -description = "麦麦一般回复时使用表情包的概率,设置为1让麦麦自己决定发不发" - -[translations.items.thinking_timeout] -name = "思考超时" -description = "麦麦最长思考时间,超过这个时间的思考会放弃(往往是api反应太慢)" - -[translations.items.willing_mode] -name = "回复意愿模式" -description = "回复意愿的计算模式:经典模式(classical)、mxp模式(mxp)、自定义模式(custom)" - -[translations.items.talk_frequency] -name = "回复频率" -description = "麦麦回复频率,一般为1,默认频率下,30分钟麦麦回复30条(约数)" - -[translations.items.response_interested_rate_amplifier] -name = "兴趣度放大系数" -description = "麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数" - -[translations.items.emoji_response_penalty] -name = "表情包回复惩罚" -description = "表情包回复惩罚系数,设为0为不回复单个表情包,减少单独回复表情包的概率" - -[translations.items.mentioned_bot_inevitable_reply] -name = "提及必回" -description = "被提及时是否必然回复" - -[translations.items.at_bot_inevitable_reply] -name = "@必回" -description = "被@时是否必然回复" - -[translations.items.down_frequency_rate] -name = "降低频率系数" -description = "降低回复频率的群组回复意愿降低系数(除法)" - -[translations.items.talk_frequency_down_groups] -name = "降低频率群组" -description = "需要降低回复频率的群组列表" - -[translations.items.think_interval] -name = "思考间隔" -description = "思考的时间间隔(秒),可以有效减少消耗" - -[translations.items.consecutive_replies] -name = "连续回复能力" -description = "连续回复能力,值越高,麦麦连续回复的概率越高" - -[translations.items.parallel_processing] -name = "并行处理" -description = "是否并行处理回忆和处理器阶段,可以节省时间" - -[translations.items.processor_max_time] -name = "处理器最大时间" -description = "处理器最大时间,单位秒,如果超过这个时间,处理器会自动停止" - - -[translations.items.observation_context_size] -name = "观察上下文大小" -description = "观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖" - -[translations.items.compressed_length] -name = "压缩长度" -description = "不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度会压缩,最短压缩长度为5" - -[translations.items.compress_length_limit] -name = "压缩限制" -description = "最多压缩份数,超过该数值的压缩上下文会被删除" - -[translations.items.self_identify_processor] -name = "自我识别处理器" -description = "是否启用自我识别处理器" - -[translations.items.tool_use_processor] -name = "工具使用处理器" -description = "是否启用工具使用处理器" - -[translations.items.working_memory_processor] -name = "工作记忆处理器" -description = "是否启用工作记忆处理器,不稳定,消耗量大" - -[translations.items.max_reg_num] -name = "最大注册数" -description = "表情包最大注册数量" - -[translations.items.do_replace] -name = "启用替换" -description = "开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包" - -[translations.items.check_interval] -name = "检查间隔" -description = "检查表情包(注册,破损,删除)的时间间隔(分钟)" - -[translations.items.save_pic] -name = "保存图片" -description = "是否保存表情包图片" - -[translations.items.cache_emoji] -name = "缓存表情包" -description = "是否缓存表情包" - -[translations.items.steal_emoji] -name = "偷取表情包" -description = "是否偷取表情包,让麦麦可以发送她保存的这些表情包" - -[translations.items.content_filtration] -name = "内容过滤" -description = "是否启用表情包过滤,只有符合该要求的表情包才会被保存" - -[translations.items.filtration_prompt] -name = "过滤要求" -description = "表情包过滤要求,只有符合该要求的表情包才会被保存" - -[translations.items.memory_build_interval] -name = "记忆构建间隔" -description = "记忆构建间隔(秒),间隔越低,麦麦学习越多,但是冗余信息也会增多" - -[translations.items.memory_build_distribution] -name = "记忆构建分布" -description = "记忆构建分布,参数:分布1均值,标准差,权重,分布2均值,标准差,权重" - -[translations.items.memory_build_sample_num] -name = "采样数量" -description = "采样数量,数值越高记忆采样次数越多" - -[translations.items.memory_build_sample_length] -name = "采样长度" -description = "采样长度,数值越高一段记忆内容越丰富" - -[translations.items.memory_compress_rate] -name = "记忆压缩率" -description = "记忆压缩率,控制记忆精简程度,建议保持默认,调高可以获得更多信息,但是冗余信息也会增多" - -[translations.items.forget_memory_interval] -name = "记忆遗忘间隔" -description = "记忆遗忘间隔(秒),间隔越低,麦麦遗忘越频繁,记忆更精简,但更难学习" - -[translations.items.memory_forget_time] -name = "遗忘时间" -description = "多长时间后的记忆会被遗忘(小时)" - -[translations.items.memory_forget_percentage] -name = "遗忘比例" -description = "记忆遗忘比例,控制记忆遗忘程度,越大遗忘越多,建议保持默认" - -[translations.items.consolidate_memory_interval] -name = "记忆整合间隔" -description = "记忆整合间隔(秒),间隔越低,麦麦整合越频繁,记忆更精简" - -[translations.items.consolidation_similarity_threshold] -name = "整合相似度阈值" -description = "相似度阈值" - -[translations.items.consolidation_check_percentage] -name = "整合检查比例" -description = "检查节点比例" - -[translations.items.memory_ban_words] -name = "记忆禁用词" -description = "不希望记忆的词,已经记忆的不会受到影响" - -[translations.items.mood_update_interval] -name = "情绪更新间隔" -description = "情绪更新间隔(秒),仅在普通聊天模式下有效" - -[translations.items.mood_decay_rate] -name = "情绪衰减率" -description = "情绪衰减率" - -[translations.items.mood_intensity_factor] -name = "情绪强度因子" -description = "情绪强度因子" - -[translations.items.enable] -name = "启用关键词反应" -description = "关键词反应功能的总开关,仅在普通聊天模式下有效" - -[translations.items.chinese_typo_enable] -name = "启用错别字" -description = "是否启用中文错别字生成器" - -[translations.items.error_rate] -name = "错误率" -description = "单字替换概率" - -[translations.items.min_freq] -name = "最小字频" -description = "最小字频阈值" - -[translations.items.tone_error_rate] -name = "声调错误率" -description = "声调错误概率" - -[translations.items.word_replace_rate] -name = "整词替换率" -description = "整词替换概率" - -[translations.items.splitter_enable] -name = "启用分割器" -description = "是否启用回复分割器" - -[translations.items.max_length] -name = "最大长度" -description = "回复允许的最大长度" - -[translations.items.max_sentence_num] -name = "最大句子数" -description = "回复允许的最大句子数" - -[translations.items.enable_kaomoji_protection] -name = "启用颜文字保护" -description = "是否启用颜文字保护" - -[translations.items.model_max_output_length] -name = "最大输出长度" -description = "模型单次返回的最大token数" - -[translations.items.auth_token] -name = "认证令牌" -description = "用于API验证的令牌列表,为空则不启用验证" - -[translations.items.use_custom] -name = "使用自定义" -description = "是否启用自定义的maim_message服务器,注意这需要设置新的端口,不能与.env重复" - -[translations.items.host] -name = "主机地址" -description = "服务器主机地址" - -[translations.items.port] -name = "端口" -description = "服务器端口" - -[translations.items.mode] -name = "模式" -description = "连接模式:ws或tcp" - -[translations.items.use_wss] -name = "使用WSS" -description = "是否使用WSS安全连接,只支持ws模式" - -[translations.items.cert_file] -name = "证书文件" -description = "SSL证书文件路径,仅在use_wss=true时有效" - -[translations.items.key_file] -name = "密钥文件" -description = "SSL密钥文件路径,仅在use_wss=true时有效" - -[translations.items.telemetry_enable] -name = "启用遥测" -description = "是否发送统计信息,主要是看全球有多少只麦麦" - -[translations.items.debug_show_chat_mode] -name = "显示聊天模式" -description = "是否在回复后显示当前聊天模式" - -[translations.items.enable_friend_chat] -name = "启用好友聊天" -description = "是否启用好友聊天功能" - -[translations.items.pfc_chatting] -name = "PFC聊天" -description = "暂时无效" - -[translations.items."response_splitter.enable"] -name = "启用分割器" -description = "是否启用回复分割器" - -[translations.items."telemetry.enable"] -name = "启用遥测" -description = "是否发送统计信息,主要是看全球有多少只麦麦" - -[translations.items."chinese_typo.enable"] -name = "启用错别字" -description = "是否启用中文错别字生成器" - -[translations.items."keyword_reaction.enable"] -name = "启用关键词反应" -description = "关键词反应功能的总开关,仅在普通聊天模式下有效"