升级配置文件读取器
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk, messagebox
|
from tkinter import ttk, messagebox, filedialog
|
||||||
import tomli
|
import tomli
|
||||||
import tomli_w
|
import tomli_w
|
||||||
import os
|
import os
|
||||||
from typing import Any, Dict, List, Union
|
from typing import Any, Dict, List, Union
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import re
|
||||||
|
|
||||||
class ConfigEditor:
|
class ConfigEditor:
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
@@ -21,6 +22,9 @@ class ConfigEditor:
|
|||||||
# 加载配置
|
# 加载配置
|
||||||
self.load_config()
|
self.load_config()
|
||||||
|
|
||||||
|
# 加载环境变量
|
||||||
|
self.load_env_vars()
|
||||||
|
|
||||||
# 自动保存相关
|
# 自动保存相关
|
||||||
self.last_save_time = time.time()
|
self.last_save_time = time.time()
|
||||||
self.save_timer = None
|
self.save_timer = None
|
||||||
@@ -92,13 +96,61 @@ class ConfigEditor:
|
|||||||
messagebox.showerror("错误", f"加载配置文件失败: {str(e)}")
|
messagebox.showerror("错误", f"加载配置文件失败: {str(e)}")
|
||||||
self.config = {}
|
self.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):
|
def create_version_label(self):
|
||||||
"""创建版本号显示标签"""
|
"""创建版本号显示标签"""
|
||||||
version = self.config.get("inner", {}).get("version", "未知版本")
|
version = self.config.get("inner", {}).get("version", "未知版本")
|
||||||
version_frame = ttk.Frame(self.main_frame)
|
version_frame = ttk.Frame(self.main_frame)
|
||||||
version_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
version_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||||
|
|
||||||
version_label = ttk.Label(version_frame, text=f"麦麦版本:{version}", font=("", 10, "bold"))
|
# 添加配置按钮
|
||||||
|
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)
|
version_label.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
def create_navbar(self):
|
def create_navbar(self):
|
||||||
@@ -113,14 +165,15 @@ class ConfigEditor:
|
|||||||
# 添加快捷设置节
|
# 添加快捷设置节
|
||||||
self.tree.insert("", "end", text="快捷设置", values=("quick_settings",))
|
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:
|
for section in self.config:
|
||||||
if section != "inner": # 跳过inner部分
|
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_trans = self.translations.get("sections", {}).get(section, {})
|
||||||
section_name = section_trans.get("name", section)
|
section_name = section_trans.get("name", section)
|
||||||
self.tree.insert("", "end", text=section_name, values=(section,))
|
self.tree.insert("", "end", text=section_name, values=(section,))
|
||||||
|
|
||||||
# 绑定选择事件
|
# 绑定选择事件
|
||||||
self.tree.bind("<<TreeviewSelect>>", self.on_section_select)
|
self.tree.bind("<<TreeviewSelect>>", self.on_section_select)
|
||||||
|
|
||||||
@@ -130,8 +183,8 @@ class ConfigEditor:
|
|||||||
self.editor_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S))
|
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 = ttk.Label(self.editor_frame, text="")
|
||||||
self.editor_title.pack(fill=tk.X)
|
# self.editor_title.pack(fill=tk.X)
|
||||||
|
|
||||||
# 创建编辑区内容
|
# 创建编辑区内容
|
||||||
self.editor_content = ttk.Frame(self.editor_frame)
|
self.editor_content = ttk.Frame(self.editor_frame)
|
||||||
@@ -167,8 +220,12 @@ class ConfigEditor:
|
|||||||
self.button_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E))
|
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 = ttk.Button(self.button_frame, text="刷新", command=self.refresh_config)
|
||||||
self.refresh_button.pack(side=tk.RIGHT, padx=5)
|
# 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:
|
def create_widget_for_value(self, parent: ttk.Frame, key: str, value: Any, path: List[str]) -> None:
|
||||||
"""为不同类型的值创建对应的编辑控件"""
|
"""为不同类型的值创建对应的编辑控件"""
|
||||||
@@ -178,6 +235,14 @@ class ConfigEditor:
|
|||||||
# --- 修改开始: 改进翻译查找逻辑 ---
|
# --- 修改开始: 改进翻译查找逻辑 ---
|
||||||
full_config_path_key = ".".join(path + [key]) # 例如 "chinese_typo.enable"
|
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_name_to_display = key # 默认显示原始键名
|
||||||
item_desc_to_display = "" # 默认无描述
|
item_desc_to_display = "" # 默认无描述
|
||||||
|
|
||||||
@@ -192,10 +257,12 @@ class ConfigEditor:
|
|||||||
if generic_translation and generic_translation.get("name"):
|
if generic_translation and generic_translation.get("name"):
|
||||||
item_name_to_display = generic_translation.get("name")
|
item_name_to_display = generic_translation.get("name")
|
||||||
item_desc_to_display = generic_translation.get("description", "")
|
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=("", 20, "bold"))
|
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))
|
label.grid(row=0, column=0, sticky=tk.W, padx=5, pady=(0, 0))
|
||||||
|
|
||||||
# 星星图标快捷设置(与配置名同一行)
|
# 星星图标快捷设置(与配置名同一行)
|
||||||
@@ -210,11 +277,19 @@ class ConfigEditor:
|
|||||||
for widget in parent.winfo_children():
|
for widget in parent.winfo_children():
|
||||||
widget.destroy()
|
widget.destroy()
|
||||||
self.widgets.clear()
|
self.widgets.clear()
|
||||||
# 重新渲染本分组
|
# 判断parent是不是self.content_frame
|
||||||
if hasattr(self, 'current_section') and self.current_section and self.current_section != "quick_settings":
|
if parent == self.content_frame:
|
||||||
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":
|
if hasattr(self, 'current_section') and self.current_section and self.current_section != "quick_settings":
|
||||||
self.create_quick_settings_widgets() # 如果当前是快捷设置,也刷新它
|
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 = 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)
|
pin_btn.grid(row=0, column=content_col_offset_for_star, sticky=tk.W, padx=5)
|
||||||
@@ -228,7 +303,7 @@ class ConfigEditor:
|
|||||||
# 配置项描述(第二行)
|
# 配置项描述(第二行)
|
||||||
desc_row = 1
|
desc_row = 1
|
||||||
if item_desc_to_display:
|
if item_desc_to_display:
|
||||||
desc_label = ttk.Label(frame, text=item_desc_to_display, foreground="gray", font=("", 16))
|
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))
|
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 # 内容控件在描述下方
|
widget_row = desc_row + 1 # 内容控件在描述下方
|
||||||
else:
|
else:
|
||||||
@@ -236,7 +311,7 @@ class ConfigEditor:
|
|||||||
|
|
||||||
# 配置内容控件(第三行或第二行)
|
# 配置内容控件(第三行或第二行)
|
||||||
if path[0] == "inner":
|
if path[0] == "inner":
|
||||||
value_label = ttk.Label(frame, text=str(value), font=("", 20))
|
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)
|
value_label.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star +1, sticky=tk.W, padx=5)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -251,7 +326,7 @@ class ConfigEditor:
|
|||||||
elif isinstance(value, (int, float)):
|
elif isinstance(value, (int, float)):
|
||||||
# 数字使用数字输入框
|
# 数字使用数字输入框
|
||||||
var = tk.StringVar(value=str(value))
|
var = tk.StringVar(value=str(value))
|
||||||
entry = ttk.Entry(frame, textvariable=var, font=("", 20))
|
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)
|
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())
|
var.trace_add("write", lambda *args: self.on_value_changed())
|
||||||
self.widgets[tuple(path + [key])] = var
|
self.widgets[tuple(path + [key])] = var
|
||||||
@@ -287,19 +362,98 @@ class ConfigEditor:
|
|||||||
else:
|
else:
|
||||||
# 其他类型(字符串等)使用普通文本框
|
# 其他类型(字符串等)使用普通文本框
|
||||||
var = tk.StringVar(value=str(value))
|
var = tk.StringVar(value=str(value))
|
||||||
entry = ttk.Entry(frame, textvariable=var, font=("", 20))
|
|
||||||
entry.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star +1, sticky=tk.W+tk.E, padx=5)
|
# 特殊处理provider字段
|
||||||
var.trace_add("write", lambda *args: self.on_value_changed())
|
full_path = ".".join(path + [key])
|
||||||
self.widgets[tuple(path + [key])] = var
|
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.normal_chat_1": "普通聊天:主要聊天模型",
|
||||||
|
"model.normal_chat_2": "普通聊天:次要聊天模型",
|
||||||
|
"model.focus_working_memory": "专注模式:工作记忆模型",
|
||||||
|
"model.focus_chat_mind": "专注模式:聊天规划模型",
|
||||||
|
"model.focus_tool_use": "专注模式:工具调用模型",
|
||||||
|
"model.focus_planner": "专注模式:决策模型",
|
||||||
|
"model.focus_expressor": "专注模式:表达器模型",
|
||||||
|
"model.focus_self_recognize": "专注模式:自我识别模型"
|
||||||
|
}
|
||||||
|
# 获取当前节的名称
|
||||||
|
# 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("<<ComboboxSelected>>", 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"
|
widget_type = "text"
|
||||||
|
|
||||||
def create_section_widgets(self, parent: ttk.Frame, section: str, data: Dict, path=None) -> None:
|
def create_section_widgets(self, parent: ttk.Frame, section: str, data: Dict, path=None) -> None:
|
||||||
"""为配置节创建编辑控件"""
|
"""为配置节创建编辑控件"""
|
||||||
if path is None:
|
if path is None:
|
||||||
path = [section]
|
path = [section]
|
||||||
|
# section完整路径
|
||||||
|
full_section_path = ".".join(path)
|
||||||
# 获取节的中文名称和描述
|
# 获取节的中文名称和描述
|
||||||
section_trans = self.translations.get("sections", {}).get(section, {})
|
section_translations = {
|
||||||
section_name = section_trans.get("name", section)
|
"model.utils": "工具模型",
|
||||||
|
"model.utils_small": "小型工具模型",
|
||||||
|
"model.memory_summary": "记忆概括模型",
|
||||||
|
"model.vlm": "图像识别模型",
|
||||||
|
"model.embedding": "嵌入模型",
|
||||||
|
"model.normal_chat_1": "主要聊天模型",
|
||||||
|
"model.normal_chat_2": "次要聊天模型",
|
||||||
|
"model.focus_working_memory": "工作记忆模型",
|
||||||
|
"model.focus_chat_mind": "聊天规划模型",
|
||||||
|
"model.focus_tool_use": "工具调用模型",
|
||||||
|
"model.focus_planner": "决策模型",
|
||||||
|
"model.focus_expressor": "表达器模型",
|
||||||
|
"model.focus_self_recognize": "自我识别模型"
|
||||||
|
}
|
||||||
|
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_desc = section_trans.get("description", "")
|
||||||
|
|
||||||
# 创建节的标签框架
|
# 创建节的标签框架
|
||||||
@@ -307,13 +461,17 @@ class ConfigEditor:
|
|||||||
section_frame.pack(fill=tk.X, padx=5, pady=10)
|
section_frame.pack(fill=tk.X, padx=5, pady=10)
|
||||||
|
|
||||||
# 创建节的名称标签
|
# 创建节的名称标签
|
||||||
section_label = ttk.Label(section_frame, text=f"[{section_name}]", font=("", 12, "bold"))
|
section_label = ttk.Label(section_frame, text=f"[{section_name}]", font=("微软雅黑", 18, "bold"))
|
||||||
section_label.pack(side=tk.LEFT, padx=5)
|
section_label.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
# 创建节的描述标签
|
# 创建节的描述标签
|
||||||
if section_desc:
|
if isinstance(section_trans.get("description"), dict):
|
||||||
desc_label = ttk.Label(section_frame, text=f"({section_desc})", foreground="gray")
|
# 如果是多语言描述,优先取en,否则取第一个
|
||||||
desc_label.pack(side=tk.LEFT, padx=5)
|
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():
|
for key, value in data.items():
|
||||||
@@ -343,14 +501,6 @@ class ConfigEditor:
|
|||||||
section = self.tree.item(selection[0])["values"][0] # 使用values中的原始节名
|
section = self.tree.item(selection[0])["values"][0] # 使用values中的原始节名
|
||||||
self.current_section = section
|
self.current_section = section
|
||||||
|
|
||||||
# 获取节的中文名称
|
|
||||||
if section == "quick_settings":
|
|
||||||
section_name = "快捷设置"
|
|
||||||
else:
|
|
||||||
section_trans = self.translations.get("sections", {}).get(section, {})
|
|
||||||
section_name = section_trans.get("name", section)
|
|
||||||
self.editor_title.config(text=f"编辑 {section_name}")
|
|
||||||
|
|
||||||
# 清空编辑器
|
# 清空编辑器
|
||||||
for widget in self.content_frame.winfo_children():
|
for widget in self.content_frame.winfo_children():
|
||||||
widget.destroy()
|
widget.destroy()
|
||||||
@@ -361,6 +511,8 @@ class ConfigEditor:
|
|||||||
# 创建编辑控件
|
# 创建编辑控件
|
||||||
if section == "quick_settings":
|
if section == "quick_settings":
|
||||||
self.create_quick_settings_widgets()
|
self.create_quick_settings_widgets()
|
||||||
|
elif section == "env_vars":
|
||||||
|
self.create_env_vars_section(self.content_frame)
|
||||||
elif section in self.config:
|
elif section in self.config:
|
||||||
self.create_section_widgets(self.content_frame, section, self.config[section])
|
self.create_section_widgets(self.content_frame, section, self.config[section])
|
||||||
|
|
||||||
@@ -382,12 +534,12 @@ class ConfigEditor:
|
|||||||
value = current.get(path[-1]) # 获取最后一个键的值
|
value = current.get(path[-1]) # 获取最后一个键的值
|
||||||
|
|
||||||
# 创建名称标签
|
# 创建名称标签
|
||||||
name_label = ttk.Label(frame, text=setting["name"], font=("", 18))
|
name_label = ttk.Label(frame, text=setting["name"], font=("微软雅黑", 18))
|
||||||
name_label.pack(fill=tk.X, padx=5, pady=(2, 0))
|
name_label.pack(fill=tk.X, padx=5, pady=(2, 0))
|
||||||
|
|
||||||
# 创建描述标签
|
# 创建描述标签
|
||||||
if setting.get("description"):
|
if setting.get("description"):
|
||||||
desc_label = ttk.Label(frame, text=setting['description'], foreground="gray", font=("", 16))
|
desc_label = ttk.Label(frame, text=setting['description'], foreground="gray", font=("微软雅黑", 16))
|
||||||
desc_label.pack(fill=tk.X, padx=5, pady=(0, 2))
|
desc_label.pack(fill=tk.X, padx=5, pady=(0, 2))
|
||||||
|
|
||||||
# 根据类型创建不同的控件
|
# 根据类型创建不同的控件
|
||||||
@@ -404,14 +556,14 @@ class ConfigEditor:
|
|||||||
elif setting_type == "text":
|
elif setting_type == "text":
|
||||||
value = str(value) if value is not None else ""
|
value = str(value) if value is not None else ""
|
||||||
var = tk.StringVar(value=value)
|
var = tk.StringVar(value=value)
|
||||||
entry = ttk.Entry(frame, textvariable=var, width=40, font=("", 18))
|
entry = ttk.Entry(frame, textvariable=var, width=40, font=("微软雅黑", 18))
|
||||||
entry.pack(fill=tk.X, padx=5, pady=(0,5))
|
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))
|
var.trace_add("write", lambda *args, p=path, v=var: self.on_quick_setting_changed(p, v))
|
||||||
|
|
||||||
elif setting_type == "number":
|
elif setting_type == "number":
|
||||||
value = str(value) if value is not None else "0"
|
value = str(value) if value is not None else "0"
|
||||||
var = tk.StringVar(value=value)
|
var = tk.StringVar(value=value)
|
||||||
entry = ttk.Entry(frame, textvariable=var, width=10, font=("", 18))
|
entry = ttk.Entry(frame, textvariable=var, width=10, font=("微软雅黑", 18))
|
||||||
entry.pack(fill=tk.X, padx=5, pady=(0,5))
|
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))
|
var.trace_add("write", lambda *args, p=path, v=var: self.on_quick_setting_changed(p, v))
|
||||||
|
|
||||||
@@ -483,22 +635,78 @@ class ConfigEditor:
|
|||||||
try:
|
try:
|
||||||
# 获取所有控件的值
|
# 获取所有控件的值
|
||||||
for path, widget in self.widgets.items():
|
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)
|
value = self.get_widget_value(widget)
|
||||||
# 更新配置
|
|
||||||
current = self.config
|
current = self.config
|
||||||
for key in path[:-1]:
|
for key in path[:-1]:
|
||||||
current = current[key]
|
current = current[key]
|
||||||
final_key = path[-1] # 直接用最后一个key
|
final_key = path[-1]
|
||||||
current[final_key] = value
|
current[final_key] = value
|
||||||
|
|
||||||
# 保存到文件
|
# === 只保存 TOML,不包含 env_vars ===
|
||||||
|
env_vars = self.config.pop('env_vars', None)
|
||||||
with open(self.config_path, "wb") as f:
|
with open(self.config_path, "wb") as f:
|
||||||
tomli_w.dump(self.config, 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,并且只保留当前界面有的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)
|
||||||
|
elif k.endswith("_BASE_URL") or k.endswith("_KEY"):
|
||||||
|
# 跳过界面上已删除的key(不保留)
|
||||||
|
continue
|
||||||
|
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.last_save_time = time.time()
|
||||||
self.pending_save = False
|
self.pending_save = False
|
||||||
self.editor_title.config(text=f"{self.editor_title.cget('text')} (已保存)")
|
|
||||||
self.root.after(2000, lambda: self.editor_title.config(text=self.editor_title.cget('text').replace(" (已保存)", "")))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messagebox.showerror("错误", f"保存配置失败: {str(e)}")
|
messagebox.showerror("错误", f"保存配置失败: {str(e)}")
|
||||||
|
|
||||||
@@ -629,6 +837,254 @@ class ConfigEditor:
|
|||||||
self.widgets.clear()
|
self.widgets.clear()
|
||||||
self.create_quick_settings_widgets()
|
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 "keyword_reaction" in self.config:
|
||||||
|
kw_frame = ttk.Frame(notebook)
|
||||||
|
notebook.add(kw_frame, text="关键词反应")
|
||||||
|
self.create_section_widgets(kw_frame, "keyword_reaction", self.config["keyword_reaction"], ["keyword_reaction"])
|
||||||
|
# 消息接收栏
|
||||||
|
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()
|
||||||
|
|
||||||
|
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():
|
def main():
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
app = ConfigEditor(root)
|
app = ConfigEditor(root)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
[config]
|
[config]
|
||||||
bot_config_path = "config/bot_config.toml"
|
bot_config_path = "C:/GitHub/MaiBot-Core/config/bot_config.toml"
|
||||||
|
env_path = "env.toml"
|
||||||
|
env_file = "c:\\GitHub\\MaiBot-Core\\.env"
|
||||||
|
|
||||||
[editor]
|
[editor]
|
||||||
window_width = 1000
|
window_width = 1000
|
||||||
@@ -72,6 +74,12 @@ description = "是否启用工作记忆处理器,不稳定,消耗量大"
|
|||||||
path = "focus_chat_processor.working_memory_processor"
|
path = "focus_chat_processor.working_memory_processor"
|
||||||
type = "bool"
|
type = "bool"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "显示聊天模式"
|
||||||
|
description = "是否在回复后显示当前聊天模式"
|
||||||
|
path = "experimental.debug_show_chat_mode"
|
||||||
|
type = "bool"
|
||||||
|
|
||||||
[translations.sections.inner]
|
[translations.sections.inner]
|
||||||
name = "版本"
|
name = "版本"
|
||||||
description = "麦麦的内部配置,包含版本号等信息。此部分仅供显示,不可编辑。"
|
description = "麦麦的内部配置,包含版本号等信息。此部分仅供显示,不可编辑。"
|
||||||
@@ -488,6 +496,10 @@ description = "暂时无效"
|
|||||||
name = "启用分割器"
|
name = "启用分割器"
|
||||||
description = "是否启用回复分割器"
|
description = "是否启用回复分割器"
|
||||||
|
|
||||||
|
[translations.items."telemetry.enable"]
|
||||||
|
name = "启用遥测"
|
||||||
|
description = "是否发送统计信息,主要是看全球有多少只麦麦"
|
||||||
|
|
||||||
[translations.items."chinese_typo.enable"]
|
[translations.items."chinese_typo.enable"]
|
||||||
name = "启用错别字"
|
name = "启用错别字"
|
||||||
description = "是否启用中文错别字生成器"
|
description = "是否启用中文错别字生成器"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from src.chat.focus_chat.expressors.default_expressor import DefaultExpressor
|
|||||||
from src.chat.message_receive.chat_stream import ChatStream
|
from src.chat.message_receive.chat_stream import ChatStream
|
||||||
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
|
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
|
||||||
from src.chat.focus_chat.hfc_utils import create_empty_anchor_message
|
from src.chat.focus_chat.hfc_utils import create_empty_anchor_message
|
||||||
|
from src.config.config import global_config
|
||||||
|
|
||||||
logger = get_logger("action_taken")
|
logger = get_logger("action_taken")
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ class ReplyAction(BaseAction):
|
|||||||
"一次只回复一个人,一次只回复一个话题,突出重点",
|
"一次只回复一个人,一次只回复一个话题,突出重点",
|
||||||
"如果是自己发的消息想继续,需自然衔接",
|
"如果是自己发的消息想继续,需自然衔接",
|
||||||
"避免重复或评价自己的发言,不要和自己聊天",
|
"避免重复或评价自己的发言,不要和自己聊天",
|
||||||
"注意:回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,回复不要浮夸,不要用夸张修辞,平淡一些。不要有额外的符号,尽量简单简短",
|
f"注意你的回复要求:{global_config.expression.expression_style}",
|
||||||
]
|
]
|
||||||
|
|
||||||
associated_types: list[str] = ["text", "emoji"]
|
associated_types: list[str] = ["text", "emoji"]
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ identity_detail = [
|
|||||||
|
|
||||||
[expression]
|
[expression]
|
||||||
# 表达方式
|
# 表达方式
|
||||||
expression_style = "描述麦麦说话的表达风格,表达习惯"
|
expression_style = "描述麦麦说话的表达风格,表达习惯,例如:(回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,回复不要浮夸,不要用夸张修辞,平淡一些。不要有额外的符号,尽量简单简短)"
|
||||||
enable_expression_learning = true # 是否启用表达学习,麦麦会学习人类说话风格
|
enable_expression_learning = true # 是否启用表达学习,麦麦会学习人类说话风格
|
||||||
learning_interval = 600 # 学习间隔 单位秒
|
learning_interval = 600 # 学习间隔 单位秒
|
||||||
|
|
||||||
@@ -194,16 +194,18 @@ temp = 0.2 #模型的温度,新V3建议0.1-0.3
|
|||||||
# 强烈建议使用免费的小模型
|
# 强烈建议使用免费的小模型
|
||||||
name = "Qwen/Qwen3-8B"
|
name = "Qwen/Qwen3-8B"
|
||||||
provider = "SILICONFLOW"
|
provider = "SILICONFLOW"
|
||||||
enable_thinking = false # 是否启用思考
|
|
||||||
pri_in = 0
|
pri_in = 0
|
||||||
pri_out = 0
|
pri_out = 0
|
||||||
|
temp = 0.7
|
||||||
|
enable_thinking = false # 是否启用思考
|
||||||
|
|
||||||
[model.memory_summary] # 记忆的概括模型
|
[model.memory_summary] # 记忆的概括模型
|
||||||
name = "Qwen/Qwen3-30B-A3B"
|
name = "Qwen/Qwen3-30B-A3B"
|
||||||
provider = "SILICONFLOW"
|
provider = "SILICONFLOW"
|
||||||
enable_thinking = false # 是否启用思考
|
|
||||||
pri_in = 0.7
|
pri_in = 0.7
|
||||||
pri_out = 2.8
|
pri_out = 2.8
|
||||||
|
temp = 0.7
|
||||||
|
enable_thinking = false # 是否启用思考
|
||||||
|
|
||||||
[model.vlm] # 图像识别模型
|
[model.vlm] # 图像识别模型
|
||||||
name = "Pro/Qwen/Qwen2.5-VL-7B-Instruct"
|
name = "Pro/Qwen/Qwen2.5-VL-7B-Instruct"
|
||||||
@@ -211,6 +213,7 @@ provider = "SILICONFLOW"
|
|||||||
pri_in = 0.35
|
pri_in = 0.35
|
||||||
pri_out = 0.35
|
pri_out = 0.35
|
||||||
|
|
||||||
|
|
||||||
#嵌入模型
|
#嵌入模型
|
||||||
[model.embedding]
|
[model.embedding]
|
||||||
name = "BAAI/bge-m3"
|
name = "BAAI/bge-m3"
|
||||||
@@ -225,6 +228,7 @@ name = "Pro/deepseek-ai/DeepSeek-R1"
|
|||||||
provider = "SILICONFLOW"
|
provider = "SILICONFLOW"
|
||||||
pri_in = 4.0 #模型的输入价格(非必填,可以记录消耗)
|
pri_in = 4.0 #模型的输入价格(非必填,可以记录消耗)
|
||||||
pri_out = 16.0 #模型的输出价格(非必填,可以记录消耗)
|
pri_out = 16.0 #模型的输出价格(非必填,可以记录消耗)
|
||||||
|
temp = 0.7
|
||||||
|
|
||||||
[model.normal_chat_2] # 一般聊天模式的次要回复模型,推荐使用 非推理模型
|
[model.normal_chat_2] # 一般聊天模式的次要回复模型,推荐使用 非推理模型
|
||||||
name = "Pro/deepseek-ai/DeepSeek-V3"
|
name = "Pro/deepseek-ai/DeepSeek-V3"
|
||||||
@@ -239,9 +243,10 @@ temp = 0.2 #模型的温度,新V3建议0.1-0.3
|
|||||||
[model.focus_working_memory] #工作记忆模型
|
[model.focus_working_memory] #工作记忆模型
|
||||||
name = "Qwen/Qwen3-30B-A3B"
|
name = "Qwen/Qwen3-30B-A3B"
|
||||||
provider = "SILICONFLOW"
|
provider = "SILICONFLOW"
|
||||||
enable_thinking = false # 是否启用思考
|
enable_thinking = false # 是否启用思考(qwen3 only)
|
||||||
pri_in = 0.7
|
pri_in = 0.7
|
||||||
pri_out = 2.8
|
pri_out = 2.8
|
||||||
|
temp = 0.7
|
||||||
|
|
||||||
[model.focus_chat_mind] #聊天规划:认真聊天时,生成麦麦对聊天的规划想法
|
[model.focus_chat_mind] #聊天规划:认真聊天时,生成麦麦对聊天的规划想法
|
||||||
name = "Pro/deepseek-ai/DeepSeek-V3"
|
name = "Pro/deepseek-ai/DeepSeek-V3"
|
||||||
@@ -255,15 +260,16 @@ temp = 0.3
|
|||||||
[model.focus_tool_use] #工具调用模型,需要使用支持工具调用的模型
|
[model.focus_tool_use] #工具调用模型,需要使用支持工具调用的模型
|
||||||
name = "Qwen/Qwen3-14B"
|
name = "Qwen/Qwen3-14B"
|
||||||
provider = "SILICONFLOW"
|
provider = "SILICONFLOW"
|
||||||
enable_thinking = false # 是否启用思考
|
|
||||||
pri_in = 0.5
|
pri_in = 0.5
|
||||||
pri_out = 2
|
pri_out = 2
|
||||||
|
temp = 0.7
|
||||||
|
enable_thinking = false # 是否启用思考(qwen3 only)
|
||||||
|
|
||||||
[model.focus_planner] #决策:认真聊天时,负责决定麦麦该做什么
|
[model.focus_planner] #决策:认真聊天时,负责决定麦麦该做什么
|
||||||
name = "Pro/deepseek-ai/DeepSeek-V3"
|
name = "Pro/deepseek-ai/DeepSeek-V3"
|
||||||
# name = "Qwen/Qwen3-30B-A3B"
|
# name = "Qwen/Qwen3-30B-A3B"
|
||||||
provider = "SILICONFLOW"
|
provider = "SILICONFLOW"
|
||||||
# enable_thinking = false # 是否启用思考
|
# enable_thinking = false # 是否启用思考(qwen3 only)
|
||||||
pri_in = 2
|
pri_in = 2
|
||||||
pri_out = 8
|
pri_out = 8
|
||||||
temp = 0.3
|
temp = 0.3
|
||||||
@@ -274,7 +280,7 @@ temp = 0.3
|
|||||||
name = "Pro/deepseek-ai/DeepSeek-V3"
|
name = "Pro/deepseek-ai/DeepSeek-V3"
|
||||||
# name = "Qwen/Qwen3-30B-A3B"
|
# name = "Qwen/Qwen3-30B-A3B"
|
||||||
provider = "SILICONFLOW"
|
provider = "SILICONFLOW"
|
||||||
# enable_thinking = false # 是否启用思考
|
# enable_thinking = false # 是否启用思考(qwen3 only)
|
||||||
pri_in = 2
|
pri_in = 2
|
||||||
pri_out = 8
|
pri_out = 8
|
||||||
temp = 0.3
|
temp = 0.3
|
||||||
@@ -284,37 +290,10 @@ temp = 0.3
|
|||||||
# name = "Pro/deepseek-ai/DeepSeek-V3"
|
# name = "Pro/deepseek-ai/DeepSeek-V3"
|
||||||
name = "Qwen/Qwen3-30B-A3B"
|
name = "Qwen/Qwen3-30B-A3B"
|
||||||
provider = "SILICONFLOW"
|
provider = "SILICONFLOW"
|
||||||
enable_thinking = false # 是否启用思考
|
|
||||||
pri_in = 0.7
|
pri_in = 0.7
|
||||||
pri_out = 2.8
|
pri_out = 2.8
|
||||||
temp = 0.7
|
temp = 0.7
|
||||||
|
enable_thinking = false # 是否启用思考(qwen3 only)
|
||||||
|
|
||||||
|
|
||||||
#私聊PFC:需要开启PFC功能,默认三个模型均为硅基流动v3,如果需要支持多人同时私聊或频繁调用,建议把其中的一个或两个换成官方v3或其它模型,以免撞到429
|
|
||||||
|
|
||||||
#PFC决策模型
|
|
||||||
[model.pfc_action_planner]
|
|
||||||
name = "Pro/deepseek-ai/DeepSeek-V3"
|
|
||||||
provider = "SILICONFLOW"
|
|
||||||
temp = 0.3
|
|
||||||
pri_in = 2
|
|
||||||
pri_out = 8
|
|
||||||
|
|
||||||
#PFC聊天模型
|
|
||||||
[model.pfc_chat]
|
|
||||||
name = "Pro/deepseek-ai/DeepSeek-V3"
|
|
||||||
provider = "SILICONFLOW"
|
|
||||||
temp = 0.3
|
|
||||||
pri_in = 2
|
|
||||||
pri_out = 8
|
|
||||||
|
|
||||||
#PFC检查模型
|
|
||||||
[model.pfc_reply_checker]
|
|
||||||
name = "Pro/deepseek-ai/DeepSeek-V3"
|
|
||||||
provider = "SILICONFLOW"
|
|
||||||
pri_in = 2
|
|
||||||
pri_out = 8
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -335,7 +314,6 @@ enable = true
|
|||||||
[experimental] #实验性功能
|
[experimental] #实验性功能
|
||||||
debug_show_chat_mode = false # 是否在回复后显示当前聊天模式
|
debug_show_chat_mode = false # 是否在回复后显示当前聊天模式
|
||||||
enable_friend_chat = false # 是否启用好友聊天
|
enable_friend_chat = false # 是否启用好友聊天
|
||||||
pfc_chatting = false # 暂时无效
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user