ruff format

This commit is contained in:
tcmofashi
2025-06-01 08:09:31 +08:00
parent 7cb546c816
commit 48adf192d0
12 changed files with 239 additions and 189 deletions

View File

@@ -8,6 +8,7 @@ import threading
import time import time
import sys import sys
class ConfigEditor: class ConfigEditor:
def __init__(self, root): def __init__(self, root):
self.root = root self.root = root
@@ -21,10 +22,10 @@ class ConfigEditor:
# 加载配置 # 加载配置
self.load_config() self.load_config()
# 加载环境变量 # 加载环境变量
self.load_env_vars() self.load_env_vars()
# 自动保存相关 # 自动保存相关
self.last_save_time = time.time() self.last_save_time = time.time()
self.save_timer = None self.save_timer = None
@@ -114,40 +115,40 @@ class ConfigEditor:
env_path = self.config.get("inner", {}).get("env_file", ".env") env_path = self.config.get("inner", {}).get("env_file", ".env")
if not os.path.isabs(env_path): if not os.path.isabs(env_path):
env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), env_path) env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), env_path)
if not os.path.exists(env_path): if not os.path.exists(env_path):
print(f"环境文件不存在: {env_path}") print(f"环境文件不存在: {env_path}")
return return
# 读取环境文件 # 读取环境文件
with open(env_path, 'r', encoding='utf-8') as f: with open(env_path, "r", encoding="utf-8") as f:
env_content = f.read() env_content = f.read()
# 解析环境变量 # 解析环境变量
env_vars = {} env_vars = {}
for line in env_content.split('\n'): for line in env_content.split("\n"):
line = line.strip() line = line.strip()
if not line or line.startswith('#'): if not line or line.startswith("#"):
continue continue
if '=' in line: if "=" in line:
key, value = line.split('=', 1) key, value = line.split("=", 1)
key = key.strip() key = key.strip()
value = value.strip() value = value.strip()
# 检查是否是目标变量 # 检查是否是目标变量
if key.endswith('_BASE_URL') or key.endswith('_KEY'): if key.endswith("_BASE_URL") or key.endswith("_KEY"):
# 提取前缀去掉_BASE_URL或_KEY # 提取前缀去掉_BASE_URL或_KEY
prefix = key[:-9] if key.endswith('_BASE_URL') else key[:-4] prefix = key[:-9] if key.endswith("_BASE_URL") else key[:-4]
if prefix not in env_vars: if prefix not in env_vars:
env_vars[prefix] = {} env_vars[prefix] = {}
env_vars[prefix][key] = value env_vars[prefix][key] = value
# 将解析的环境变量添加到配置中 # 将解析的环境变量添加到配置中
if 'env_vars' not in self.config: if "env_vars" not in self.config:
self.config['env_vars'] = {} self.config["env_vars"] = {}
self.config['env_vars'].update(env_vars) self.config["env_vars"].update(env_vars)
except Exception as e: except Exception as e:
print(f"加载环境变量失败: {str(e)}") print(f"加载环境变量失败: {str(e)}")
@@ -156,11 +157,11 @@ class ConfigEditor:
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))
# 添加配置按钮 # 添加配置按钮
config_button = ttk.Button(version_frame, text="配置路径", command=self.open_path_config) config_button = ttk.Button(version_frame, text="配置路径", command=self.open_path_config)
config_button.pack(side=tk.LEFT, padx=5) config_button.pack(side=tk.LEFT, padx=5)
version_label = ttk.Label(version_frame, text=f"麦麦版本:{version}", font=("微软雅黑", 10, "bold")) 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)
@@ -175,13 +176,22 @@ class ConfigEditor:
# 添加快捷设置节 # 添加快捷设置节
self.tree.insert("", "end", text="快捷设置", values=("quick_settings",)) self.tree.insert("", "end", text="快捷设置", values=("quick_settings",))
# 添加env_vars节显示为"配置你的模型APIKEY" # 添加env_vars节显示为"配置你的模型APIKEY"
self.tree.insert("", "end", text="配置你的模型APIKEY", values=("env_vars",)) self.tree.insert("", "end", text="配置你的模型APIKEY", values=("env_vars",))
# 只显示bot_config.toml实际存在的section # 只显示bot_config.toml实际存在的section
for section in self.config: for section in self.config:
if section not in ("inner", "env_vars", "telemetry", "experimental", "maim_message", "keyword_reaction", "message_receive", "relationship"): 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,))
@@ -196,7 +206,7 @@ class ConfigEditor:
# 创建编辑区标题 # 创建编辑区标题
# 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)
self.editor_content.pack(fill=tk.BOTH, expand=True) self.editor_content.pack(fill=tk.BOTH, expand=True)
@@ -245,15 +255,15 @@ 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 = { model_item_translations = {
"name": ("模型名称", "模型的唯一标识或名称"), "name": ("模型名称", "模型的唯一标识或名称"),
"provider": ("模型提供商", "模型API的提供商"), "provider": ("模型提供商", "模型API的提供商"),
"pri_in": ("输入价格", "模型输入的价格/消耗"), "pri_in": ("输入价格", "模型输入的价格/消耗"),
"pri_out": ("输出价格", "模型输出的价格/消耗"), "pri_out": ("输出价格", "模型输出的价格/消耗"),
"temp": ("模型温度", "控制模型输出的多样性") "temp": ("模型温度", "控制模型输出的多样性"),
} }
item_name_to_display = key # 默认显示原始键名 item_name_to_display = key # 默认显示原始键名
item_desc_to_display = "" # 默认无描述 item_desc_to_display = "" # 默认无描述
@@ -294,9 +304,15 @@ class ConfigEditor:
# 判断parent是不是self.content_frame # 判断parent是不是self.content_frame
if parent == self.content_frame: if parent == self.content_frame:
# 主界面 # 主界面
if hasattr(self, 'current_section') and self.current_section and self.current_section != "quick_settings": if (
self.create_section_widgets(parent, self.current_section, self.config[self.current_section], [self.current_section]) hasattr(self, "current_section")
elif hasattr(self, 'current_section') and self.current_section == "quick_settings": 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() self.create_quick_settings_widgets()
else: else:
# 弹窗Tab # 弹窗Tab
@@ -318,15 +334,17 @@ 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=("微软雅黑", 10)) 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(
widget_row = desc_row + 1 # 内容控件在描述下方 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: else:
widget_row = desc_row # 内容控件直接在第二行 widget_row = desc_row # 内容控件直接在第二行
# 配置内容控件(第三行或第二行) # 配置内容控件(第三行或第二行)
if path[0] == "inner": if path[0] == "inner":
value_label = ttk.Label(frame, text=str(value), font=("微软雅黑", 16)) 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
if isinstance(value, bool): if isinstance(value, bool):
@@ -341,7 +359,7 @@ class ConfigEditor:
# 数字使用数字输入框 # 数字使用数字输入框
var = tk.StringVar(value=str(value)) var = tk.StringVar(value=str(value))
entry = ttk.Entry(frame, textvariable=var, font=("微软雅黑", 16)) 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
widget_type = "number" widget_type = "number"
@@ -380,7 +398,7 @@ class ConfigEditor:
else: else:
# 其他类型(字符串等)使用普通文本框 # 其他类型(字符串等)使用普通文本框
var = tk.StringVar(value=str(value)) var = tk.StringVar(value=str(value))
# 特殊处理provider字段 # 特殊处理provider字段
full_path = ".".join(path + [key]) full_path = ".".join(path + [key])
if key == "provider" and full_path.startswith("model."): if key == "provider" and full_path.startswith("model."):
@@ -397,37 +415,45 @@ class ConfigEditor:
if f"{prefix}_BASE_URL" in values and f"{prefix}_KEY" in values: if f"{prefix}_BASE_URL" in values and f"{prefix}_KEY" in values:
providers.append(prefix) providers.append(prefix)
# print(f"添加provider: {prefix}") # print(f"添加provider: {prefix}")
# print(f"最终providers列表: {providers}") # print(f"最终providers列表: {providers}")
if providers: if providers:
# 创建模型名称标签(大字体) # 创建模型名称标签(大字体)
model_name = var.get() if var.get() else providers[0] # model_name = var.get() if var.get() else providers[0]
section_translations = { # section_translations = {
"model.utils": "麦麦组件模型", # "model.utils": "麦麦组件模型",
"model.utils_small": "小型麦麦组件模型", # "model.utils_small": "小型麦麦组件模型",
"model.memory_summary": "记忆概括模型", # "model.memory_summary": "记忆概括模型",
"model.vlm": "图像识别模型", # "model.vlm": "图像识别模型",
"model.embedding": "嵌入模型", # "model.embedding": "嵌入模型",
"model.normal_chat_1": "普通聊天:主要聊天模型", # "model.normal_chat_1": "普通聊天:主要聊天模型",
"model.normal_chat_2": "普通聊天:次要聊天模型", # "model.normal_chat_2": "普通聊天:次要聊天模型",
"model.focus_working_memory": "专注模式:工作记忆模型", # "model.focus_working_memory": "专注模式:工作记忆模型",
"model.focus_chat_mind": "专注模式:聊天思考模型", # "model.focus_chat_mind": "专注模式:聊天思考模型",
"model.focus_tool_use": "专注模式:工具调用模型", # "model.focus_tool_use": "专注模式:工具调用模型",
"model.focus_planner": "专注模式:决策模型", # "model.focus_planner": "专注模式:决策模型",
"model.focus_expressor": "专注模式:表达器模型", # "model.focus_expressor": "专注模式:表达器模型",
"model.focus_self_recognize": "专注模式:自我识别模型" # "model.focus_self_recognize": "专注模式:自我识别模型"
} # }
# 获取当前节的名称 # 获取当前节的名称
# current_section = ".".join(path[:-1]) # 去掉最后一个key # current_section = ".".join(path[:-1]) # 去掉最后一个key
# section_name = section_translations.get(current_section, current_section) # section_name = section_translations.get(current_section, current_section)
# 创建节名称标签(大字体) # 创建节名称标签(大字体)
# section_label = ttk.Label(frame, text="11", font=("微软雅黑", 24, "bold")) # 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)) # 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 = ttk.Combobox(
combo.grid(row=widget_row + 1, column=0, columnspan=content_col_offset_for_star +1, sticky=tk.W+tk.E, padx=5) 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()) combo.bind("<<ComboboxSelected>>", lambda e: self.on_value_changed())
self.widgets[tuple(path + [key])] = var self.widgets[tuple(path + [key])] = var
widget_type = "provider" widget_type = "provider"
@@ -436,14 +462,18 @@ class ConfigEditor:
# 如果没有可用的provider使用普通文本框 # 如果没有可用的provider使用普通文本框
# print(f"没有可用的provider使用普通文本框") # print(f"没有可用的provider使用普通文本框")
entry = ttk.Entry(frame, textvariable=var, font=("微软雅黑", 16)) 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
widget_type = "text" widget_type = "text"
else: else:
# 普通文本框 # 普通文本框
entry = ttk.Entry(frame, textvariable=var, font=("微软雅黑", 16)) 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
widget_type = "text" widget_type = "text"
@@ -468,7 +498,7 @@ class ConfigEditor:
"model.focus_tool_use": "工具调用模型", "model.focus_tool_use": "工具调用模型",
"model.focus_planner": "决策模型", "model.focus_planner": "决策模型",
"model.focus_expressor": "表达器模型", "model.focus_expressor": "表达器模型",
"model.focus_self_recognize": "自我识别模型" "model.focus_self_recognize": "自我识别模型",
} }
section_trans = self.translations.get("sections", {}).get(full_section_path, {}) 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_name = section_trans.get("name") or section_translations.get(full_section_path) or section
@@ -490,7 +520,7 @@ class ConfigEditor:
else: else:
desc_label = ttk.Label(section_frame, text=section_desc, foreground="gray", font=("微软雅黑", 10)) desc_label = ttk.Label(section_frame, text=section_desc, foreground="gray", font=("微软雅黑", 10))
desc_label.pack(side=tk.LEFT, padx=5) desc_label.pack(side=tk.LEFT, padx=5)
# 为每个配置项创建对应的控件 # 为每个配置项创建对应的控件
for key, value in data.items(): for key, value in data.items():
if isinstance(value, dict): if isinstance(value, dict):
@@ -518,7 +548,7 @@ 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
# 清空编辑器 # 清空编辑器
for widget in self.content_frame.winfo_children(): for widget in self.content_frame.winfo_children():
widget.destroy() widget.destroy()
@@ -557,7 +587,7 @@ class ConfigEditor:
# 创建描述标签 # 创建描述标签
if setting.get("description"): if setting.get("description"):
desc_label = ttk.Label(frame, text=setting['description'], foreground="gray", font=("微软雅黑", 10)) desc_label = ttk.Label(frame, text=setting["description"], foreground="gray", font=("微软雅黑", 10))
desc_label.pack(fill=tk.X, padx=5, pady=(0, 2)) desc_label.pack(fill=tk.X, padx=5, pady=(0, 2))
# 根据类型创建不同的控件 # 根据类型创建不同的控件
@@ -575,14 +605,14 @@ class ConfigEditor:
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=("微软雅黑", 12)) entry = ttk.Entry(frame, textvariable=var, width=40, font=("微软雅黑", 12))
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=("微软雅黑", 12)) entry = ttk.Entry(frame, textvariable=var, width=10, font=("微软雅黑", 12))
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 == "list": elif setting_type == "list":
@@ -659,7 +689,7 @@ class ConfigEditor:
# 获取所有控件的值 # 获取所有控件的值
for path, widget in self.widgets.items(): for path, widget in self.widgets.items():
# 跳过 env_vars 的控件赋值(只用于.env不写回config # 跳过 env_vars 的控件赋值(只用于.env不写回config
if len(path) >= 2 and path[0] == 'env_vars': if len(path) >= 2 and path[0] == "env_vars":
continue continue
value = self.get_widget_value(widget) value = self.get_widget_value(widget)
current = self.config current = self.config
@@ -669,11 +699,11 @@ class ConfigEditor:
current[final_key] = value current[final_key] = value
# === 只保存 TOML不包含 env_vars === # === 只保存 TOML不包含 env_vars ===
env_vars = self.config.pop('env_vars', None) 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: if env_vars is not None:
self.config['env_vars'] = env_vars self.config["env_vars"] = env_vars
# === 保存 env_vars 到 .env 文件只覆盖特定key其他内容保留 === # === 保存 env_vars 到 .env 文件只覆盖特定key其他内容保留 ===
env_path = self.editor_config["config"].get("env_file", ".env") env_path = self.editor_config["config"].get("env_file", ".env")
@@ -687,7 +717,7 @@ class ConfigEditor:
# 2. 收集所有目标key的新值直接从widgets取 # 2. 收集所有目标key的新值直接从widgets取
new_env_dict = {} new_env_dict = {}
for path, widget in self.widgets.items(): for path, widget in self.widgets.items():
if len(path) == 2 and path[0] == 'env_vars': if len(path) == 2 and path[0] == "env_vars":
k = path[1] k = path[1]
if k.endswith("_BASE_URL") or k.endswith("_KEY"): if k.endswith("_BASE_URL") or k.endswith("_KEY"):
new_env_dict[k] = self.get_widget_value(widget) new_env_dict[k] = self.get_widget_value(widget)
@@ -715,15 +745,15 @@ class ConfigEditor:
# === 保存完 .env 后,同步 widgets 的值回 self.config['env_vars'] === # === 保存完 .env 后,同步 widgets 的值回 self.config['env_vars'] ===
for path, widget in self.widgets.items(): for path, widget in self.widgets.items():
if len(path) == 2 and path[0] == 'env_vars': if len(path) == 2 and path[0] == "env_vars":
prefix_key = path[1] prefix_key = path[1]
if prefix_key.endswith("_BASE_URL") or prefix_key.endswith("_KEY"): 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] prefix = prefix_key[:-9] if prefix_key.endswith("_BASE_URL") else prefix_key[:-4]
if 'env_vars' not in self.config: if "env_vars" not in self.config:
self.config['env_vars'] = {} self.config["env_vars"] = {}
if prefix not in self.config['env_vars']: if prefix not in self.config["env_vars"]:
self.config['env_vars'][prefix] = {} self.config["env_vars"][prefix] = {}
self.config['env_vars'][prefix][prefix_key] = self.get_widget_value(widget) 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
@@ -862,62 +892,60 @@ class ConfigEditor:
"""创建环境变量组""" """创建环境变量组"""
frame = ttk.Frame(parent) frame = ttk.Frame(parent)
frame.pack(fill=tk.X, padx=5, pady=2) frame.pack(fill=tk.X, padx=5, pady=2)
# 创建组标题 # 创建组标题
title_frame = ttk.Frame(frame) title_frame = ttk.Frame(frame)
title_frame.pack(fill=tk.X, pady=(5, 0)) title_frame.pack(fill=tk.X, pady=(5, 0))
title_label = ttk.Label(title_frame, text=f"API配置组: {prefix}", font=("微软雅黑", 16, "bold")) title_label = ttk.Label(title_frame, text=f"API配置组: {prefix}", font=("微软雅黑", 16, "bold"))
title_label.pack(side=tk.LEFT, padx=5) title_label.pack(side=tk.LEFT, padx=5)
# 删除按钮 # 删除按钮
del_button = ttk.Button(title_frame, text="删除组", del_button = ttk.Button(title_frame, text="删除组", command=lambda: self.delete_env_var_group(prefix))
command=lambda: self.delete_env_var_group(prefix))
del_button.pack(side=tk.RIGHT, padx=5) del_button.pack(side=tk.RIGHT, padx=5)
# 创建BASE_URL输入框 # 创建BASE_URL输入框
base_url_frame = ttk.Frame(frame) base_url_frame = ttk.Frame(frame)
base_url_frame.pack(fill=tk.X, padx=5, pady=2) 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 = ttk.Label(base_url_frame, text="BASE_URL:", font=("微软雅黑", 12))
base_url_label.pack(side=tk.LEFT, padx=5) base_url_label.pack(side=tk.LEFT, padx=5)
base_url_var = tk.StringVar(value=values.get(f"{prefix}_BASE_URL", "")) 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 = 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_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
base_url_var.trace_add("write", lambda *args: self.on_value_changed()) base_url_var.trace_add("write", lambda *args: self.on_value_changed())
# 创建KEY输入框 # 创建KEY输入框
key_frame = ttk.Frame(frame) key_frame = ttk.Frame(frame)
key_frame.pack(fill=tk.X, padx=5, pady=2) key_frame.pack(fill=tk.X, padx=5, pady=2)
key_label = ttk.Label(key_frame, text="API KEY:", font=("微软雅黑", 12)) key_label = ttk.Label(key_frame, text="API KEY:", font=("微软雅黑", 12))
key_label.pack(side=tk.LEFT, padx=5) key_label.pack(side=tk.LEFT, padx=5)
key_var = tk.StringVar(value=values.get(f"{prefix}_KEY", "")) key_var = tk.StringVar(value=values.get(f"{prefix}_KEY", ""))
key_entry = ttk.Entry(key_frame, textvariable=key_var, font=("微软雅黑", 12)) 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_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
key_var.trace_add("write", lambda *args: self.on_value_changed()) 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}_BASE_URL"])] = base_url_var
self.widgets[tuple(path + [f"{prefix}_KEY"])] = key_var self.widgets[tuple(path + [f"{prefix}_KEY"])] = key_var
# 添加分隔线 # 添加分隔线
separator = ttk.Separator(frame, orient='horizontal') separator = ttk.Separator(frame, orient="horizontal")
separator.pack(fill=tk.X, pady=5) separator.pack(fill=tk.X, pady=5)
def create_env_vars_section(self, parent: ttk.Frame) -> None: def create_env_vars_section(self, parent: ttk.Frame) -> None:
"""创建环境变量编辑区""" """创建环境变量编辑区"""
# 创建添加新组的按钮 # 创建添加新组的按钮
add_button = ttk.Button(parent, text="添加新的API配置组", add_button = ttk.Button(parent, text="添加新的API配置组", command=self.add_new_env_var_group)
command=self.add_new_env_var_group)
add_button.pack(pady=10) add_button.pack(pady=10)
# 创建现有组的编辑区 # 创建现有组的编辑区
if 'env_vars' in self.config: if "env_vars" in self.config:
for prefix, values in self.config['env_vars'].items(): for prefix, values in self.config["env_vars"].items():
self.create_env_var_group(parent, prefix, values, ['env_vars']) self.create_env_var_group(parent, prefix, values, ["env_vars"])
def add_new_env_var_group(self): def add_new_env_var_group(self):
"""添加新的环境变量组""" """添加新的环境变量组"""
@@ -925,42 +953,39 @@ class ConfigEditor:
dialog = tk.Toplevel(self.root) dialog = tk.Toplevel(self.root)
dialog.title("添加新的API配置组") dialog.title("添加新的API配置组")
dialog.geometry("400x200") dialog.geometry("400x200")
# 创建输入框架 # 创建输入框架
frame = ttk.Frame(dialog, padding="10") frame = ttk.Frame(dialog, padding="10")
frame.pack(fill=tk.BOTH, expand=True) frame.pack(fill=tk.BOTH, expand=True)
# 前缀输入 # 前缀输入
prefix_label = ttk.Label(frame, text="API前缀名称:", font=("微软雅黑", 12)) prefix_label = ttk.Label(frame, text="API前缀名称:", font=("微软雅黑", 12))
prefix_label.pack(pady=5) prefix_label.pack(pady=5)
prefix_var = tk.StringVar() prefix_var = tk.StringVar()
prefix_entry = ttk.Entry(frame, textvariable=prefix_var, font=("微软雅黑", 12)) prefix_entry = ttk.Entry(frame, textvariable=prefix_var, font=("微软雅黑", 12))
prefix_entry.pack(fill=tk.X, pady=5) prefix_entry.pack(fill=tk.X, pady=5)
# 确认按钮 # 确认按钮
def on_confirm(): def on_confirm():
prefix = prefix_var.get().strip() prefix = prefix_var.get().strip()
if prefix: if prefix:
if 'env_vars' not in self.config: if "env_vars" not in self.config:
self.config['env_vars'] = {} self.config["env_vars"] = {}
self.config['env_vars'][prefix] = { self.config["env_vars"][prefix] = {f"{prefix}_BASE_URL": "", f"{prefix}_KEY": ""}
f"{prefix}_BASE_URL": "",
f"{prefix}_KEY": ""
}
# 刷新显示 # 刷新显示
self.refresh_env_vars_section() self.refresh_env_vars_section()
self.on_value_changed() self.on_value_changed()
dialog.destroy() dialog.destroy()
confirm_button = ttk.Button(frame, text="确认", command=on_confirm) confirm_button = ttk.Button(frame, text="确认", command=on_confirm)
confirm_button.pack(pady=10) confirm_button.pack(pady=10)
def delete_env_var_group(self, prefix: str): def delete_env_var_group(self, prefix: str):
"""删除环境变量组""" """删除环境变量组"""
if messagebox.askyesno("确认", f"确定要删除 {prefix} 配置组吗?"): if messagebox.askyesno("确认", f"确定要删除 {prefix} 配置组吗?"):
if 'env_vars' in self.config: if "env_vars" in self.config:
del self.config['env_vars'][prefix] del self.config["env_vars"][prefix]
# 刷新显示 # 刷新显示
self.refresh_env_vars_section() self.refresh_env_vars_section()
self.on_value_changed() self.on_value_changed()
@@ -971,7 +996,7 @@ class ConfigEditor:
for widget in self.content_frame.winfo_children(): for widget in self.content_frame.winfo_children():
widget.destroy() widget.destroy()
self.widgets.clear() self.widgets.clear()
# 重新创建编辑区 # 重新创建编辑区
self.create_env_vars_section(self.content_frame) self.create_env_vars_section(self.content_frame)
@@ -980,10 +1005,10 @@ class ConfigEditor:
dialog = tk.Toplevel(self.root) dialog = tk.Toplevel(self.root)
dialog.title("高级选项") dialog.title("高级选项")
dialog.geometry("700x800") dialog.geometry("700x800")
notebook = ttk.Notebook(dialog) notebook = ttk.Notebook(dialog)
notebook.pack(fill=tk.BOTH, expand=True) notebook.pack(fill=tk.BOTH, expand=True)
# 遥测栏 # 遥测栏
if "telemetry" in self.config: if "telemetry" in self.config:
telemetry_frame = ttk.Frame(notebook) telemetry_frame = ttk.Frame(notebook)
@@ -1003,7 +1028,9 @@ class ConfigEditor:
if "message_receive" in self.config: if "message_receive" in self.config:
recv_frame = ttk.Frame(notebook) recv_frame = ttk.Frame(notebook)
notebook.add(recv_frame, text="消息接收") notebook.add(recv_frame, text="消息接收")
self.create_section_widgets(recv_frame, "message_receive", self.config["message_receive"], ["message_receive"]) self.create_section_widgets(
recv_frame, "message_receive", self.config["message_receive"], ["message_receive"]
)
# 关系栏 # 关系栏
if "relationship" in self.config: if "relationship" in self.config:
rel_frame = ttk.Frame(notebook) rel_frame = ttk.Frame(notebook)
@@ -1015,96 +1042,95 @@ class ConfigEditor:
dialog = tk.Toplevel(self.root) dialog = tk.Toplevel(self.root)
dialog.title("配置路径") dialog.title("配置路径")
dialog.geometry("600x200") dialog.geometry("600x200")
# 创建输入框架 # 创建输入框架
frame = ttk.Frame(dialog, padding="10") frame = ttk.Frame(dialog, padding="10")
frame.pack(fill=tk.BOTH, expand=True) frame.pack(fill=tk.BOTH, expand=True)
# bot_config.toml路径配置 # bot_config.toml路径配置
bot_config_frame = ttk.Frame(frame) bot_config_frame = ttk.Frame(frame)
bot_config_frame.pack(fill=tk.X, pady=5) 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 = ttk.Label(bot_config_frame, text="bot_config.toml路径:", font=("微软雅黑", 12))
bot_config_label.pack(side=tk.LEFT, padx=5) bot_config_label.pack(side=tk.LEFT, padx=5)
bot_config_var = tk.StringVar(value=self.config_path) 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 = 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) bot_config_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
def apply_config(): def apply_config():
new_bot_config_path = bot_config_var.get().strip() new_bot_config_path = bot_config_var.get().strip()
new_env_path = env_var.get().strip() new_env_path = env_var.get().strip()
if not new_bot_config_path or not new_env_path: if not new_bot_config_path or not new_env_path:
messagebox.showerror("错误", "路径不能为空") messagebox.showerror("错误", "路径不能为空")
return return
if not os.path.exists(new_bot_config_path): if not os.path.exists(new_bot_config_path):
messagebox.showerror("错误", "bot_config.toml文件不存在") messagebox.showerror("错误", "bot_config.toml文件不存在")
return return
# 更新配置 # 更新配置
self.config_path = new_bot_config_path self.config_path = new_bot_config_path
self.editor_config["config"]["bot_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 self.editor_config["config"]["env_file"] = new_env_path
# 保存编辑器配置 # 保存编辑器配置
config_path = os.path.join(os.path.dirname(__file__), "configexe.toml") config_path = os.path.join(os.path.dirname(__file__), "configexe.toml")
with open(config_path, "wb") as f: with open(config_path, "wb") as f:
tomli_w.dump(self.editor_config, f) tomli_w.dump(self.editor_config, f)
# 重新加载配置 # 重新加载配置
self.load_config() self.load_config()
self.load_env_vars() self.load_env_vars()
# 刷新显示 # 刷新显示
self.refresh_config() self.refresh_config()
messagebox.showinfo("成功", "路径配置已更新,程序将重新启动") messagebox.showinfo("成功", "路径配置已更新,程序将重新启动")
dialog.destroy() dialog.destroy()
# 重启程序 # 重启程序
self.root.quit() self.root.quit()
os.execv(sys.executable, ['python'] + sys.argv) os.execv(sys.executable, ["python"] + sys.argv)
def browse_bot_config(): def browse_bot_config():
file_path = filedialog.askopenfilename( file_path = filedialog.askopenfilename(
title="选择bot_config.toml文件", title="选择bot_config.toml文件", filetypes=[("TOML文件", "*.toml"), ("所有文件", "*.*")]
filetypes=[("TOML文件", "*.toml"), ("所有文件", "*.*")]
) )
if file_path: if file_path:
bot_config_var.set(file_path) bot_config_var.set(file_path)
apply_config() apply_config()
browse_bot_config_btn = ttk.Button(bot_config_frame, text="浏览", command=browse_bot_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) browse_bot_config_btn.pack(side=tk.LEFT, padx=5)
# .env路径配置 # .env路径配置
env_frame = ttk.Frame(frame) env_frame = ttk.Frame(frame)
env_frame.pack(fill=tk.X, pady=5) env_frame.pack(fill=tk.X, pady=5)
env_label = ttk.Label(env_frame, text=".env路径:", font=("微软雅黑", 12)) env_label = ttk.Label(env_frame, text=".env路径:", font=("微软雅黑", 12))
env_label.pack(side=tk.LEFT, padx=5) env_label.pack(side=tk.LEFT, padx=5)
env_path = self.editor_config["config"].get("env_file", ".env") env_path = self.editor_config["config"].get("env_file", ".env")
if not os.path.isabs(env_path): if not os.path.isabs(env_path):
env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), env_path) env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), env_path)
env_var = tk.StringVar(value=env_path) env_var = tk.StringVar(value=env_path)
env_entry = ttk.Entry(env_frame, textvariable=env_var, font=("微软雅黑", 12)) env_entry = ttk.Entry(env_frame, textvariable=env_var, font=("微软雅黑", 12))
env_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) env_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
def browse_env(): def browse_env():
file_path = filedialog.askopenfilename( file_path = filedialog.askopenfilename(
title="选择.env文件", title="选择.env文件", filetypes=[("环境变量文件", "*.env"), ("所有文件", "*.*")]
filetypes=[("环境变量文件", "*.env"), ("所有文件", "*.*")]
) )
if file_path: if file_path:
env_var.set(file_path) env_var.set(file_path)
apply_config() apply_config()
browse_env_btn = ttk.Button(env_frame, text="浏览", command=browse_env) browse_env_btn = ttk.Button(env_frame, text="浏览", command=browse_env)
browse_env_btn.pack(side=tk.LEFT, padx=5) 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)

View File

@@ -1,9 +1,10 @@
import time import time
import os import os
from typing import List, Optional, Dict, Any from typing import Optional, Dict, Any
log_dir = "log/log_cycle_debug/" log_dir = "log/log_cycle_debug/"
class CycleDetail: class CycleDetail:
"""循环信息记录类""" """循环信息记录类"""
@@ -23,35 +24,40 @@ class CycleDetail:
def to_dict(self) -> Dict[str, Any]: def to_dict(self) -> Dict[str, Any]:
"""将循环信息转换为字典格式""" """将循环信息转换为字典格式"""
def convert_to_serializable(obj, depth=0, seen=None): def convert_to_serializable(obj, depth=0, seen=None):
if seen is None: if seen is None:
seen = set() seen = set()
# 防止递归过深 # 防止递归过深
if depth > 5: # 降低递归深度限制 if depth > 5: # 降低递归深度限制
return str(obj) return str(obj)
# 防止循环引用 # 防止循环引用
obj_id = id(obj) obj_id = id(obj)
if obj_id in seen: if obj_id in seen:
return str(obj) return str(obj)
seen.add(obj_id) seen.add(obj_id)
try: try:
if hasattr(obj, 'to_dict'): if hasattr(obj, "to_dict"):
# 对于有to_dict方法的对象直接调用其to_dict方法 # 对于有to_dict方法的对象直接调用其to_dict方法
return obj.to_dict() return obj.to_dict()
elif isinstance(obj, dict): elif isinstance(obj, dict):
# 对于字典,只保留基本类型和可序列化的值 # 对于字典,只保留基本类型和可序列化的值
return {k: convert_to_serializable(v, depth + 1, seen) return {
for k, v in obj.items() k: convert_to_serializable(v, depth + 1, seen)
if isinstance(k, (str, int, float, bool))} for k, v in obj.items()
if isinstance(k, (str, int, float, bool))
}
elif isinstance(obj, (list, tuple)): elif isinstance(obj, (list, tuple)):
# 对于列表和元组,只保留可序列化的元素 # 对于列表和元组,只保留可序列化的元素
return [convert_to_serializable(item, depth + 1, seen) return [
for item in obj convert_to_serializable(item, depth + 1, seen)
if not isinstance(item, (dict, list, tuple)) or for item in obj
isinstance(item, (str, int, float, bool, type(None)))] if not isinstance(item, (dict, list, tuple))
or isinstance(item, (str, int, float, bool, type(None)))
]
elif isinstance(obj, (str, int, float, bool, type(None))): elif isinstance(obj, (str, int, float, bool, type(None))):
return obj return obj
else: else:
@@ -74,19 +80,19 @@ class CycleDetail:
def complete_cycle(self): def complete_cycle(self):
"""完成循环,记录结束时间""" """完成循环,记录结束时间"""
self.end_time = time.time() self.end_time = time.time()
# 处理 prefix只保留中英文字符 # 处理 prefix只保留中英文字符
if not self.prefix: if not self.prefix:
self.prefix = "group" self.prefix = "group"
else: else:
# 只保留中文和英文字符 # 只保留中文和英文字符
self.prefix = ''.join(char for char in self.prefix if '\u4e00' <= char <= '\u9fff' or char.isascii()) self.prefix = "".join(char for char in self.prefix if "\u4e00" <= char <= "\u9fff" or char.isascii())
if not self.prefix: if not self.prefix:
self.prefix = "group" self.prefix = "group"
current_time_minute = time.strftime("%Y%m%d_%H%M", time.localtime()) current_time_minute = time.strftime("%Y%m%d_%H%M", time.localtime())
self.log_cycle_to_file(log_dir + self.prefix + f"/{current_time_minute}_cycle_" + str(self.cycle_id) + ".json") self.log_cycle_to_file(log_dir + self.prefix + f"/{current_time_minute}_cycle_" + str(self.cycle_id) + ".json")
def log_cycle_to_file(self, file_path: str): def log_cycle_to_file(self, file_path: str):
"""将循环信息写入文件""" """将循环信息写入文件"""
# 如果目录不存在,则创建目录 # 如果目录不存在,则创建目录
@@ -95,6 +101,7 @@ class CycleDetail:
os.makedirs(dir_name, exist_ok=True) os.makedirs(dir_name, exist_ok=True)
# 写入文件 # 写入文件
import json import json
with open(file_path, "a", encoding="utf-8") as f: with open(file_path, "a", encoding="utf-8") as f:
f.write(json.dumps(self.to_dict(), ensure_ascii=False) + "\n") f.write(json.dumps(self.to_dict(), ensure_ascii=False) + "\n")

View File

@@ -418,7 +418,9 @@ class HeartFChatting:
# 记录耗时 # 记录耗时
processor_time_costs[processor_name] = duration_since_parallel_start processor_time_costs[processor_name] = duration_since_parallel_start
except asyncio.TimeoutError: except asyncio.TimeoutError:
logger.info(f"{self.log_prefix} 处理器 {processor_name} 超时(>{global_config.focus_chat.processor_max_time}s已跳过") logger.info(
f"{self.log_prefix} 处理器 {processor_name} 超时(>{global_config.focus_chat.processor_max_time}s已跳过"
)
processor_time_costs[processor_name] = global_config.focus_chat.processor_max_time processor_time_costs[processor_name] = global_config.focus_chat.processor_max_time
except Exception as e: except Exception as e:
logger.error( logger.error(
@@ -462,7 +464,7 @@ class HeartFChatting:
} }
self.all_observations = observations self.all_observations = observations
with Timer("调整动作", cycle_timers): with Timer("调整动作", cycle_timers):
# 处理特殊的观察 # 处理特殊的观察
await self.action_modifier.modify_actions(observations=observations) await self.action_modifier.modify_actions(observations=observations)
@@ -476,26 +478,24 @@ class HeartFChatting:
with Timer("并行回忆和处理", cycle_timers): with Timer("并行回忆和处理", cycle_timers):
memory_task = asyncio.create_task(self.memory_activator.activate_memory(observations)) memory_task = asyncio.create_task(self.memory_activator.activate_memory(observations))
processor_task = asyncio.create_task(self._process_processors(observations, [])) processor_task = asyncio.create_task(self._process_processors(observations, []))
# 等待两个任务完成 # 等待两个任务完成
running_memorys, (all_plan_info, processor_time_costs) = await asyncio.gather(memory_task, processor_task) running_memorys, (all_plan_info, processor_time_costs) = await asyncio.gather(
memory_task, processor_task
)
else: else:
# 串行执行 # 串行执行
with Timer("回忆", cycle_timers): with Timer("回忆", cycle_timers):
running_memorys = await self.memory_activator.activate_memory(observations) running_memorys = await self.memory_activator.activate_memory(observations)
with Timer("执行 信息处理器", cycle_timers): with Timer("执行 信息处理器", cycle_timers):
all_plan_info, processor_time_costs = await self._process_processors( all_plan_info, processor_time_costs = await self._process_processors(observations, running_memorys)
observations, running_memorys
)
loop_processor_info = { loop_processor_info = {
"all_plan_info": all_plan_info, "all_plan_info": all_plan_info,
"processor_time_costs": processor_time_costs, "processor_time_costs": processor_time_costs,
} }
with Timer("规划器", cycle_timers): with Timer("规划器", cycle_timers):
plan_result = await self.action_planner.plan(all_plan_info, running_memorys) plan_result = await self.action_planner.plan(all_plan_info, running_memorys)

View File

@@ -30,7 +30,6 @@ class ActionModifier:
observations: Optional[List[Observation]] = None, observations: Optional[List[Observation]] = None,
**kwargs: Any, **kwargs: Any,
): ):
# 处理Observation对象 # 处理Observation对象
if observations: if observations:
# action_info = ActionInfo() # action_info = ActionInfo()
@@ -163,22 +162,34 @@ class ActionModifier:
if len(last_max_reply_num) >= max_reply_num and all(last_max_reply_num): if len(last_max_reply_num) >= max_reply_num and all(last_max_reply_num):
# 如果最近max_reply_num次都是reply直接移除 # 如果最近max_reply_num次都是reply直接移除
result["remove"].append("reply") result["remove"].append("reply")
logger.info(f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply直接移除") logger.info(
f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply直接移除"
)
elif len(last_max_reply_num) >= sec_thres_reply_num and all(last_max_reply_num[-sec_thres_reply_num:]): elif len(last_max_reply_num) >= sec_thres_reply_num and all(last_max_reply_num[-sec_thres_reply_num:]):
# 如果最近sec_thres_reply_num次都是reply40%概率移除 # 如果最近sec_thres_reply_num次都是reply40%概率移除
if random.random() < 0.4 / global_config.focus_chat.consecutive_replies: if random.random() < 0.4 / global_config.focus_chat.consecutive_replies:
result["remove"].append("reply") result["remove"].append("reply")
logger.info(f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply{0.4 / global_config.focus_chat.consecutive_replies}概率移除,移除") logger.info(
f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply{0.4 / global_config.focus_chat.consecutive_replies}概率移除,移除"
)
else: else:
logger.debug(f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply{0.4 / global_config.focus_chat.consecutive_replies}概率移除,不移除") logger.debug(
f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply{0.4 / global_config.focus_chat.consecutive_replies}概率移除,不移除"
)
elif len(last_max_reply_num) >= one_thres_reply_num and all(last_max_reply_num[-one_thres_reply_num:]): elif len(last_max_reply_num) >= one_thres_reply_num and all(last_max_reply_num[-one_thres_reply_num:]):
# 如果最近one_thres_reply_num次都是reply20%概率移除 # 如果最近one_thres_reply_num次都是reply20%概率移除
if random.random() < 0.2 / global_config.focus_chat.consecutive_replies: if random.random() < 0.2 / global_config.focus_chat.consecutive_replies:
result["remove"].append("reply") result["remove"].append("reply")
logger.info(f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply{0.2 / global_config.focus_chat.consecutive_replies}概率移除,移除") logger.info(
f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply{0.2 / global_config.focus_chat.consecutive_replies}概率移除,移除"
)
else: else:
logger.debug(f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply{0.2 / global_config.focus_chat.consecutive_replies}概率移除,不移除") logger.debug(
f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply{0.2 / global_config.focus_chat.consecutive_replies}概率移除,不移除"
)
else: else:
logger.debug(f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply无需移除") logger.debug(
f"最近{len(last_max_reply_num)}次回复中,有{no_reply_count}次no_reply{len(last_max_reply_num) - no_reply_count}次reply无需移除"
)
return result return result

View File

@@ -42,5 +42,5 @@ class ActionObservation:
"observe_id": self.observe_id, "observe_id": self.observe_id,
"last_observe_time": self.last_observe_time, "last_observe_time": self.last_observe_time,
"all_actions": self.all_actions, "all_actions": self.all_actions,
"all_using_actions": self.all_using_actions "all_using_actions": self.all_using_actions,
} }

View File

@@ -81,7 +81,7 @@ class ChattingObservation(Observation):
"person_list": self.person_list, "person_list": self.person_list,
"oldest_messages_str": self.oldest_messages_str, "oldest_messages_str": self.oldest_messages_str,
"compressor_prompt": self.compressor_prompt, "compressor_prompt": self.compressor_prompt,
"last_observe_time": self.last_observe_time "last_observe_time": self.last_observe_time,
} }
async def initialize(self): async def initialize(self):

View File

@@ -39,7 +39,7 @@ class HFCloopObservation:
responses_for_prompt = [] responses_for_prompt = []
cycle_last_reason = "" cycle_last_reason = ""
# 检查这最近的活动循环中有多少是连续的文本回复 (从最近的开始看) # 检查这最近的活动循环中有多少是连续的文本回复 (从最近的开始看)
for cycle in recent_active_cycles: for cycle in recent_active_cycles:
action_type = cycle.loop_plan_info["action_result"]["action_type"] action_type = cycle.loop_plan_info["action_result"]["action_type"]
@@ -57,29 +57,33 @@ class HFCloopObservation:
action_reasoning_str = f"你选择这个action的原因是:{action_reasoning}" action_reasoning_str = f"你选择这个action的原因是:{action_reasoning}"
else: else:
action_reasoning_str = "" action_reasoning_str = ""
if action_type == "reply": if action_type == "reply":
consecutive_text_replies += 1 consecutive_text_replies += 1
response_text = cycle.loop_plan_info["action_result"]["action_data"].get("text", "[空回复]") response_text = cycle.loop_plan_info["action_result"]["action_data"].get("text", "[空回复]")
responses_for_prompt.append(response_text) responses_for_prompt.append(response_text)
if is_taken: if is_taken:
action_detailed_str += f"{action_taken_time_str}时,你选择回复(action:{action_type},内容是:'{response_text}')。{action_reasoning_str}\n" action_detailed_str += f"{action_taken_time_str}时,你选择回复(action:{action_type},内容是:'{response_text}')。{action_reasoning_str}\n"
else: else:
action_detailed_str += f"{action_taken_time_str}时,你选择回复(action:{action_type},内容是:'{response_text}'),但是动作失败了。{action_reasoning_str}\n" action_detailed_str += f"{action_taken_time_str}时,你选择回复(action:{action_type},内容是:'{response_text}'),但是动作失败了。{action_reasoning_str}\n"
elif action_type == "no_reply": elif action_type == "no_reply":
action_detailed_str += f"{action_taken_time_str}时,你选择不回复(action:{action_type}){action_reasoning_str}\n" action_detailed_str += (
f"{action_taken_time_str}时,你选择不回复(action:{action_type}){action_reasoning_str}\n"
)
else: else:
if is_taken: if is_taken:
action_detailed_str += f"{action_taken_time_str}时,你选择执行了(action:{action_type}){action_reasoning_str}\n" action_detailed_str += (
f"{action_taken_time_str}时,你选择执行了(action:{action_type}){action_reasoning_str}\n"
)
else: else:
action_detailed_str += f"{action_taken_time_str}时,你选择执行了(action:{action_type}),但是动作失败了。{action_reasoning_str}\n" action_detailed_str += f"{action_taken_time_str}时,你选择执行了(action:{action_type}),但是动作失败了。{action_reasoning_str}\n"
if action_detailed_str: if action_detailed_str:
cycle_info_block = f"\n你最近做的事:\n{action_detailed_str}\n" cycle_info_block = f"\n你最近做的事:\n{action_detailed_str}\n"
else: else:
cycle_info_block = "\n" cycle_info_block = "\n"
# 根据连续文本回复的数量构建提示信息 # 根据连续文本回复的数量构建提示信息
if consecutive_text_replies >= 3: # 如果最近的三个活动都是文本回复 if consecutive_text_replies >= 3: # 如果最近的三个活动都是文本回复
cycle_info_block = f'你已经连续回复了三条消息(最近: "{responses_for_prompt[0]}",第二近: "{responses_for_prompt[1]}",第三近: "{responses_for_prompt[2]}")。你回复的有点多了,请注意' cycle_info_block = f'你已经连续回复了三条消息(最近: "{responses_for_prompt[0]}",第二近: "{responses_for_prompt[1]}",第三近: "{responses_for_prompt[2]}")。你回复的有点多了,请注意'
@@ -116,5 +120,5 @@ class HFCloopObservation:
"observe_id": self.observe_id, "observe_id": self.observe_id,
"last_observe_time": self.last_observe_time, "last_observe_time": self.last_observe_time,
# 不序列化history_loop避免循环引用 # 不序列化history_loop避免循环引用
"history_loop_count": len(self.history_loop) "history_loop_count": len(self.history_loop),
} }

View File

@@ -18,7 +18,7 @@ class Observation:
return { return {
"observe_info": self.observe_info, "observe_info": self.observe_info,
"observe_id": self.observe_id, "observe_id": self.observe_id,
"last_observe_time": self.last_observe_time "last_observe_time": self.last_observe_time,
} }
async def observe(self): async def observe(self):

View File

@@ -22,7 +22,7 @@ class StructureObservation:
"observe_id": self.observe_id, "observe_id": self.observe_id,
"last_observe_time": self.last_observe_time, "last_observe_time": self.last_observe_time,
"history_loop": self.history_loop, "history_loop": self.history_loop,
"structured_info": self.structured_info "structured_info": self.structured_info,
} }
def get_observe_info(self): def get_observe_info(self):

View File

@@ -39,6 +39,10 @@ class WorkingMemoryObservation:
"observe_info": self.observe_info, "observe_info": self.observe_info,
"observe_id": self.observe_id, "observe_id": self.observe_id,
"last_observe_time": self.last_observe_time, "last_observe_time": self.last_observe_time,
"working_memory": self.working_memory.to_dict() if hasattr(self.working_memory, 'to_dict') else str(self.working_memory), "working_memory": self.working_memory.to_dict()
"retrieved_working_memory": [item.to_dict() if hasattr(item, 'to_dict') else str(item) for item in self.retrieved_working_memory] if hasattr(self.working_memory, "to_dict")
else str(self.working_memory),
"retrieved_working_memory": [
item.to_dict() if hasattr(item, "to_dict") else str(item) for item in self.retrieved_working_memory
],
} }

View File

@@ -146,10 +146,10 @@ class FocusChatConfig(ConfigBase):
consecutive_replies: float = 1 consecutive_replies: float = 1
"""连续回复能力,值越高,麦麦连续回复的概率越高""" """连续回复能力,值越高,麦麦连续回复的概率越高"""
parallel_processing: bool = False parallel_processing: bool = False
"""是否允许处理器阶段和回忆阶段并行执行""" """是否允许处理器阶段和回忆阶段并行执行"""
processor_max_time: int = 25 processor_max_time: int = 25
"""处理器最大时间,单位秒,如果超过这个时间,处理器会自动停止""" """处理器最大时间,单位秒,如果超过这个时间,处理器会自动停止"""
@@ -166,13 +166,11 @@ class FocusChatProcessorConfig(ConfigBase):
working_memory_processor: bool = True working_memory_processor: bool = True
"""是否启用工作记忆处理器""" """是否启用工作记忆处理器"""
lite_chat_mind_processor: bool = False lite_chat_mind_processor: bool = False
"""是否启用轻量级聊天思维处理器可以节省token消耗和时间""" """是否启用轻量级聊天思维处理器可以节省token消耗和时间"""
@dataclass @dataclass
class ExpressionConfig(ConfigBase): class ExpressionConfig(ConfigBase):
"""表达配置类""" """表达配置类"""

View File

@@ -753,7 +753,7 @@ class LLMRequest:
response = await self._execute_request(endpoint="/chat/completions", payload=data, prompt=prompt) response = await self._execute_request(endpoint="/chat/completions", payload=data, prompt=prompt)
# 原样返回响应,不做处理 # 原样返回响应,不做处理
if len(response) == 3: if len(response) == 3:
content, reasoning_content, tool_calls = response content, reasoning_content, tool_calls = response
return content, (reasoning_content, self.model_name, tool_calls) return content, (reasoning_content, self.model_name, tool_calls)