mirror of https://github.com/Mai-with-u/MaiBot.git
Merge branch 'MaiM-with-u:dev' into dev
commit
1378cab5f0
|
|
@ -0,0 +1,660 @@
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk, messagebox
|
||||||
|
import tomli
|
||||||
|
import tomli_w
|
||||||
|
import os
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigEditor:
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
self.root.title("麦麦配置编辑器")
|
||||||
|
|
||||||
|
# 加载编辑器配置
|
||||||
|
self.load_editor_config()
|
||||||
|
|
||||||
|
# 设置窗口大小
|
||||||
|
self.root.geometry(f"{self.window_width}x{self.window_height}")
|
||||||
|
|
||||||
|
# 加载配置
|
||||||
|
self.load_config()
|
||||||
|
|
||||||
|
# 自动保存相关
|
||||||
|
self.last_save_time = time.time()
|
||||||
|
self.save_timer = None
|
||||||
|
self.save_lock = threading.Lock()
|
||||||
|
self.current_section = None # 当前编辑的节
|
||||||
|
self.pending_save = False # 是否有待保存的更改
|
||||||
|
|
||||||
|
# 存储控件的字典
|
||||||
|
self.widgets = {}
|
||||||
|
|
||||||
|
# 创建主框架
|
||||||
|
self.main_frame = ttk.Frame(self.root, padding="10")
|
||||||
|
self.main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
|
||||||
|
# 创建版本号显示
|
||||||
|
self.create_version_label()
|
||||||
|
|
||||||
|
# 创建左侧导航栏
|
||||||
|
self.create_navbar()
|
||||||
|
|
||||||
|
# 创建右侧编辑区
|
||||||
|
self.create_editor()
|
||||||
|
|
||||||
|
# 创建底部按钮
|
||||||
|
self.create_buttons()
|
||||||
|
|
||||||
|
# 配置网格权重
|
||||||
|
self.root.columnconfigure(0, weight=1)
|
||||||
|
self.root.rowconfigure(0, weight=1)
|
||||||
|
self.main_frame.columnconfigure(1, weight=1)
|
||||||
|
self.main_frame.rowconfigure(1, weight=1) # 修改为1,因为第0行是版本号
|
||||||
|
|
||||||
|
def load_editor_config(self):
|
||||||
|
"""加载编辑器配置"""
|
||||||
|
try:
|
||||||
|
editor_config_path = os.path.join(os.path.dirname(__file__), "configexe.toml")
|
||||||
|
with open(editor_config_path, "rb") as f:
|
||||||
|
self.editor_config = tomli.load(f) # 保存整个配置对象
|
||||||
|
|
||||||
|
# 设置配置路径
|
||||||
|
self.config_path = self.editor_config["config"]["bot_config_path"]
|
||||||
|
# 如果路径是相对路径,转换为绝对路径
|
||||||
|
if not os.path.isabs(self.config_path):
|
||||||
|
self.config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), self.config_path)
|
||||||
|
|
||||||
|
# 设置编辑器参数
|
||||||
|
self.window_width = self.editor_config["editor"]["window_width"]
|
||||||
|
self.window_height = self.editor_config["editor"]["window_height"]
|
||||||
|
self.save_delay = self.editor_config["editor"]["save_delay"]
|
||||||
|
|
||||||
|
# 加载翻译
|
||||||
|
self.translations = self.editor_config.get("translations", {})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("错误", f"加载编辑器配置失败: {str(e)}")
|
||||||
|
# 使用默认值
|
||||||
|
self.editor_config = {} # 初始化空配置
|
||||||
|
self.config_path = "config/bot_config.toml"
|
||||||
|
self.window_width = 1000
|
||||||
|
self.window_height = 800
|
||||||
|
self.save_delay = 1.0
|
||||||
|
self.translations = {}
|
||||||
|
|
||||||
|
def load_config(self):
|
||||||
|
try:
|
||||||
|
with open(self.config_path, "rb") as f:
|
||||||
|
self.config = tomli.load(f)
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("错误", f"加载配置文件失败: {str(e)}")
|
||||||
|
self.config = {}
|
||||||
|
|
||||||
|
def create_version_label(self):
|
||||||
|
"""创建版本号显示标签"""
|
||||||
|
version = self.config.get("inner", {}).get("version", "未知版本")
|
||||||
|
version_frame = ttk.Frame(self.main_frame)
|
||||||
|
version_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||||
|
|
||||||
|
version_label = ttk.Label(version_frame, text=f"麦麦版本:{version}", font=("", 10, "bold"))
|
||||||
|
version_label.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
def create_navbar(self):
|
||||||
|
# 创建左侧导航栏
|
||||||
|
self.nav_frame = ttk.Frame(self.main_frame, padding="5")
|
||||||
|
self.nav_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
|
||||||
|
# 创建导航树
|
||||||
|
self.tree = ttk.Treeview(self.nav_frame)
|
||||||
|
self.tree.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# 添加快捷设置节
|
||||||
|
self.tree.insert("", "end", text="快捷设置", values=("quick_settings",))
|
||||||
|
|
||||||
|
# 添加配置项到树
|
||||||
|
for section in self.config:
|
||||||
|
if section != "inner": # 跳过inner部分
|
||||||
|
# 获取节的中文名称
|
||||||
|
section_trans = self.translations.get("sections", {}).get(section, {})
|
||||||
|
section_name = section_trans.get("name", section)
|
||||||
|
self.tree.insert("", "end", text=section_name, values=(section,))
|
||||||
|
|
||||||
|
# 绑定选择事件
|
||||||
|
self.tree.bind("<<TreeviewSelect>>", self.on_section_select)
|
||||||
|
|
||||||
|
def create_editor(self):
|
||||||
|
# 创建右侧编辑区
|
||||||
|
self.editor_frame = ttk.Frame(self.main_frame, padding="5")
|
||||||
|
self.editor_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
|
||||||
|
# 创建编辑区标题
|
||||||
|
self.editor_title = ttk.Label(self.editor_frame, text="")
|
||||||
|
self.editor_title.pack(fill=tk.X)
|
||||||
|
|
||||||
|
# 创建编辑区内容
|
||||||
|
self.editor_content = ttk.Frame(self.editor_frame)
|
||||||
|
self.editor_content.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# 创建滚动条
|
||||||
|
self.scrollbar = ttk.Scrollbar(self.editor_content)
|
||||||
|
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
|
|
||||||
|
# 创建画布和框架
|
||||||
|
self.canvas = tk.Canvas(self.editor_content, yscrollcommand=self.scrollbar.set)
|
||||||
|
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
|
self.scrollbar.config(command=self.canvas.yview)
|
||||||
|
|
||||||
|
# 创建内容框架
|
||||||
|
self.content_frame = ttk.Frame(self.canvas)
|
||||||
|
self.canvas.create_window((0, 0), window=self.content_frame, anchor=tk.NW)
|
||||||
|
|
||||||
|
# 绑定画布大小变化事件
|
||||||
|
self.content_frame.bind("<Configure>", self.on_frame_configure)
|
||||||
|
self.canvas.bind("<Configure>", self.on_canvas_configure)
|
||||||
|
|
||||||
|
def on_frame_configure(self, event=None):
|
||||||
|
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
|
||||||
|
|
||||||
|
def on_canvas_configure(self, event):
|
||||||
|
# 更新内容框架的宽度以适应画布
|
||||||
|
self.canvas.itemconfig(self.canvas.find_withtag("all")[0], width=event.width)
|
||||||
|
|
||||||
|
def create_buttons(self):
|
||||||
|
# 创建底部按钮区
|
||||||
|
self.button_frame = ttk.Frame(self.main_frame, padding="5")
|
||||||
|
self.button_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E))
|
||||||
|
|
||||||
|
# 刷新按钮
|
||||||
|
self.refresh_button = ttk.Button(self.button_frame, text="刷新", command=self.refresh_config)
|
||||||
|
self.refresh_button.pack(side=tk.RIGHT, padx=5)
|
||||||
|
|
||||||
|
def create_widget_for_value(self, parent: ttk.Frame, key: str, value: Any, path: List[str]) -> None:
|
||||||
|
"""为不同类型的值创建对应的编辑控件"""
|
||||||
|
frame = ttk.Frame(parent)
|
||||||
|
frame.pack(fill=tk.X, padx=5, pady=2)
|
||||||
|
|
||||||
|
# --- 修改开始: 改进翻译查找逻辑 ---
|
||||||
|
full_config_path_key = ".".join(path + [key]) # 例如 "chinese_typo.enable"
|
||||||
|
|
||||||
|
item_name_to_display = key # 默认显示原始键名
|
||||||
|
item_desc_to_display = "" # 默认无描述
|
||||||
|
|
||||||
|
# 1. 尝试使用完整路径的特定翻译
|
||||||
|
specific_translation = self.translations.get("items", {}).get(full_config_path_key)
|
||||||
|
if specific_translation and specific_translation.get("name"):
|
||||||
|
item_name_to_display = specific_translation.get("name")
|
||||||
|
item_desc_to_display = specific_translation.get("description", "")
|
||||||
|
else:
|
||||||
|
# 2. 如果特定翻译未找到或没有name,尝试使用通用键名的翻译
|
||||||
|
generic_translation = self.translations.get("items", {}).get(key)
|
||||||
|
if generic_translation and generic_translation.get("name"):
|
||||||
|
item_name_to_display = generic_translation.get("name")
|
||||||
|
item_desc_to_display = generic_translation.get("description", "")
|
||||||
|
# --- 修改结束 ---
|
||||||
|
|
||||||
|
# 配置名(大号字体)
|
||||||
|
label = ttk.Label(frame, text=item_name_to_display, font=("", 20, "bold"))
|
||||||
|
label.grid(row=0, column=0, sticky=tk.W, padx=5, pady=(0, 0))
|
||||||
|
|
||||||
|
# 星星图标快捷设置(与配置名同一行)
|
||||||
|
content_col_offset_for_star = 1 # 星标按钮占一列
|
||||||
|
quick_settings = self.editor_config.get("editor", {}).get("quick_settings", {}).get("items", [])
|
||||||
|
already_in_quick = any(item.get("path") == full_config_path_key for item in quick_settings)
|
||||||
|
icon = "★" if already_in_quick else "☆"
|
||||||
|
icon_fg = "#FFD600" # 始终金色
|
||||||
|
|
||||||
|
def on_star_click():
|
||||||
|
self.toggle_quick_setting(
|
||||||
|
full_config_path_key, widget_type, item_name_to_display, item_desc_to_display, already_in_quick
|
||||||
|
)
|
||||||
|
# 立即刷新本分组
|
||||||
|
for widget in parent.winfo_children():
|
||||||
|
widget.destroy()
|
||||||
|
self.widgets.clear()
|
||||||
|
# 重新渲染本分组
|
||||||
|
if hasattr(self, "current_section") and self.current_section and self.current_section != "quick_settings":
|
||||||
|
self.create_section_widgets(
|
||||||
|
parent, self.current_section, self.config[self.current_section], [self.current_section]
|
||||||
|
)
|
||||||
|
elif hasattr(self, "current_section") and self.current_section == "quick_settings":
|
||||||
|
self.create_quick_settings_widgets() # 如果当前是快捷设置,也刷新它
|
||||||
|
|
||||||
|
pin_btn = ttk.Button(frame, text=icon, width=2, command=on_star_click)
|
||||||
|
pin_btn.grid(row=0, column=content_col_offset_for_star, sticky=tk.W, padx=5)
|
||||||
|
try:
|
||||||
|
pin_btn.configure(style="Pin.TButton")
|
||||||
|
style = ttk.Style()
|
||||||
|
style.configure("Pin.TButton", foreground=icon_fg)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 配置项描述(第二行)
|
||||||
|
desc_row = 1
|
||||||
|
if item_desc_to_display:
|
||||||
|
desc_label = ttk.Label(frame, text=item_desc_to_display, foreground="gray", font=("", 16))
|
||||||
|
desc_label.grid(
|
||||||
|
row=desc_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5, pady=(0, 4)
|
||||||
|
)
|
||||||
|
widget_row = desc_row + 1 # 内容控件在描述下方
|
||||||
|
else:
|
||||||
|
widget_row = desc_row # 内容控件直接在第二行
|
||||||
|
|
||||||
|
# 配置内容控件(第三行或第二行)
|
||||||
|
if path[0] == "inner":
|
||||||
|
value_label = ttk.Label(frame, text=str(value), font=("", 20))
|
||||||
|
value_label.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5)
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(value, bool):
|
||||||
|
# 布尔值使用复选框
|
||||||
|
var = tk.BooleanVar(value=value)
|
||||||
|
checkbox = ttk.Checkbutton(frame, variable=var, command=lambda: self.on_value_changed())
|
||||||
|
checkbox.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W, padx=5)
|
||||||
|
self.widgets[tuple(path + [key])] = var
|
||||||
|
widget_type = "bool"
|
||||||
|
|
||||||
|
elif isinstance(value, (int, float)):
|
||||||
|
# 数字使用数字输入框
|
||||||
|
var = tk.StringVar(value=str(value))
|
||||||
|
entry = ttk.Entry(frame, textvariable=var, font=("", 20))
|
||||||
|
entry.grid(row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5)
|
||||||
|
var.trace_add("write", lambda *args: self.on_value_changed())
|
||||||
|
self.widgets[tuple(path + [key])] = var
|
||||||
|
widget_type = "number"
|
||||||
|
|
||||||
|
elif isinstance(value, list):
|
||||||
|
# 列表使用每行一个输入框的形式
|
||||||
|
frame_list = ttk.Frame(frame)
|
||||||
|
frame_list.grid(
|
||||||
|
row=widget_row, column=0, columnspan=content_col_offset_for_star + 1, sticky=tk.W + tk.E, padx=5
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建添加和删除按钮
|
||||||
|
button_frame = ttk.Frame(frame_list)
|
||||||
|
button_frame.pack(side=tk.RIGHT, padx=5)
|
||||||
|
|
||||||
|
add_button = ttk.Button(
|
||||||
|
button_frame, text="+", width=3, command=lambda p=path + [key]: self.add_list_item(frame_list, p)
|
||||||
|
)
|
||||||
|
add_button.pack(side=tk.TOP, pady=2)
|
||||||
|
|
||||||
|
# 创建列表项框架
|
||||||
|
items_frame = ttk.Frame(frame_list)
|
||||||
|
items_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||||
|
|
||||||
|
# 存储所有输入框的变量
|
||||||
|
entry_vars = []
|
||||||
|
|
||||||
|
# 为每个列表项创建输入框
|
||||||
|
for i, item in enumerate(value):
|
||||||
|
self.create_list_item(items_frame, item, i, entry_vars, path + [key])
|
||||||
|
|
||||||
|
# 存储控件引用
|
||||||
|
self.widgets[tuple(path + [key])] = (items_frame, entry_vars)
|
||||||
|
widget_type = "list"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# 其他类型(字符串等)使用普通文本框
|
||||||
|
var = tk.StringVar(value=str(value))
|
||||||
|
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)
|
||||||
|
var.trace_add("write", lambda *args: self.on_value_changed())
|
||||||
|
self.widgets[tuple(path + [key])] = var
|
||||||
|
widget_type = "text"
|
||||||
|
|
||||||
|
def create_section_widgets(self, parent: ttk.Frame, section: str, data: Dict, path=None) -> None:
|
||||||
|
"""为配置节创建编辑控件"""
|
||||||
|
if path is None:
|
||||||
|
path = [section]
|
||||||
|
# 获取节的中文名称和描述
|
||||||
|
section_trans = self.translations.get("sections", {}).get(section, {})
|
||||||
|
section_name = section_trans.get("name", section)
|
||||||
|
section_desc = section_trans.get("description", "")
|
||||||
|
|
||||||
|
# 创建节的标签框架
|
||||||
|
section_frame = ttk.Frame(parent)
|
||||||
|
section_frame.pack(fill=tk.X, padx=5, pady=10)
|
||||||
|
|
||||||
|
# 创建节的名称标签
|
||||||
|
section_label = ttk.Label(section_frame, text=f"[{section_name}]", font=("", 12, "bold"))
|
||||||
|
section_label.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
# 创建节的描述标签
|
||||||
|
if section_desc:
|
||||||
|
desc_label = ttk.Label(section_frame, text=f"({section_desc})", foreground="gray")
|
||||||
|
desc_label.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
# 为每个配置项创建对应的控件
|
||||||
|
for key, value in data.items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
self.create_section_widgets(parent, key, value, path + [key])
|
||||||
|
else:
|
||||||
|
self.create_widget_for_value(parent, key, value, path)
|
||||||
|
|
||||||
|
def on_value_changed(self):
|
||||||
|
"""当值改变时触发自动保存"""
|
||||||
|
self.pending_save = True
|
||||||
|
current_time = time.time()
|
||||||
|
if current_time - self.last_save_time > self.save_delay:
|
||||||
|
if self.save_timer:
|
||||||
|
self.root.after_cancel(self.save_timer)
|
||||||
|
self.save_timer = self.root.after(int(self.save_delay * 1000), self.save_config)
|
||||||
|
|
||||||
|
def on_section_select(self, event):
|
||||||
|
# 如果有待保存的更改,先保存
|
||||||
|
if self.pending_save:
|
||||||
|
self.save_config()
|
||||||
|
|
||||||
|
selection = self.tree.selection()
|
||||||
|
if not selection:
|
||||||
|
return
|
||||||
|
|
||||||
|
section = self.tree.item(selection[0])["values"][0] # 使用values中的原始节名
|
||||||
|
self.current_section = section
|
||||||
|
|
||||||
|
# 获取节的中文名称
|
||||||
|
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():
|
||||||
|
widget.destroy()
|
||||||
|
|
||||||
|
# 清空控件字典
|
||||||
|
self.widgets.clear()
|
||||||
|
|
||||||
|
# 创建编辑控件
|
||||||
|
if section == "quick_settings":
|
||||||
|
self.create_quick_settings_widgets()
|
||||||
|
elif section in self.config:
|
||||||
|
self.create_section_widgets(self.content_frame, section, self.config[section])
|
||||||
|
|
||||||
|
def create_quick_settings_widgets(self):
|
||||||
|
"""创建快捷设置编辑界面"""
|
||||||
|
# 获取快捷设置配置
|
||||||
|
quick_settings = self.editor_config.get("editor", {}).get("quick_settings", {}).get("items", [])
|
||||||
|
|
||||||
|
# 创建快捷设置控件
|
||||||
|
for setting in quick_settings:
|
||||||
|
frame = ttk.Frame(self.content_frame)
|
||||||
|
frame.pack(fill=tk.X, padx=5, pady=2)
|
||||||
|
|
||||||
|
# 获取当前值
|
||||||
|
path = setting["path"].split(".")
|
||||||
|
current = self.config
|
||||||
|
for key in path[:-1]: # 除了最后一个键
|
||||||
|
current = current.get(key, {})
|
||||||
|
value = current.get(path[-1]) # 获取最后一个键的值
|
||||||
|
|
||||||
|
# 创建名称标签
|
||||||
|
name_label = ttk.Label(frame, text=setting["name"], font=("", 18))
|
||||||
|
name_label.pack(fill=tk.X, padx=5, pady=(2, 0))
|
||||||
|
|
||||||
|
# 创建描述标签
|
||||||
|
if setting.get("description"):
|
||||||
|
desc_label = ttk.Label(frame, text=setting["description"], foreground="gray", font=("", 16))
|
||||||
|
desc_label.pack(fill=tk.X, padx=5, pady=(0, 2))
|
||||||
|
|
||||||
|
# 根据类型创建不同的控件
|
||||||
|
setting_type = setting.get("type", "bool")
|
||||||
|
|
||||||
|
if setting_type == "bool":
|
||||||
|
value = bool(value) if value is not None else False
|
||||||
|
var = tk.BooleanVar(value=value)
|
||||||
|
checkbox = ttk.Checkbutton(
|
||||||
|
frame, text="", variable=var, command=lambda p=path, v=var: self.on_quick_setting_changed(p, v)
|
||||||
|
)
|
||||||
|
checkbox.pack(anchor=tk.W, padx=5, pady=(0, 5))
|
||||||
|
|
||||||
|
elif setting_type == "text":
|
||||||
|
value = str(value) if value is not None else ""
|
||||||
|
var = tk.StringVar(value=value)
|
||||||
|
entry = ttk.Entry(frame, textvariable=var, width=40, font=("", 18))
|
||||||
|
entry.pack(fill=tk.X, padx=5, pady=(0, 5))
|
||||||
|
var.trace_add("write", lambda *args, p=path, v=var: self.on_quick_setting_changed(p, v))
|
||||||
|
|
||||||
|
elif setting_type == "number":
|
||||||
|
value = str(value) if value is not None else "0"
|
||||||
|
var = tk.StringVar(value=value)
|
||||||
|
entry = ttk.Entry(frame, textvariable=var, width=10, font=("", 18))
|
||||||
|
entry.pack(fill=tk.X, padx=5, pady=(0, 5))
|
||||||
|
var.trace_add("write", lambda *args, p=path, v=var: self.on_quick_setting_changed(p, v))
|
||||||
|
|
||||||
|
elif setting_type == "list":
|
||||||
|
# 对于列表类型,创建一个按钮来打开编辑窗口
|
||||||
|
button = ttk.Button(
|
||||||
|
frame, text="编辑列表", command=lambda p=path, s=setting: self.open_list_editor(p, s)
|
||||||
|
)
|
||||||
|
button.pack(anchor=tk.W, padx=5, pady=(0, 5))
|
||||||
|
|
||||||
|
def create_list_item(self, parent, value, index, entry_vars, path):
|
||||||
|
"""创建单个列表项的输入框"""
|
||||||
|
item_frame = ttk.Frame(parent)
|
||||||
|
item_frame.pack(fill=tk.X, pady=1)
|
||||||
|
|
||||||
|
# 创建输入框
|
||||||
|
var = tk.StringVar(value=str(value))
|
||||||
|
entry = ttk.Entry(item_frame, textvariable=var)
|
||||||
|
entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
|
||||||
|
var.trace_add("write", lambda *args: self.on_value_changed())
|
||||||
|
|
||||||
|
# 创建删除按钮
|
||||||
|
del_button = ttk.Button(
|
||||||
|
item_frame,
|
||||||
|
text="-",
|
||||||
|
width=3,
|
||||||
|
command=lambda: self.remove_list_item(parent, item_frame, entry_vars, index, path),
|
||||||
|
)
|
||||||
|
del_button.pack(side=tk.RIGHT, padx=5)
|
||||||
|
|
||||||
|
# 存储变量引用
|
||||||
|
entry_vars.append(var)
|
||||||
|
|
||||||
|
def add_list_item(self, parent, path):
|
||||||
|
"""添加新的列表项"""
|
||||||
|
items_frame = parent.winfo_children()[1] # 获取列表项框架
|
||||||
|
entry_vars = self.widgets[tuple(path)][1] # 获取变量列表
|
||||||
|
|
||||||
|
# 创建新的列表项
|
||||||
|
self.create_list_item(items_frame, "", len(entry_vars), entry_vars, path)
|
||||||
|
self.on_value_changed()
|
||||||
|
|
||||||
|
def remove_list_item(self, parent, item_frame, entry_vars, index, path):
|
||||||
|
"""删除列表项"""
|
||||||
|
item_frame.destroy()
|
||||||
|
entry_vars.pop(index)
|
||||||
|
self.on_value_changed()
|
||||||
|
|
||||||
|
def get_widget_value(self, widget) -> Any:
|
||||||
|
"""获取控件的值"""
|
||||||
|
if isinstance(widget, tk.BooleanVar):
|
||||||
|
return widget.get()
|
||||||
|
elif isinstance(widget, tk.StringVar):
|
||||||
|
value = widget.get()
|
||||||
|
try:
|
||||||
|
# 尝试转换为数字
|
||||||
|
if "." in value:
|
||||||
|
return float(value)
|
||||||
|
return int(value)
|
||||||
|
except ValueError:
|
||||||
|
return value
|
||||||
|
elif isinstance(widget, tuple): # 列表类型
|
||||||
|
items_frame, entry_vars = widget
|
||||||
|
# 获取所有非空输入框的值
|
||||||
|
return [var.get() for var in entry_vars if var.get().strip()]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def save_config(self):
|
||||||
|
"""保存配置到文件"""
|
||||||
|
if not self.pending_save:
|
||||||
|
return
|
||||||
|
|
||||||
|
with self.save_lock:
|
||||||
|
try:
|
||||||
|
# 获取所有控件的值
|
||||||
|
for path, widget in self.widgets.items():
|
||||||
|
value = self.get_widget_value(widget)
|
||||||
|
# 更新配置
|
||||||
|
current = self.config
|
||||||
|
for key in path[:-1]:
|
||||||
|
current = current[key]
|
||||||
|
final_key = path[-1] # 直接用最后一个key
|
||||||
|
current[final_key] = value
|
||||||
|
|
||||||
|
# 保存到文件
|
||||||
|
with open(self.config_path, "wb") as f:
|
||||||
|
tomli_w.dump(self.config, f)
|
||||||
|
|
||||||
|
self.last_save_time = time.time()
|
||||||
|
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:
|
||||||
|
messagebox.showerror("错误", f"保存配置失败: {str(e)}")
|
||||||
|
|
||||||
|
def refresh_config(self):
|
||||||
|
# 如果有待保存的更改,先保存
|
||||||
|
if self.pending_save:
|
||||||
|
self.save_config()
|
||||||
|
|
||||||
|
self.load_config()
|
||||||
|
self.tree.delete(*self.tree.get_children())
|
||||||
|
for section in self.config:
|
||||||
|
# 获取节的中文名称
|
||||||
|
section_trans = self.translations.get("sections", {}).get(section, {})
|
||||||
|
section_name = section_trans.get("name", section)
|
||||||
|
self.tree.insert("", "end", text=section_name, values=(section,))
|
||||||
|
messagebox.showinfo("成功", "配置已刷新")
|
||||||
|
|
||||||
|
def open_list_editor(self, path, setting):
|
||||||
|
"""打开列表编辑窗口"""
|
||||||
|
# 创建新窗口
|
||||||
|
dialog = tk.Toplevel(self.root)
|
||||||
|
dialog.title(f"编辑 {setting['name']}")
|
||||||
|
dialog.geometry("400x300")
|
||||||
|
|
||||||
|
# 获取当前值
|
||||||
|
current = self.config
|
||||||
|
for key in path[:-1]:
|
||||||
|
current = current.get(key, {})
|
||||||
|
value = current.get(path[-1], [])
|
||||||
|
|
||||||
|
# 创建编辑区
|
||||||
|
frame = ttk.Frame(dialog, padding="10")
|
||||||
|
frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# 创建列表项框架
|
||||||
|
items_frame = ttk.Frame(frame)
|
||||||
|
items_frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# 存储所有输入框的变量
|
||||||
|
entry_vars = []
|
||||||
|
|
||||||
|
# 为每个列表项创建输入框
|
||||||
|
for i, item in enumerate(value):
|
||||||
|
self.create_list_item(items_frame, item, i, entry_vars, path)
|
||||||
|
|
||||||
|
# 创建按钮框架
|
||||||
|
button_frame = ttk.Frame(frame)
|
||||||
|
button_frame.pack(fill=tk.X, pady=10)
|
||||||
|
|
||||||
|
# 添加按钮
|
||||||
|
add_button = ttk.Button(button_frame, text="添加", command=lambda: self.add_list_item(items_frame, path))
|
||||||
|
add_button.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
# 保存按钮
|
||||||
|
save_button = ttk.Button(
|
||||||
|
button_frame, text="保存", command=lambda: self.save_list_editor(dialog, path, entry_vars)
|
||||||
|
)
|
||||||
|
save_button.pack(side=tk.RIGHT, padx=5)
|
||||||
|
|
||||||
|
def save_list_editor(self, dialog, path, entry_vars):
|
||||||
|
"""保存列表编辑窗口的内容"""
|
||||||
|
# 获取所有非空输入框的值
|
||||||
|
values = [var.get() for var in entry_vars if var.get().strip()]
|
||||||
|
|
||||||
|
# 更新配置
|
||||||
|
current = self.config
|
||||||
|
for key in path[:-1]:
|
||||||
|
if key not in current:
|
||||||
|
current[key] = {}
|
||||||
|
current = current[key]
|
||||||
|
current[path[-1]] = values
|
||||||
|
|
||||||
|
# 触发保存
|
||||||
|
self.on_value_changed()
|
||||||
|
|
||||||
|
# 关闭窗口
|
||||||
|
dialog.destroy()
|
||||||
|
|
||||||
|
def on_quick_setting_changed(self, path, var):
|
||||||
|
"""快捷设置值改变时的处理"""
|
||||||
|
# 更新配置
|
||||||
|
current = self.config
|
||||||
|
for key in path[:-1]:
|
||||||
|
if key not in current:
|
||||||
|
current[key] = {}
|
||||||
|
current = current[key]
|
||||||
|
# 根据变量类型设置值
|
||||||
|
if isinstance(var, tk.BooleanVar):
|
||||||
|
current[path[-1]] = var.get()
|
||||||
|
elif isinstance(var, tk.StringVar):
|
||||||
|
value = var.get()
|
||||||
|
try:
|
||||||
|
# 尝试转换为数字
|
||||||
|
if "." in value:
|
||||||
|
current[path[-1]] = float(value)
|
||||||
|
else:
|
||||||
|
current[path[-1]] = int(value)
|
||||||
|
except ValueError:
|
||||||
|
current[path[-1]] = value
|
||||||
|
# 触发保存
|
||||||
|
self.on_value_changed()
|
||||||
|
|
||||||
|
def toggle_quick_setting(self, full_path, widget_type, name, desc, already_in_quick):
|
||||||
|
quick_settings = (
|
||||||
|
self.editor_config.setdefault("editor", {}).setdefault("quick_settings", {}).setdefault("items", [])
|
||||||
|
)
|
||||||
|
if already_in_quick:
|
||||||
|
# 移除
|
||||||
|
self.editor_config["editor"]["quick_settings"]["items"] = [
|
||||||
|
item for item in quick_settings if item.get("path") != full_path
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
# 添加
|
||||||
|
quick_settings.append({"name": name, "description": desc, "path": full_path, "type": widget_type})
|
||||||
|
# 保存到configexe.toml
|
||||||
|
import tomli_w
|
||||||
|
import os
|
||||||
|
|
||||||
|
config_path = os.path.join(os.path.dirname(__file__), "configexe.toml")
|
||||||
|
with open(config_path, "wb") as f:
|
||||||
|
tomli_w.dump(self.editor_config, f)
|
||||||
|
self.refresh_quick_settings()
|
||||||
|
|
||||||
|
def refresh_quick_settings(self):
|
||||||
|
# 重新渲染快捷设置栏(如果当前在快捷设置页)
|
||||||
|
if self.current_section == "quick_settings":
|
||||||
|
for widget in self.content_frame.winfo_children():
|
||||||
|
widget.destroy()
|
||||||
|
self.widgets.clear()
|
||||||
|
self.create_quick_settings_widgets()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
root = tk.Tk()
|
||||||
|
_app = ConfigEditor(root)
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,497 @@
|
||||||
|
[config]
|
||||||
|
bot_config_path = "config/bot_config.toml"
|
||||||
|
|
||||||
|
[editor]
|
||||||
|
window_width = 1000
|
||||||
|
window_height = 800
|
||||||
|
save_delay = 1.0
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "性格细节"
|
||||||
|
description = "麦麦性格的细节描述,条数任意,不能为0"
|
||||||
|
path = "personality.personality_sides"
|
||||||
|
type = "list"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "身份细节"
|
||||||
|
description = "麦麦的身份特征描述,可以描述外貌、性别、身高、职业、属性等"
|
||||||
|
path = "identity.identity_detail"
|
||||||
|
type = "list"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "表达风格"
|
||||||
|
description = "麦麦说话的表达风格,表达习惯"
|
||||||
|
path = "expression.expression_style"
|
||||||
|
type = "text"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "聊天模式"
|
||||||
|
description = "麦麦的聊天模式:normal(普通模式)、focus(专注模式)、auto(自动模式)"
|
||||||
|
path = "chat.chat_mode"
|
||||||
|
type = "text"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "退出专注阈值"
|
||||||
|
description = "自动退出专注聊天的阈值,越低越容易退出专注聊天"
|
||||||
|
path = "chat.exit_focus_threshold"
|
||||||
|
type = "number"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "偷取表情包"
|
||||||
|
description = "是否偷取表情包,让麦麦可以发送她保存的这些表情包"
|
||||||
|
path = "emoji.steal_emoji"
|
||||||
|
type = "bool"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "核心性格"
|
||||||
|
description = "麦麦的核心性格描述,建议50字以内"
|
||||||
|
path = "personality.personality_core"
|
||||||
|
type = "text"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "自动专注阈值"
|
||||||
|
description = "自动切换到专注聊天的阈值,越低越容易进入专注聊天"
|
||||||
|
path = "chat.auto_focus_threshold"
|
||||||
|
type = "number"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "自我识别处理器"
|
||||||
|
description = "是否启用自我识别处理器"
|
||||||
|
path = "focus_chat_processor.self_identify_processor"
|
||||||
|
type = "bool"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "工具使用处理器"
|
||||||
|
description = "是否启用工具使用处理器"
|
||||||
|
path = "focus_chat_processor.tool_use_processor"
|
||||||
|
type = "bool"
|
||||||
|
|
||||||
|
[[editor.quick_settings.items]]
|
||||||
|
name = "工作记忆处理器"
|
||||||
|
description = "是否启用工作记忆处理器,不稳定,消耗量大"
|
||||||
|
path = "focus_chat_processor.working_memory_processor"
|
||||||
|
type = "bool"
|
||||||
|
|
||||||
|
[translations.sections.inner]
|
||||||
|
name = "版本"
|
||||||
|
description = "麦麦的内部配置,包含版本号等信息。此部分仅供显示,不可编辑。"
|
||||||
|
|
||||||
|
[translations.sections.bot]
|
||||||
|
name = "麦麦bot配置"
|
||||||
|
description = "麦麦的基本配置,包括QQ号、昵称和别名等基础信息"
|
||||||
|
|
||||||
|
[translations.sections.personality]
|
||||||
|
name = "人格"
|
||||||
|
description = "麦麦的性格设定,包括核心性格(建议50字以内)和细节描述"
|
||||||
|
|
||||||
|
[translations.sections.identity]
|
||||||
|
name = "身份特点"
|
||||||
|
description = "麦麦的身份特征,包括年龄、性别、外貌等描述,可以描述外貌、性别、身高、职业、属性等"
|
||||||
|
|
||||||
|
[translations.sections.expression]
|
||||||
|
name = "表达方式"
|
||||||
|
description = "麦麦的表达方式和学习设置,包括表达风格和表达学习功能"
|
||||||
|
|
||||||
|
[translations.sections.relationship]
|
||||||
|
name = "关系"
|
||||||
|
description = "麦麦与用户的关系设置,包括取名功能等"
|
||||||
|
|
||||||
|
[translations.sections.chat]
|
||||||
|
name = "聊天模式"
|
||||||
|
description = "麦麦的聊天模式和行为设置,包括普通模式、专注模式和自动模式"
|
||||||
|
|
||||||
|
[translations.sections.message_receive]
|
||||||
|
name = "消息接收"
|
||||||
|
description = "消息过滤和接收设置,可以根据规则过滤特定消息"
|
||||||
|
|
||||||
|
[translations.sections.normal_chat]
|
||||||
|
name = "普通聊天配置"
|
||||||
|
description = "普通聊天模式下的行为设置,包括回复概率、上下文长度、表情包使用等"
|
||||||
|
|
||||||
|
[translations.sections.focus_chat]
|
||||||
|
name = "专注聊天配置"
|
||||||
|
description = "专注聊天模式下的行为设置,包括思考间隔、上下文大小等"
|
||||||
|
|
||||||
|
[translations.sections.focus_chat_processor]
|
||||||
|
name = "专注聊天处理器"
|
||||||
|
description = "专注聊天模式下的处理器设置,包括自我识别、工具使用、工作记忆等功能"
|
||||||
|
|
||||||
|
[translations.sections.emoji]
|
||||||
|
name = "表情包"
|
||||||
|
description = "表情包相关的设置,包括最大注册数量、替换策略、检查间隔等"
|
||||||
|
|
||||||
|
[translations.sections.memory]
|
||||||
|
name = "记忆"
|
||||||
|
description = "麦麦的记忆系统设置,包括记忆构建、遗忘、整合等参数"
|
||||||
|
|
||||||
|
[translations.sections.mood]
|
||||||
|
name = "情绪"
|
||||||
|
description = "麦麦的情绪系统设置,仅在普通聊天模式下有效"
|
||||||
|
|
||||||
|
[translations.sections.keyword_reaction]
|
||||||
|
name = "关键词反应"
|
||||||
|
description = "针对特定关键词作出反应的设置,仅在普通聊天模式下有效"
|
||||||
|
|
||||||
|
[translations.sections.chinese_typo]
|
||||||
|
name = "错别字生成器"
|
||||||
|
description = "中文错别字生成器的设置,可以控制错别字生成的概率"
|
||||||
|
|
||||||
|
[translations.sections.response_splitter]
|
||||||
|
name = "回复分割器"
|
||||||
|
description = "回复分割器的设置,用于控制回复的长度和句子数量"
|
||||||
|
|
||||||
|
[translations.sections.model]
|
||||||
|
name = "模型"
|
||||||
|
description = "各种AI模型的设置,包括组件模型、普通聊天模型、专注聊天模型等"
|
||||||
|
|
||||||
|
[translations.sections.maim_message]
|
||||||
|
name = "消息服务"
|
||||||
|
description = "消息服务的设置,包括认证令牌、服务器配置等"
|
||||||
|
|
||||||
|
[translations.sections.telemetry]
|
||||||
|
name = "遥测"
|
||||||
|
description = "统计信息发送设置,用于统计全球麦麦的数量"
|
||||||
|
|
||||||
|
[translations.sections.experimental]
|
||||||
|
name = "实验功能"
|
||||||
|
description = "实验性功能的设置,包括调试显示、好友聊天等功能"
|
||||||
|
|
||||||
|
[translations.items.version]
|
||||||
|
name = "版本号"
|
||||||
|
description = "麦麦的版本号,格式:主版本号.次版本号.修订号。主版本号用于不兼容的API修改,次版本号用于向下兼容的功能性新增,修订号用于向下兼容的问题修正"
|
||||||
|
|
||||||
|
[translations.items.qq_account]
|
||||||
|
name = "QQ账号"
|
||||||
|
description = "麦麦的QQ账号"
|
||||||
|
|
||||||
|
[translations.items.nickname]
|
||||||
|
name = "昵称"
|
||||||
|
description = "麦麦的昵称"
|
||||||
|
|
||||||
|
[translations.items.alias_names]
|
||||||
|
name = "别名"
|
||||||
|
description = "麦麦的其他称呼"
|
||||||
|
|
||||||
|
[translations.items.personality_core]
|
||||||
|
name = "核心性格"
|
||||||
|
description = "麦麦的核心性格描述,建议50字以内"
|
||||||
|
|
||||||
|
[translations.items.personality_sides]
|
||||||
|
name = "性格细节"
|
||||||
|
description = "麦麦性格的细节描述,条数任意,不能为0"
|
||||||
|
|
||||||
|
[translations.items.identity_detail]
|
||||||
|
name = "身份细节"
|
||||||
|
description = "麦麦的身份特征描述,可以描述外貌、性别、身高、职业、属性等,条数任意,不能为0"
|
||||||
|
|
||||||
|
[translations.items.expression_style]
|
||||||
|
name = "表达风格"
|
||||||
|
description = "麦麦说话的表达风格,表达习惯"
|
||||||
|
|
||||||
|
[translations.items.enable_expression_learning]
|
||||||
|
name = "启用表达学习"
|
||||||
|
description = "是否启用表达学习功能,麦麦会学习人类说话风格"
|
||||||
|
|
||||||
|
[translations.items.learning_interval]
|
||||||
|
name = "学习间隔"
|
||||||
|
description = "表达学习的间隔时间(秒)"
|
||||||
|
|
||||||
|
[translations.items.give_name]
|
||||||
|
name = "取名功能"
|
||||||
|
description = "麦麦是否给其他人取名,关闭后无法使用禁言功能"
|
||||||
|
|
||||||
|
[translations.items.chat_mode]
|
||||||
|
name = "聊天模式"
|
||||||
|
description = "麦麦的聊天模式:normal(普通模式,token消耗较低)、focus(专注模式,token消耗较高)、auto(自动模式,根据消息内容自动切换)"
|
||||||
|
|
||||||
|
[translations.items.auto_focus_threshold]
|
||||||
|
name = "自动专注阈值"
|
||||||
|
description = "自动切换到专注聊天的阈值,越低越容易进入专注聊天"
|
||||||
|
|
||||||
|
[translations.items.exit_focus_threshold]
|
||||||
|
name = "退出专注阈值"
|
||||||
|
description = "自动退出专注聊天的阈值,越低越容易退出专注聊天"
|
||||||
|
|
||||||
|
[translations.items.ban_words]
|
||||||
|
name = "禁用词"
|
||||||
|
description = "需要过滤的词语列表"
|
||||||
|
|
||||||
|
[translations.items.ban_msgs_regex]
|
||||||
|
name = "禁用消息正则"
|
||||||
|
description = "需要过滤的消息正则表达式,匹配到的消息将被过滤"
|
||||||
|
|
||||||
|
[translations.items.normal_chat_first_probability]
|
||||||
|
name = "首要模型概率"
|
||||||
|
description = "麦麦回答时选择首要模型的概率(与之相对的,次要模型的概率为1 - normal_chat_first_probability)"
|
||||||
|
|
||||||
|
[translations.items.max_context_size]
|
||||||
|
name = "最大上下文长度"
|
||||||
|
description = "聊天上下文的最大长度"
|
||||||
|
|
||||||
|
[translations.items.emoji_chance]
|
||||||
|
name = "表情包概率"
|
||||||
|
description = "麦麦一般回复时使用表情包的概率,设置为1让麦麦自己决定发不发"
|
||||||
|
|
||||||
|
[translations.items.thinking_timeout]
|
||||||
|
name = "思考超时"
|
||||||
|
description = "麦麦最长思考时间,超过这个时间的思考会放弃(往往是api反应太慢)"
|
||||||
|
|
||||||
|
[translations.items.willing_mode]
|
||||||
|
name = "回复意愿模式"
|
||||||
|
description = "回复意愿的计算模式:经典模式(classical)、mxp模式(mxp)、自定义模式(custom)"
|
||||||
|
|
||||||
|
[translations.items.talk_frequency]
|
||||||
|
name = "回复频率"
|
||||||
|
description = "麦麦回复频率,一般为1,默认频率下,30分钟麦麦回复30条(约数)"
|
||||||
|
|
||||||
|
[translations.items.response_willing_amplifier]
|
||||||
|
name = "回复意愿放大系数"
|
||||||
|
description = "麦麦回复意愿放大系数,一般为1"
|
||||||
|
|
||||||
|
[translations.items.response_interested_rate_amplifier]
|
||||||
|
name = "兴趣度放大系数"
|
||||||
|
description = "麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数"
|
||||||
|
|
||||||
|
[translations.items.emoji_response_penalty]
|
||||||
|
name = "表情包回复惩罚"
|
||||||
|
description = "表情包回复惩罚系数,设为0为不回复单个表情包,减少单独回复表情包的概率"
|
||||||
|
|
||||||
|
[translations.items.mentioned_bot_inevitable_reply]
|
||||||
|
name = "提及必回"
|
||||||
|
description = "被提及时是否必然回复"
|
||||||
|
|
||||||
|
[translations.items.at_bot_inevitable_reply]
|
||||||
|
name = "@必回"
|
||||||
|
description = "被@时是否必然回复"
|
||||||
|
|
||||||
|
[translations.items.down_frequency_rate]
|
||||||
|
name = "降低频率系数"
|
||||||
|
description = "降低回复频率的群组回复意愿降低系数(除法)"
|
||||||
|
|
||||||
|
[translations.items.talk_frequency_down_groups]
|
||||||
|
name = "降低频率群组"
|
||||||
|
description = "需要降低回复频率的群组列表"
|
||||||
|
|
||||||
|
[translations.items.think_interval]
|
||||||
|
name = "思考间隔"
|
||||||
|
description = "思考的时间间隔(秒),可以有效减少消耗"
|
||||||
|
|
||||||
|
[translations.items.observation_context_size]
|
||||||
|
name = "观察上下文大小"
|
||||||
|
description = "观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖"
|
||||||
|
|
||||||
|
[translations.items.compressed_length]
|
||||||
|
name = "压缩长度"
|
||||||
|
description = "不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度会压缩,最短压缩长度为5"
|
||||||
|
|
||||||
|
[translations.items.compress_length_limit]
|
||||||
|
name = "压缩限制"
|
||||||
|
description = "最多压缩份数,超过该数值的压缩上下文会被删除"
|
||||||
|
|
||||||
|
[translations.items.self_identify_processor]
|
||||||
|
name = "自我识别处理器"
|
||||||
|
description = "是否启用自我识别处理器"
|
||||||
|
|
||||||
|
[translations.items.tool_use_processor]
|
||||||
|
name = "工具使用处理器"
|
||||||
|
description = "是否启用工具使用处理器"
|
||||||
|
|
||||||
|
[translations.items.working_memory_processor]
|
||||||
|
name = "工作记忆处理器"
|
||||||
|
description = "是否启用工作记忆处理器,不稳定,消耗量大"
|
||||||
|
|
||||||
|
[translations.items.max_reg_num]
|
||||||
|
name = "最大注册数"
|
||||||
|
description = "表情包最大注册数量"
|
||||||
|
|
||||||
|
[translations.items.do_replace]
|
||||||
|
name = "启用替换"
|
||||||
|
description = "开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包"
|
||||||
|
|
||||||
|
[translations.items.check_interval]
|
||||||
|
name = "检查间隔"
|
||||||
|
description = "检查表情包(注册,破损,删除)的时间间隔(分钟)"
|
||||||
|
|
||||||
|
[translations.items.save_pic]
|
||||||
|
name = "保存图片"
|
||||||
|
description = "是否保存表情包图片"
|
||||||
|
|
||||||
|
[translations.items.cache_emoji]
|
||||||
|
name = "缓存表情包"
|
||||||
|
description = "是否缓存表情包"
|
||||||
|
|
||||||
|
[translations.items.steal_emoji]
|
||||||
|
name = "偷取表情包"
|
||||||
|
description = "是否偷取表情包,让麦麦可以发送她保存的这些表情包"
|
||||||
|
|
||||||
|
[translations.items.content_filtration]
|
||||||
|
name = "内容过滤"
|
||||||
|
description = "是否启用表情包过滤,只有符合该要求的表情包才会被保存"
|
||||||
|
|
||||||
|
[translations.items.filtration_prompt]
|
||||||
|
name = "过滤要求"
|
||||||
|
description = "表情包过滤要求,只有符合该要求的表情包才会被保存"
|
||||||
|
|
||||||
|
[translations.items.memory_build_interval]
|
||||||
|
name = "记忆构建间隔"
|
||||||
|
description = "记忆构建间隔(秒),间隔越低,麦麦学习越多,但是冗余信息也会增多"
|
||||||
|
|
||||||
|
[translations.items.memory_build_distribution]
|
||||||
|
name = "记忆构建分布"
|
||||||
|
description = "记忆构建分布,参数:分布1均值,标准差,权重,分布2均值,标准差,权重"
|
||||||
|
|
||||||
|
[translations.items.memory_build_sample_num]
|
||||||
|
name = "采样数量"
|
||||||
|
description = "采样数量,数值越高记忆采样次数越多"
|
||||||
|
|
||||||
|
[translations.items.memory_build_sample_length]
|
||||||
|
name = "采样长度"
|
||||||
|
description = "采样长度,数值越高一段记忆内容越丰富"
|
||||||
|
|
||||||
|
[translations.items.memory_compress_rate]
|
||||||
|
name = "记忆压缩率"
|
||||||
|
description = "记忆压缩率,控制记忆精简程度,建议保持默认,调高可以获得更多信息,但是冗余信息也会增多"
|
||||||
|
|
||||||
|
[translations.items.forget_memory_interval]
|
||||||
|
name = "记忆遗忘间隔"
|
||||||
|
description = "记忆遗忘间隔(秒),间隔越低,麦麦遗忘越频繁,记忆更精简,但更难学习"
|
||||||
|
|
||||||
|
[translations.items.memory_forget_time]
|
||||||
|
name = "遗忘时间"
|
||||||
|
description = "多长时间后的记忆会被遗忘(小时)"
|
||||||
|
|
||||||
|
[translations.items.memory_forget_percentage]
|
||||||
|
name = "遗忘比例"
|
||||||
|
description = "记忆遗忘比例,控制记忆遗忘程度,越大遗忘越多,建议保持默认"
|
||||||
|
|
||||||
|
[translations.items.consolidate_memory_interval]
|
||||||
|
name = "记忆整合间隔"
|
||||||
|
description = "记忆整合间隔(秒),间隔越低,麦麦整合越频繁,记忆更精简"
|
||||||
|
|
||||||
|
[translations.items.consolidation_similarity_threshold]
|
||||||
|
name = "整合相似度阈值"
|
||||||
|
description = "相似度阈值"
|
||||||
|
|
||||||
|
[translations.items.consolidation_check_percentage]
|
||||||
|
name = "整合检查比例"
|
||||||
|
description = "检查节点比例"
|
||||||
|
|
||||||
|
[translations.items.memory_ban_words]
|
||||||
|
name = "记忆禁用词"
|
||||||
|
description = "不希望记忆的词,已经记忆的不会受到影响"
|
||||||
|
|
||||||
|
[translations.items.mood_update_interval]
|
||||||
|
name = "情绪更新间隔"
|
||||||
|
description = "情绪更新间隔(秒),仅在普通聊天模式下有效"
|
||||||
|
|
||||||
|
[translations.items.mood_decay_rate]
|
||||||
|
name = "情绪衰减率"
|
||||||
|
description = "情绪衰减率"
|
||||||
|
|
||||||
|
[translations.items.mood_intensity_factor]
|
||||||
|
name = "情绪强度因子"
|
||||||
|
description = "情绪强度因子"
|
||||||
|
|
||||||
|
[translations.items.enable]
|
||||||
|
name = "启用关键词反应"
|
||||||
|
description = "关键词反应功能的总开关,仅在普通聊天模式下有效"
|
||||||
|
|
||||||
|
[translations.items.chinese_typo_enable]
|
||||||
|
name = "启用错别字"
|
||||||
|
description = "是否启用中文错别字生成器"
|
||||||
|
|
||||||
|
[translations.items.error_rate]
|
||||||
|
name = "错误率"
|
||||||
|
description = "单字替换概率"
|
||||||
|
|
||||||
|
[translations.items.min_freq]
|
||||||
|
name = "最小字频"
|
||||||
|
description = "最小字频阈值"
|
||||||
|
|
||||||
|
[translations.items.tone_error_rate]
|
||||||
|
name = "声调错误率"
|
||||||
|
description = "声调错误概率"
|
||||||
|
|
||||||
|
[translations.items.word_replace_rate]
|
||||||
|
name = "整词替换率"
|
||||||
|
description = "整词替换概率"
|
||||||
|
|
||||||
|
[translations.items.splitter_enable]
|
||||||
|
name = "启用分割器"
|
||||||
|
description = "是否启用回复分割器"
|
||||||
|
|
||||||
|
[translations.items.max_length]
|
||||||
|
name = "最大长度"
|
||||||
|
description = "回复允许的最大长度"
|
||||||
|
|
||||||
|
[translations.items.max_sentence_num]
|
||||||
|
name = "最大句子数"
|
||||||
|
description = "回复允许的最大句子数"
|
||||||
|
|
||||||
|
[translations.items.enable_kaomoji_protection]
|
||||||
|
name = "启用颜文字保护"
|
||||||
|
description = "是否启用颜文字保护"
|
||||||
|
|
||||||
|
[translations.items.model_max_output_length]
|
||||||
|
name = "最大输出长度"
|
||||||
|
description = "模型单次返回的最大token数"
|
||||||
|
|
||||||
|
[translations.items.auth_token]
|
||||||
|
name = "认证令牌"
|
||||||
|
description = "用于API验证的令牌列表,为空则不启用验证"
|
||||||
|
|
||||||
|
[translations.items.use_custom]
|
||||||
|
name = "使用自定义"
|
||||||
|
description = "是否启用自定义的maim_message服务器,注意这需要设置新的端口,不能与.env重复"
|
||||||
|
|
||||||
|
[translations.items.host]
|
||||||
|
name = "主机地址"
|
||||||
|
description = "服务器主机地址"
|
||||||
|
|
||||||
|
[translations.items.port]
|
||||||
|
name = "端口"
|
||||||
|
description = "服务器端口"
|
||||||
|
|
||||||
|
[translations.items.mode]
|
||||||
|
name = "模式"
|
||||||
|
description = "连接模式:ws或tcp"
|
||||||
|
|
||||||
|
[translations.items.use_wss]
|
||||||
|
name = "使用WSS"
|
||||||
|
description = "是否使用WSS安全连接,只支持ws模式"
|
||||||
|
|
||||||
|
[translations.items.cert_file]
|
||||||
|
name = "证书文件"
|
||||||
|
description = "SSL证书文件路径,仅在use_wss=true时有效"
|
||||||
|
|
||||||
|
[translations.items.key_file]
|
||||||
|
name = "密钥文件"
|
||||||
|
description = "SSL密钥文件路径,仅在use_wss=true时有效"
|
||||||
|
|
||||||
|
[translations.items.telemetry_enable]
|
||||||
|
name = "启用遥测"
|
||||||
|
description = "是否发送统计信息,主要是看全球有多少只麦麦"
|
||||||
|
|
||||||
|
[translations.items.debug_show_chat_mode]
|
||||||
|
name = "显示聊天模式"
|
||||||
|
description = "是否在回复后显示当前聊天模式"
|
||||||
|
|
||||||
|
[translations.items.enable_friend_chat]
|
||||||
|
name = "启用好友聊天"
|
||||||
|
description = "是否启用好友聊天功能"
|
||||||
|
|
||||||
|
[translations.items.pfc_chatting]
|
||||||
|
name = "PFC聊天"
|
||||||
|
description = "暂时无效"
|
||||||
|
|
||||||
|
[translations.items."response_splitter.enable"]
|
||||||
|
name = "启用分割器"
|
||||||
|
description = "是否启用回复分割器"
|
||||||
|
|
||||||
|
[translations.items."chinese_typo.enable"]
|
||||||
|
name = "启用错别字"
|
||||||
|
description = "是否启用中文错别字生成器"
|
||||||
|
|
||||||
|
[translations.items."keyword_reaction.enable"]
|
||||||
|
name = "启用关键词反应"
|
||||||
|
description = "关键词反应功能的总开关,仅在普通聊天模式下有效"
|
||||||
|
|
@ -199,7 +199,7 @@ class MongoToSQLiteMigrator:
|
||||||
"timestamp": "timestamp",
|
"timestamp": "timestamp",
|
||||||
},
|
},
|
||||||
enable_validation=True, # 禁用数据验证"
|
enable_validation=True, # 禁用数据验证"
|
||||||
unique_fields=["user_id", "prompt_tokens","completion_tokens","total_tokens","cost"], # 组合唯一性
|
unique_fields=["user_id", "prompt_tokens", "completion_tokens", "total_tokens", "cost"], # 组合唯一性
|
||||||
),
|
),
|
||||||
# 消息迁移配置
|
# 消息迁移配置
|
||||||
MigrationConfig(
|
MigrationConfig(
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class DefaultExpressor:
|
||||||
model=global_config.model.focus_expressor,
|
model=global_config.model.focus_expressor,
|
||||||
# temperature=global_config.model.focus_expressor["temp"],
|
# temperature=global_config.model.focus_expressor["temp"],
|
||||||
max_tokens=256,
|
max_tokens=256,
|
||||||
request_type="focus_expressor",
|
request_type="focus.expressor",
|
||||||
)
|
)
|
||||||
self.heart_fc_sender = HeartFCSender()
|
self.heart_fc_sender = HeartFCSender()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class ExpressionLearner:
|
||||||
model=global_config.model.focus_expressor,
|
model=global_config.model.focus_expressor,
|
||||||
temperature=0.1,
|
temperature=0.1,
|
||||||
max_tokens=256,
|
max_tokens=256,
|
||||||
request_type="learn_expression",
|
request_type="expressor.learner",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_expression_by_chat_id(self, chat_id: str) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]:
|
async def get_expression_by_chat_id(self, chat_id: str) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]:
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,19 @@ from src.chat.focus_chat.info.info_base import InfoBase
|
||||||
from src.chat.focus_chat.info_processors.chattinginfo_processor import ChattingInfoProcessor
|
from src.chat.focus_chat.info_processors.chattinginfo_processor import ChattingInfoProcessor
|
||||||
from src.chat.focus_chat.info_processors.mind_processor import MindProcessor
|
from src.chat.focus_chat.info_processors.mind_processor import MindProcessor
|
||||||
from src.chat.focus_chat.info_processors.working_memory_processor import WorkingMemoryProcessor
|
from src.chat.focus_chat.info_processors.working_memory_processor import WorkingMemoryProcessor
|
||||||
from src.chat.focus_chat.info_processors.action_processor import ActionProcessor
|
|
||||||
|
# from src.chat.focus_chat.info_processors.action_processor import ActionProcessor
|
||||||
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
||||||
from src.chat.heart_flow.observation.working_observation import WorkingMemoryObservation
|
from src.chat.heart_flow.observation.working_observation import WorkingMemoryObservation
|
||||||
from src.chat.heart_flow.observation.structure_observation import StructureObservation
|
from src.chat.heart_flow.observation.structure_observation import StructureObservation
|
||||||
|
from src.chat.heart_flow.observation.actions_observation import ActionObservation
|
||||||
from src.chat.focus_chat.info_processors.tool_processor import ToolProcessor
|
from src.chat.focus_chat.info_processors.tool_processor import ToolProcessor
|
||||||
from src.chat.focus_chat.expressors.default_expressor import DefaultExpressor
|
from src.chat.focus_chat.expressors.default_expressor import DefaultExpressor
|
||||||
from src.chat.focus_chat.memory_activator import MemoryActivator
|
from src.chat.focus_chat.memory_activator import MemoryActivator
|
||||||
from src.chat.focus_chat.info_processors.base_processor import BaseProcessor
|
from src.chat.focus_chat.info_processors.base_processor import BaseProcessor
|
||||||
from src.chat.focus_chat.info_processors.self_processor import SelfProcessor
|
from src.chat.focus_chat.info_processors.self_processor import SelfProcessor
|
||||||
from src.chat.focus_chat.planners.planner import ActionPlanner
|
from src.chat.focus_chat.planners.planner import ActionPlanner
|
||||||
|
from src.chat.focus_chat.planners.modify_actions import ActionModifier
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
from src.chat.focus_chat.planners.action_manager import ActionManager
|
||||||
from src.chat.focus_chat.working_memory.working_memory import WorkingMemory
|
from src.chat.focus_chat.working_memory.working_memory import WorkingMemory
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
|
|
@ -41,7 +44,7 @@ PROCESSOR_CLASSES = {
|
||||||
"ToolProcessor": (ToolProcessor, "tool_use_processor"),
|
"ToolProcessor": (ToolProcessor, "tool_use_processor"),
|
||||||
"WorkingMemoryProcessor": (WorkingMemoryProcessor, "working_memory_processor"),
|
"WorkingMemoryProcessor": (WorkingMemoryProcessor, "working_memory_processor"),
|
||||||
"SelfProcessor": (SelfProcessor, "self_identify_processor"),
|
"SelfProcessor": (SelfProcessor, "self_identify_processor"),
|
||||||
"ActionProcessor": (ActionProcessor, "action_processor"), # 这个处理器不需要配置键名,默认启用
|
# "ActionProcessor": (ActionProcessor, "action_processor"), # 这个处理器不需要配置键名,默认启用
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -129,8 +132,10 @@ class HeartFChatting:
|
||||||
self.expressor = DefaultExpressor(chat_id=self.stream_id)
|
self.expressor = DefaultExpressor(chat_id=self.stream_id)
|
||||||
self.action_manager = ActionManager()
|
self.action_manager = ActionManager()
|
||||||
self.action_planner = ActionPlanner(log_prefix=self.log_prefix, action_manager=self.action_manager)
|
self.action_planner = ActionPlanner(log_prefix=self.log_prefix, action_manager=self.action_manager)
|
||||||
|
self.action_modifier = ActionModifier(action_manager=self.action_manager)
|
||||||
|
self.action_observation = ActionObservation(observe_id=self.stream_id)
|
||||||
|
|
||||||
self.hfcloop_observation.set_action_manager(self.action_manager)
|
self.action_observation.set_action_manager(self.action_manager)
|
||||||
|
|
||||||
self.all_observations = observations
|
self.all_observations = observations
|
||||||
|
|
||||||
|
|
@ -335,7 +340,9 @@ class HeartFChatting:
|
||||||
for pname, ptime in processor_time_costs.items():
|
for pname, ptime in processor_time_costs.items():
|
||||||
formatted_ptime = f"{ptime * 1000:.2f}毫秒" if ptime < 1 else f"{ptime:.2f}秒"
|
formatted_ptime = f"{ptime * 1000:.2f}毫秒" if ptime < 1 else f"{ptime:.2f}秒"
|
||||||
processor_time_strings.append(f"{pname}: {formatted_ptime}")
|
processor_time_strings.append(f"{pname}: {formatted_ptime}")
|
||||||
processor_time_log = ("\n各处理器耗时: " + "; ".join(processor_time_strings)) if processor_time_strings else ""
|
processor_time_log = (
|
||||||
|
("\n各处理器耗时: " + "; ".join(processor_time_strings)) if processor_time_strings else ""
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 第{self._current_cycle.cycle_id}次思考,"
|
f"{self.log_prefix} 第{self._current_cycle.cycle_id}次思考,"
|
||||||
|
|
@ -470,8 +477,16 @@ class HeartFChatting:
|
||||||
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):
|
||||||
|
# 处理特殊的观察
|
||||||
|
await self.action_modifier.modify_actions(observations=observations, running_memorys=running_memorys)
|
||||||
|
await self.action_observation.observe()
|
||||||
|
observations.append(self.action_observation)
|
||||||
|
|
||||||
with Timer("执行 信息处理器", cycle_timers):
|
with Timer("执行 信息处理器", cycle_timers):
|
||||||
all_plan_info, processor_time_costs = await self._process_processors(observations, running_memorys, cycle_timers)
|
all_plan_info, processor_time_costs = await self._process_processors(
|
||||||
|
observations, running_memorys, cycle_timers
|
||||||
|
)
|
||||||
|
|
||||||
loop_processor_info = {
|
loop_processor_info = {
|
||||||
"all_plan_info": all_plan_info,
|
"all_plan_info": all_plan_info,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from datetime import datetime
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from src.llm_models.utils_model import LLMRequest
|
from src.llm_models.utils_model import LLMRequest
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
|
import asyncio
|
||||||
|
|
||||||
logger = get_logger("processor")
|
logger = get_logger("processor")
|
||||||
|
|
||||||
|
|
@ -28,7 +29,10 @@ class ChattingInfoProcessor(BaseProcessor):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
# TODO: API-Adapter修改标记
|
# TODO: API-Adapter修改标记
|
||||||
self.model_summary = LLMRequest(
|
self.model_summary = LLMRequest(
|
||||||
model=global_config.model.utils_small, temperature=0.7, max_tokens=300, request_type="chat_observation"
|
model=global_config.model.utils_small,
|
||||||
|
temperature=0.7,
|
||||||
|
max_tokens=300,
|
||||||
|
request_type="focus.observation.chat",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def process_info(
|
async def process_info(
|
||||||
|
|
@ -59,7 +63,8 @@ class ChattingInfoProcessor(BaseProcessor):
|
||||||
|
|
||||||
obs_info = ObsInfo()
|
obs_info = ObsInfo()
|
||||||
|
|
||||||
await self.chat_compress(obs)
|
# 改为异步任务,不阻塞主流程
|
||||||
|
asyncio.create_task(self.chat_compress(obs))
|
||||||
|
|
||||||
# 设置说话消息
|
# 设置说话消息
|
||||||
if hasattr(obs, "talking_message_str"):
|
if hasattr(obs, "talking_message_str"):
|
||||||
|
|
@ -95,15 +100,20 @@ class ChattingInfoProcessor(BaseProcessor):
|
||||||
return processed_infos
|
return processed_infos
|
||||||
|
|
||||||
async def chat_compress(self, obs: ChattingObservation):
|
async def chat_compress(self, obs: ChattingObservation):
|
||||||
|
log_msg = ""
|
||||||
if obs.compressor_prompt:
|
if obs.compressor_prompt:
|
||||||
summary = ""
|
summary = ""
|
||||||
try:
|
try:
|
||||||
summary_result, _, _ = await self.model_summary.generate_response(obs.compressor_prompt)
|
summary_result, _, _ = await self.model_summary.generate_response(obs.compressor_prompt)
|
||||||
summary = "没有主题的闲聊" # 默认值
|
summary = "没有主题的闲聊"
|
||||||
if summary_result: # 确保结果不为空
|
if summary_result:
|
||||||
summary = summary_result
|
summary = summary_result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"总结主题失败 for chat {obs.chat_id}: {e}")
|
log_msg = f"总结主题失败 for chat {obs.chat_id}: {e}"
|
||||||
|
logger.error(log_msg)
|
||||||
|
else:
|
||||||
|
log_msg = f"chat_compress 完成 for chat {obs.chat_id}, summary: {summary}"
|
||||||
|
logger.info(log_msg)
|
||||||
|
|
||||||
mid_memory = {
|
mid_memory = {
|
||||||
"id": str(int(datetime.now().timestamp())),
|
"id": str(int(datetime.now().timestamp())),
|
||||||
|
|
@ -130,3 +140,5 @@ class ChattingInfoProcessor(BaseProcessor):
|
||||||
obs.compressor_prompt = ""
|
obs.compressor_prompt = ""
|
||||||
obs.oldest_messages = []
|
obs.oldest_messages = []
|
||||||
obs.oldest_messages_str = ""
|
obs.oldest_messages_str = ""
|
||||||
|
|
||||||
|
return log_msg
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ from .base_processor import BaseProcessor
|
||||||
from src.chat.focus_chat.info.mind_info import MindInfo
|
from src.chat.focus_chat.info.mind_info import MindInfo
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
||||||
|
from src.chat.heart_flow.observation.actions_observation import ActionObservation
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from src.chat.focus_chat.info.info_base import InfoBase
|
from src.chat.focus_chat.info.info_base import InfoBase
|
||||||
|
|
||||||
|
|
@ -30,6 +31,8 @@ def init_prompt():
|
||||||
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
||||||
{chat_observe_info}
|
{chat_observe_info}
|
||||||
|
|
||||||
|
{action_observe_info}
|
||||||
|
|
||||||
以下是你之前对聊天的观察和规划,你的名字是{bot_name}:
|
以下是你之前对聊天的观察和规划,你的名字是{bot_name}:
|
||||||
{last_mind}
|
{last_mind}
|
||||||
|
|
||||||
|
|
@ -50,6 +53,8 @@ def init_prompt():
|
||||||
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容:
|
||||||
{chat_observe_info}
|
{chat_observe_info}
|
||||||
|
|
||||||
|
{action_observe_info}
|
||||||
|
|
||||||
以下是你之前对聊天的观察和规划,你的名字是{bot_name}:
|
以下是你之前对聊天的观察和规划,你的名字是{bot_name}:
|
||||||
{last_mind}
|
{last_mind}
|
||||||
|
|
||||||
|
|
@ -74,7 +79,7 @@ class MindProcessor(BaseProcessor):
|
||||||
model=global_config.model.focus_chat_mind,
|
model=global_config.model.focus_chat_mind,
|
||||||
temperature=global_config.model.focus_chat_mind["temp"],
|
temperature=global_config.model.focus_chat_mind["temp"],
|
||||||
max_tokens=800,
|
max_tokens=800,
|
||||||
request_type="focus_chat_mind",
|
request_type="focus.processor.chat_mind",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.current_mind = ""
|
self.current_mind = ""
|
||||||
|
|
@ -191,6 +196,8 @@ class MindProcessor(BaseProcessor):
|
||||||
person_list = observation.person_list
|
person_list = observation.person_list
|
||||||
if isinstance(observation, HFCloopObservation):
|
if isinstance(observation, HFCloopObservation):
|
||||||
hfcloop_observe_info = observation.get_observe_info()
|
hfcloop_observe_info = observation.get_observe_info()
|
||||||
|
if isinstance(observation, ActionObservation):
|
||||||
|
action_observe_info = observation.get_observe_info()
|
||||||
|
|
||||||
# ---------- 3. 准备个性化数据 ----------
|
# ---------- 3. 准备个性化数据 ----------
|
||||||
# 获取个性化信息
|
# 获取个性化信息
|
||||||
|
|
@ -211,6 +218,7 @@ class MindProcessor(BaseProcessor):
|
||||||
chat_observe_info=chat_observe_info,
|
chat_observe_info=chat_observe_info,
|
||||||
last_mind=previous_mind,
|
last_mind=previous_mind,
|
||||||
cycle_info_block=hfcloop_observe_info,
|
cycle_info_block=hfcloop_observe_info,
|
||||||
|
action_observe_info=action_observe_info,
|
||||||
chat_target_name=chat_target_name,
|
chat_target_name=chat_target_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class SelfProcessor(BaseProcessor):
|
||||||
model=global_config.model.focus_self_recognize,
|
model=global_config.model.focus_self_recognize,
|
||||||
temperature=global_config.model.focus_self_recognize["temp"],
|
temperature=global_config.model.focus_self_recognize["temp"],
|
||||||
max_tokens=800,
|
max_tokens=800,
|
||||||
request_type="focus_self_identify",
|
request_type="focus.processor.self_identify",
|
||||||
)
|
)
|
||||||
|
|
||||||
name = chat_manager.get_stream_name(self.subheartflow_id)
|
name = chat_manager.get_stream_name(self.subheartflow_id)
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ class ToolProcessor(BaseProcessor):
|
||||||
self.llm_model = LLMRequest(
|
self.llm_model = LLMRequest(
|
||||||
model=global_config.model.focus_tool_use,
|
model=global_config.model.focus_tool_use,
|
||||||
max_tokens=500,
|
max_tokens=500,
|
||||||
request_type="focus_tool",
|
request_type="focus.processor.tool",
|
||||||
)
|
)
|
||||||
self.structured_info = []
|
self.structured_info = []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class WorkingMemoryProcessor(BaseProcessor):
|
||||||
model=global_config.model.focus_chat_mind,
|
model=global_config.model.focus_chat_mind,
|
||||||
temperature=global_config.model.focus_chat_mind["temp"],
|
temperature=global_config.model.focus_chat_mind["temp"],
|
||||||
max_tokens=800,
|
max_tokens=800,
|
||||||
request_type="focus_working_memory",
|
request_type="focus.processor.working_memory",
|
||||||
)
|
)
|
||||||
|
|
||||||
name = chat_manager.get_stream_name(self.subheartflow_id)
|
name = chat_manager.get_stream_name(self.subheartflow_id)
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,10 @@ class MemoryActivator:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# TODO: API-Adapter修改标记
|
# TODO: API-Adapter修改标记
|
||||||
self.summary_model = LLMRequest(
|
self.summary_model = LLMRequest(
|
||||||
model=global_config.model.memory_summary, temperature=0.7, max_tokens=50, request_type="chat_observation"
|
model=global_config.model.memory_summary,
|
||||||
|
temperature=0.7,
|
||||||
|
max_tokens=50,
|
||||||
|
request_type="focus.memory_activator",
|
||||||
)
|
)
|
||||||
self.running_memory = []
|
self.running_memory = []
|
||||||
self.cached_keywords = set() # 用于缓存历史关键词
|
self.cached_keywords = set() # 用于缓存历史关键词
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
from typing import List, Optional, Any
|
from typing import List, Optional, Any
|
||||||
from src.chat.heart_flow.observation.observation import Observation
|
from src.chat.heart_flow.observation.observation import Observation
|
||||||
from src.chat.focus_chat.info.info_base import InfoBase
|
|
||||||
from src.chat.focus_chat.info.action_info import ActionInfo
|
|
||||||
from .base_processor import BaseProcessor
|
|
||||||
from src.common.logger_manager import get_logger
|
from src.common.logger_manager import get_logger
|
||||||
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
||||||
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
|
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
|
||||||
|
|
@ -10,11 +7,12 @@ from src.chat.message_receive.chat_stream import chat_manager
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
import random
|
import random
|
||||||
|
from src.chat.focus_chat.planners.action_manager import ActionManager
|
||||||
|
|
||||||
logger = get_logger("processor")
|
logger = get_logger("action_manager")
|
||||||
|
|
||||||
|
|
||||||
class ActionProcessor(BaseProcessor):
|
class ActionModifier:
|
||||||
"""动作处理器
|
"""动作处理器
|
||||||
|
|
||||||
用于处理Observation对象,将其转换为ObsInfo对象。
|
用于处理Observation对象,将其转换为ObsInfo对象。
|
||||||
|
|
@ -22,33 +20,24 @@ class ActionProcessor(BaseProcessor):
|
||||||
|
|
||||||
log_prefix = "动作处理"
|
log_prefix = "动作处理"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, action_manager: ActionManager):
|
||||||
"""初始化观察处理器"""
|
"""初始化观察处理器"""
|
||||||
super().__init__()
|
self.action_manager = action_manager
|
||||||
|
self.all_actions = self.action_manager.get_registered_actions()
|
||||||
|
|
||||||
async def process_info(
|
async def modify_actions(
|
||||||
self,
|
self,
|
||||||
observations: Optional[List[Observation]] = None,
|
observations: Optional[List[Observation]] = None,
|
||||||
running_memorys: Optional[List[Dict]] = None,
|
running_memorys: Optional[List[Dict]] = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> List[InfoBase]:
|
):
|
||||||
"""处理Observation对象
|
|
||||||
|
|
||||||
Args:
|
|
||||||
infos: InfoBase对象列表
|
|
||||||
observations: 可选的Observation对象列表
|
|
||||||
**kwargs: 其他可选参数
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[InfoBase]: 处理后的ObsInfo实例列表
|
|
||||||
"""
|
|
||||||
# print(f"observations: {observations}")
|
# print(f"observations: {observations}")
|
||||||
processed_infos = []
|
# processed_infos = []
|
||||||
|
|
||||||
# 处理Observation对象
|
# 处理Observation对象
|
||||||
if observations:
|
if observations:
|
||||||
action_info = ActionInfo()
|
# action_info = ActionInfo()
|
||||||
all_actions = None
|
# all_actions = None
|
||||||
hfc_obs = None
|
hfc_obs = None
|
||||||
chat_obs = None
|
chat_obs = None
|
||||||
|
|
||||||
|
|
@ -66,7 +55,7 @@ class ActionProcessor(BaseProcessor):
|
||||||
# 处理HFCloopObservation
|
# 处理HFCloopObservation
|
||||||
if hfc_obs:
|
if hfc_obs:
|
||||||
obs = hfc_obs
|
obs = hfc_obs
|
||||||
all_actions = obs.all_actions
|
all_actions = self.all_actions
|
||||||
action_changes = await self.analyze_loop_actions(obs)
|
action_changes = await self.analyze_loop_actions(obs)
|
||||||
if action_changes["add"] or action_changes["remove"]:
|
if action_changes["add"] or action_changes["remove"]:
|
||||||
# 合并动作变更
|
# 合并动作变更
|
||||||
|
|
@ -74,13 +63,13 @@ class ActionProcessor(BaseProcessor):
|
||||||
merged_action_changes["remove"].extend(action_changes["remove"])
|
merged_action_changes["remove"].extend(action_changes["remove"])
|
||||||
|
|
||||||
# 收集变更原因
|
# 收集变更原因
|
||||||
if action_changes["add"]:
|
# if action_changes["add"]:
|
||||||
reasons.append(f"添加动作{action_changes['add']}因为检测到大量无回复")
|
# reasons.append(f"添加动作{action_changes['add']}因为检测到大量无回复")
|
||||||
if action_changes["remove"]:
|
# if action_changes["remove"]:
|
||||||
reasons.append(f"移除动作{action_changes['remove']}因为检测到连续回复")
|
# reasons.append(f"移除动作{action_changes['remove']}因为检测到连续回复")
|
||||||
|
|
||||||
# 处理ChattingObservation
|
# 处理ChattingObservation
|
||||||
if chat_obs and all_actions is not None:
|
if chat_obs:
|
||||||
obs = chat_obs
|
obs = chat_obs
|
||||||
# 检查动作的关联类型
|
# 检查动作的关联类型
|
||||||
chat_context = chat_manager.get_stream(obs.chat_id).context
|
chat_context = chat_manager.get_stream(obs.chat_id).context
|
||||||
|
|
@ -98,14 +87,23 @@ class ActionProcessor(BaseProcessor):
|
||||||
merged_action_changes["remove"].extend(type_mismatched_actions)
|
merged_action_changes["remove"].extend(type_mismatched_actions)
|
||||||
reasons.append(f"移除动作{type_mismatched_actions}因为关联类型不匹配")
|
reasons.append(f"移除动作{type_mismatched_actions}因为关联类型不匹配")
|
||||||
|
|
||||||
|
for action_name in merged_action_changes["add"]:
|
||||||
|
if action_name in self.action_manager.get_registered_actions():
|
||||||
|
self.action_manager.add_action_to_using(action_name)
|
||||||
|
logger.debug(f"{self.log_prefix} 添加动作: {action_name}, 原因: {reasons}")
|
||||||
|
|
||||||
|
for action_name in merged_action_changes["remove"]:
|
||||||
|
self.action_manager.remove_action_from_using(action_name)
|
||||||
|
logger.debug(f"{self.log_prefix} 移除动作: {action_name}, 原因: {reasons}")
|
||||||
|
|
||||||
# 如果有任何动作变更,设置到action_info中
|
# 如果有任何动作变更,设置到action_info中
|
||||||
if merged_action_changes["add"] or merged_action_changes["remove"]:
|
# if merged_action_changes["add"] or merged_action_changes["remove"]:
|
||||||
action_info.set_action_changes(merged_action_changes)
|
# action_info.set_action_changes(merged_action_changes)
|
||||||
action_info.set_reason(" | ".join(reasons))
|
# action_info.set_reason(" | ".join(reasons))
|
||||||
|
|
||||||
processed_infos.append(action_info)
|
# processed_infos.append(action_info)
|
||||||
|
|
||||||
return processed_infos
|
# return processed_infos
|
||||||
|
|
||||||
async def analyze_loop_actions(self, obs: HFCloopObservation) -> Dict[str, List[str]]:
|
async def analyze_loop_actions(self, obs: HFCloopObservation) -> Dict[str, List[str]]:
|
||||||
"""分析最近的循环内容并决定动作的增减
|
"""分析最近的循环内容并决定动作的增减
|
||||||
|
|
@ -79,7 +79,7 @@ class ActionPlanner:
|
||||||
self.planner_llm = LLMRequest(
|
self.planner_llm = LLMRequest(
|
||||||
model=global_config.model.focus_planner,
|
model=global_config.model.focus_planner,
|
||||||
max_tokens=1000,
|
max_tokens=1000,
|
||||||
request_type="focus_planner", # 用于动作规划
|
request_type="focus.planner", # 用于动作规划
|
||||||
)
|
)
|
||||||
|
|
||||||
self.action_manager = action_manager
|
self.action_manager = action_manager
|
||||||
|
|
@ -101,35 +101,24 @@ class ActionPlanner:
|
||||||
# 获取观察信息
|
# 获取观察信息
|
||||||
extra_info: list[str] = []
|
extra_info: list[str] = []
|
||||||
|
|
||||||
# 首先处理动作变更
|
# 设置默认值
|
||||||
for info in all_plan_info:
|
nickname_str = ""
|
||||||
if isinstance(info, ActionInfo) and info.has_changes():
|
for nicknames in global_config.bot.alias_names:
|
||||||
add_actions = info.get_add_actions()
|
nickname_str += f"{nicknames},"
|
||||||
remove_actions = info.get_remove_actions()
|
name_block = f"你的名字是{global_config.bot.nickname},你的昵称有{nickname_str},有人也会用这些昵称称呼你。"
|
||||||
reason = info.get_reason()
|
|
||||||
print(f"{self.log_prefix} 动作变更: {add_actions} {remove_actions} {reason}")
|
|
||||||
|
|
||||||
# 处理动作的增加
|
personality_block = individuality.get_personality_prompt(x_person=2, level=2)
|
||||||
for action_name in add_actions:
|
identity_block = individuality.get_identity_prompt(x_person=2, level=2)
|
||||||
if action_name in self.action_manager.get_registered_actions():
|
|
||||||
self.action_manager.add_action_to_using(action_name)
|
|
||||||
logger.debug(f"{self.log_prefix}添加动作: {action_name}, 原因: {reason}")
|
|
||||||
|
|
||||||
# 处理动作的移除
|
self_info = name_block + personality_block + identity_block
|
||||||
for action_name in remove_actions:
|
current_mind = "你思考了很久,没有想清晰要做什么"
|
||||||
self.action_manager.remove_action_from_using(action_name)
|
|
||||||
logger.debug(f"{self.log_prefix}移除动作: {action_name}, 原因: {reason}")
|
|
||||||
|
|
||||||
# 如果当前选择的动作被移除了,更新为no_reply
|
|
||||||
if action in remove_actions:
|
|
||||||
action = "no_reply"
|
|
||||||
reasoning = f"之前选择的动作{action}已被移除,原因: {reason}"
|
|
||||||
|
|
||||||
# 继续处理其他信息
|
|
||||||
self_info = ""
|
|
||||||
current_mind = ""
|
|
||||||
cycle_info = ""
|
cycle_info = ""
|
||||||
structured_info = ""
|
structured_info = ""
|
||||||
|
extra_info = []
|
||||||
|
observed_messages = []
|
||||||
|
observed_messages_str = ""
|
||||||
|
chat_type = "group"
|
||||||
is_group_chat = True
|
is_group_chat = True
|
||||||
for info in all_plan_info:
|
for info in all_plan_info:
|
||||||
if isinstance(info, ObsInfo):
|
if isinstance(info, ObsInfo):
|
||||||
|
|
@ -146,8 +135,8 @@ class ActionPlanner:
|
||||||
elif isinstance(info, StructuredInfo):
|
elif isinstance(info, StructuredInfo):
|
||||||
structured_info = info.get_processed_info()
|
structured_info = info.get_processed_info()
|
||||||
# print(f"structured_info: {structured_info}")
|
# print(f"structured_info: {structured_info}")
|
||||||
elif not isinstance(info, ActionInfo): # 跳过已处理的ActionInfo
|
# elif not isinstance(info, ActionInfo): # 跳过已处理的ActionInfo
|
||||||
extra_info.append(info.get_processed_info())
|
# extra_info.append(info.get_processed_info())
|
||||||
|
|
||||||
# 获取当前可用的动作
|
# 获取当前可用的动作
|
||||||
current_available_actions = self.action_manager.get_using_actions()
|
current_available_actions = self.action_manager.get_using_actions()
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class MemoryManager:
|
||||||
model=global_config.model.focus_working_memory,
|
model=global_config.model.focus_working_memory,
|
||||||
temperature=0.3,
|
temperature=0.3,
|
||||||
max_tokens=512,
|
max_tokens=512,
|
||||||
request_type="memory_summarization",
|
request_type="focus.processor.working_memory",
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
# 定义了来自外部世界的信息
|
||||||
|
# 外部世界可以是某个聊天 不同平台的聊天 也可以是任意媒体
|
||||||
|
from datetime import datetime
|
||||||
|
from src.common.logger_manager import get_logger
|
||||||
|
from src.chat.focus_chat.planners.action_manager import ActionManager
|
||||||
|
|
||||||
|
logger = get_logger("observation")
|
||||||
|
|
||||||
|
|
||||||
|
# 特殊的观察,专门用于观察动作
|
||||||
|
# 所有观察的基类
|
||||||
|
class ActionObservation:
|
||||||
|
def __init__(self, observe_id):
|
||||||
|
self.observe_info = ""
|
||||||
|
self.observe_id = observe_id
|
||||||
|
self.last_observe_time = datetime.now().timestamp() # 初始化为当前时间
|
||||||
|
self.action_manager: ActionManager = None
|
||||||
|
|
||||||
|
self.all_actions = {}
|
||||||
|
self.all_using_actions = {}
|
||||||
|
|
||||||
|
def get_observe_info(self):
|
||||||
|
return self.observe_info
|
||||||
|
|
||||||
|
def set_action_manager(self, action_manager: ActionManager):
|
||||||
|
self.action_manager = action_manager
|
||||||
|
self.all_actions = self.action_manager.get_registered_actions()
|
||||||
|
|
||||||
|
async def observe(self):
|
||||||
|
action_info_block = ""
|
||||||
|
self.all_using_actions = self.action_manager.get_using_actions()
|
||||||
|
for action_name, action_info in self.all_using_actions.items():
|
||||||
|
action_info_block += f"\n{action_name}: {action_info.get('description', '')}"
|
||||||
|
action_info_block += "\n注意,除了上面动作选项之外,你在群聊里不能做其他任何事情,这是你能力的边界\n"
|
||||||
|
|
||||||
|
self.observe_info = action_info_block
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from src.common.logger_manager import get_logger
|
from src.common.logger_manager import get_logger
|
||||||
from src.chat.focus_chat.heartFC_Cycleinfo import CycleDetail
|
from src.chat.focus_chat.heartFC_Cycleinfo import CycleDetail
|
||||||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
|
||||||
from typing import List
|
from typing import List
|
||||||
# Import the new utility function
|
# Import the new utility function
|
||||||
|
|
||||||
|
|
@ -17,9 +16,6 @@ class HFCloopObservation:
|
||||||
self.observe_id = observe_id
|
self.observe_id = observe_id
|
||||||
self.last_observe_time = datetime.now().timestamp() # 初始化为当前时间
|
self.last_observe_time = datetime.now().timestamp() # 初始化为当前时间
|
||||||
self.history_loop: List[CycleDetail] = []
|
self.history_loop: List[CycleDetail] = []
|
||||||
self.action_manager: ActionManager = None
|
|
||||||
|
|
||||||
self.all_actions = {}
|
|
||||||
|
|
||||||
def get_observe_info(self):
|
def get_observe_info(self):
|
||||||
return self.observe_info
|
return self.observe_info
|
||||||
|
|
@ -27,10 +23,6 @@ class HFCloopObservation:
|
||||||
def add_loop_info(self, loop_info: CycleDetail):
|
def add_loop_info(self, loop_info: CycleDetail):
|
||||||
self.history_loop.append(loop_info)
|
self.history_loop.append(loop_info)
|
||||||
|
|
||||||
def set_action_manager(self, action_manager: ActionManager):
|
|
||||||
self.action_manager = action_manager
|
|
||||||
self.all_actions = self.action_manager.get_registered_actions()
|
|
||||||
|
|
||||||
async def observe(self):
|
async def observe(self):
|
||||||
recent_active_cycles: List[CycleDetail] = []
|
recent_active_cycles: List[CycleDetail] = []
|
||||||
for cycle in reversed(self.history_loop):
|
for cycle in reversed(self.history_loop):
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@ class NormalChatGenerator:
|
||||||
model=global_config.model.normal_chat_1,
|
model=global_config.model.normal_chat_1,
|
||||||
# temperature=0.7,
|
# temperature=0.7,
|
||||||
max_tokens=3000,
|
max_tokens=3000,
|
||||||
request_type="normal_chat_1",
|
request_type="normal.chat_1",
|
||||||
)
|
)
|
||||||
self.model_normal = LLMRequest(
|
self.model_normal = LLMRequest(
|
||||||
model=global_config.model.normal_chat_2,
|
model=global_config.model.normal_chat_2,
|
||||||
# temperature=global_config.model.normal_chat_2["temp"],
|
# temperature=global_config.model.normal_chat_2["temp"],
|
||||||
max_tokens=256,
|
max_tokens=256,
|
||||||
request_type="normal_chat_2",
|
request_type="normal.chat_2",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.model_sum = LLMRequest(
|
self.model_sum = LLMRequest(
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ def init_prompt():
|
||||||
{relation_prompt}
|
{relation_prompt}
|
||||||
{prompt_info}
|
{prompt_info}
|
||||||
{chat_target}
|
{chat_target}
|
||||||
|
现在时间是:{now_time}
|
||||||
{chat_talking_prompt}
|
{chat_talking_prompt}
|
||||||
现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言或者回复这条消息。\n
|
现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言或者回复这条消息。\n
|
||||||
你的网名叫{bot_name},有人也叫你{bot_other_names},{prompt_personality}。
|
你的网名叫{bot_name},有人也叫你{bot_other_names},{prompt_personality}。
|
||||||
|
|
@ -221,6 +222,8 @@ class PromptBuilder:
|
||||||
|
|
||||||
logger.debug("开始构建 normal prompt")
|
logger.debug("开始构建 normal prompt")
|
||||||
|
|
||||||
|
now_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||||
|
|
||||||
# --- Choose template and format based on chat type ---
|
# --- Choose template and format based on chat type ---
|
||||||
if is_group_chat:
|
if is_group_chat:
|
||||||
template_name = "reasoning_prompt_main"
|
template_name = "reasoning_prompt_main"
|
||||||
|
|
@ -249,6 +252,7 @@ class PromptBuilder:
|
||||||
prompt_ger=prompt_ger,
|
prompt_ger=prompt_ger,
|
||||||
# moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"),
|
# moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"),
|
||||||
moderation_prompt=moderation_prompt_block,
|
moderation_prompt=moderation_prompt_block,
|
||||||
|
now_time=now_time,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
template_name = "reasoning_prompt_private_main"
|
template_name = "reasoning_prompt_private_main"
|
||||||
|
|
@ -273,6 +277,7 @@ class PromptBuilder:
|
||||||
prompt_ger=prompt_ger,
|
prompt_ger=prompt_ger,
|
||||||
# moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"),
|
# moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"),
|
||||||
moderation_prompt=moderation_prompt_block,
|
moderation_prompt=moderation_prompt_block,
|
||||||
|
now_time=now_time,
|
||||||
)
|
)
|
||||||
# --- End choosing template ---
|
# --- End choosing template ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -457,7 +457,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str:
|
||||||
return "SELF"
|
return "SELF"
|
||||||
try:
|
try:
|
||||||
person_id = person_info_manager.get_person_id(platform, user_id)
|
person_id = person_info_manager.get_person_id(platform, user_id)
|
||||||
except Exception as e:
|
except Exception as _e:
|
||||||
person_id = None
|
person_id = None
|
||||||
if not person_id:
|
if not person_id:
|
||||||
return "?"
|
return "?"
|
||||||
|
|
@ -472,7 +472,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str:
|
||||||
# user_info = msg.get("user_info", {})
|
# user_info = msg.get("user_info", {})
|
||||||
platform = msg.get("chat_info_platform")
|
platform = msg.get("chat_info_platform")
|
||||||
user_id = msg.get("user_id")
|
user_id = msg.get("user_id")
|
||||||
timestamp = msg.get("time")
|
_timestamp = msg.get("time")
|
||||||
# print(f"msg:{msg}")
|
# print(f"msg:{msg}")
|
||||||
# print(f"platform:{platform}")
|
# print(f"platform:{platform}")
|
||||||
# print(f"user_id:{user_id}")
|
# print(f"user_id:{user_id}")
|
||||||
|
|
@ -488,7 +488,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str:
|
||||||
content = content.replace("ⁿ", "")
|
content = content.replace("ⁿ", "")
|
||||||
|
|
||||||
# if not all([platform, user_id, timestamp is not None]):
|
# if not all([platform, user_id, timestamp is not None]):
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
anon_name = get_anon_name(platform, user_id)
|
anon_name = get_anon_name(platform, user_id)
|
||||||
# print(f"anon_name:{anon_name}")
|
# print(f"anon_name:{anon_name}")
|
||||||
|
|
@ -514,7 +514,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str:
|
||||||
new_content = ""
|
new_content = ""
|
||||||
last_end = 0
|
last_end = 0
|
||||||
for m in at_matches:
|
for m in at_matches:
|
||||||
new_content += content[last_end:m.start()]
|
new_content += content[last_end : m.start()]
|
||||||
bbb = m.group(2)
|
bbb = m.group(2)
|
||||||
try:
|
try:
|
||||||
anon_at = get_anon_name(platform, bbb)
|
anon_at = get_anon_name(platform, bbb)
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,22 @@ def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, float]:
|
||||||
f"消息中包含不合理的设置 is_mentioned: {message.message_info.additional_config.get('is_mentioned')}"
|
f"消息中包含不合理的设置 is_mentioned: {message.message_info.additional_config.get('is_mentioned')}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if global_config.bot.nickname in message.processed_plain_text:
|
||||||
|
is_mentioned = True
|
||||||
|
|
||||||
|
for alias_name in global_config.bot.alias_names:
|
||||||
|
if alias_name in message.processed_plain_text:
|
||||||
|
is_mentioned = True
|
||||||
|
|
||||||
# 判断是否被@
|
# 判断是否被@
|
||||||
if re.search(rf"@<(.+?):{global_config.bot.qq_account}>", message.processed_plain_text):
|
if re.search(rf"@<(.+?):{global_config.bot.qq_account}>", message.processed_plain_text):
|
||||||
is_at = True
|
is_at = True
|
||||||
is_mentioned = True
|
is_mentioned = True
|
||||||
|
|
||||||
|
# print(f"message.processed_plain_text: {message.processed_plain_text}")
|
||||||
|
# print(f"is_mentioned: {is_mentioned}")
|
||||||
|
# print(f"is_at: {is_at}")
|
||||||
|
|
||||||
if is_at and global_config.normal_chat.at_bot_inevitable_reply:
|
if is_at and global_config.normal_chat.at_bot_inevitable_reply:
|
||||||
reply_probability = 1.0
|
reply_probability = 1.0
|
||||||
logger.info("被@,回复概率设置为100%")
|
logger.info("被@,回复概率设置为100%")
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ from src.common.logger_manager import get_logger
|
||||||
from src.chat.message_receive.message import MessageRecv
|
from src.chat.message_receive.message import MessageRecv
|
||||||
from src.chat.message_receive.storage import MessageStorage
|
from src.chat.message_receive.storage import MessageStorage
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
|
from src.chat.message_receive.chat_stream import ChatStream
|
||||||
|
|
||||||
|
from maim_message import UserInfo
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import re
|
||||||
|
|
||||||
logger = get_logger("pfc")
|
logger = get_logger("pfc")
|
||||||
|
|
||||||
|
|
@ -14,7 +18,7 @@ class MessageProcessor:
|
||||||
self.storage = MessageStorage()
|
self.storage = MessageStorage()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_ban_words(text: str, chat, userinfo) -> bool:
|
def _check_ban_words(text: str, chat: ChatStream, userinfo: UserInfo) -> bool:
|
||||||
"""检查消息中是否包含过滤词"""
|
"""检查消息中是否包含过滤词"""
|
||||||
for word in global_config.message_receive.ban_words:
|
for word in global_config.message_receive.ban_words:
|
||||||
if word in text:
|
if word in text:
|
||||||
|
|
@ -26,13 +30,12 @@ class MessageProcessor:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_ban_regex(text: str, chat, userinfo) -> bool:
|
def _check_ban_regex(text: str, chat: ChatStream, userinfo: UserInfo) -> bool:
|
||||||
"""检查消息是否匹配过滤正则表达式"""
|
"""检查消息是否匹配过滤正则表达式"""
|
||||||
for pattern in global_config.chat.ban_msgs_regex:
|
for pattern in global_config.message_receive.ban_msgs_regex:
|
||||||
if pattern.search(text):
|
if re.search(pattern, text):
|
||||||
logger.info(
|
chat_name = chat.group_info.group_name if chat.group_info else "私聊"
|
||||||
f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}"
|
logger.info(f"[{chat_name}]{userinfo.user_nickname}:{text}")
|
||||||
)
|
|
||||||
logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered")
|
logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered")
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class PersonalityExpression:
|
||||||
model=global_config.model.focus_expressor,
|
model=global_config.model.focus_expressor,
|
||||||
temperature=0.1,
|
temperature=0.1,
|
||||||
max_tokens=256,
|
max_tokens=256,
|
||||||
request_type="learn_expression",
|
request_type="expressor.learner",
|
||||||
)
|
)
|
||||||
self.meta_file_path = os.path.join("data", "expression", "personality", "expression_style_meta.json")
|
self.meta_file_path = os.path.join("data", "expression", "personality", "expression_style_meta.json")
|
||||||
self.expressions_file_path = os.path.join("data", "expression", "personality", "expressions.json")
|
self.expressions_file_path = os.path.join("data", "expression", "personality", "expressions.json")
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class PersonInfoManager:
|
||||||
self.qv_name_llm = LLMRequest(
|
self.qv_name_llm = LLMRequest(
|
||||||
model=global_config.model.utils,
|
model=global_config.model.utils,
|
||||||
max_tokens=256,
|
max_tokens=256,
|
||||||
request_type="qv_name",
|
request_type="relation.qv_name",
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
db.connect(reuse_if_open=True)
|
db.connect(reuse_if_open=True)
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,15 @@ class PicAction(PluginAction):
|
||||||
"""根据描述使用火山引擎HTTP API生成图片的动作处理类"""
|
"""根据描述使用火山引擎HTTP API生成图片的动作处理类"""
|
||||||
|
|
||||||
action_name = "pic_action"
|
action_name = "pic_action"
|
||||||
action_description = "可以根据特定的描述,生成并发送一张图片,如果没提供描述,就根据聊天内容生成"
|
action_description = (
|
||||||
|
"可以根据特定的描述,生成并发送一张图片,如果没提供描述,就根据聊天内容生成,你可以立刻画好,不用等待"
|
||||||
|
)
|
||||||
action_parameters = {
|
action_parameters = {
|
||||||
"description": "图片描述,输入你想要生成并发送的图片的描述,必填",
|
"description": "图片描述,输入你想要生成并发送的图片的描述,必填",
|
||||||
"size": "图片尺寸,例如 '1024x1024' (可选, 默认从配置或 '1024x1024')",
|
"size": "图片尺寸,例如 '1024x1024' (可选, 默认从配置或 '1024x1024')",
|
||||||
}
|
}
|
||||||
action_require = [
|
action_require = [
|
||||||
|
"当有人让你画东西时使用,你可以立刻画好,不用等待",
|
||||||
"当有人要求你生成并发送一张图片时使用",
|
"当有人要求你生成并发送一张图片时使用",
|
||||||
"当有人让你画一张图时使用",
|
"当有人让你画一张图时使用",
|
||||||
]
|
]
|
||||||
|
|
@ -45,6 +48,8 @@ class PicAction(PluginAction):
|
||||||
):
|
):
|
||||||
super().__init__(action_data, reasoning, cycle_timers, thinking_id, global_config, **kwargs)
|
super().__init__(action_data, reasoning, cycle_timers, thinking_id, global_config, **kwargs)
|
||||||
|
|
||||||
|
logger.info(f"{self.log_prefix} 开始绘图!原因是:{self.reasoning}")
|
||||||
|
|
||||||
http_base_url = self.config.get("base_url")
|
http_base_url = self.config.get("base_url")
|
||||||
http_api_key = self.config.get("volcano_generate_api_key")
|
http_api_key = self.config.get("volcano_generate_api_key")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from src.tools.tool_can_use.base_tool import BaseTool
|
from src.tools.tool_can_use.base_tool import BaseTool
|
||||||
from src.chat.utils.utils import get_embedding
|
|
||||||
|
|
||||||
# from src.common.database import db
|
# from src.common.database import db
|
||||||
from src.common.logger_manager import get_logger
|
from src.common.logger_manager import get_logger
|
||||||
|
|
@ -38,125 +37,19 @@ class SearchKnowledgeFromLPMMTool(BaseTool):
|
||||||
# threshold = function_args.get("threshold", 0.4)
|
# threshold = function_args.get("threshold", 0.4)
|
||||||
|
|
||||||
# 调用知识库搜索
|
# 调用知识库搜索
|
||||||
embedding = await get_embedding(query, request_type="info_retrieval")
|
|
||||||
if embedding:
|
knowledge_info = qa_manager.get_knowledge(query)
|
||||||
knowledge_info = qa_manager.get_knowledge(query)
|
|
||||||
logger.debug(f"知识库查询结果: {knowledge_info}")
|
logger.debug(f"知识库查询结果: {knowledge_info}")
|
||||||
if knowledge_info:
|
|
||||||
content = f"你知道这些知识: {knowledge_info}"
|
if knowledge_info:
|
||||||
else:
|
content = f"你知道这些知识: {knowledge_info}"
|
||||||
content = f"你不太了解有关{query}的知识"
|
else:
|
||||||
return {"type": "lpmm_knowledge", "id": query, "content": content}
|
content = f"你不太了解有关{query}的知识"
|
||||||
# 如果获取嵌入失败
|
return {"type": "lpmm_knowledge", "id": query, "content": content}
|
||||||
return {"type": "info", "id": query, "content": f"无法获取关于'{query}'的嵌入向量,你lpmm知识库炸了"}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# 捕获异常并记录错误
|
||||||
logger.error(f"知识库搜索工具执行失败: {str(e)}")
|
logger.error(f"知识库搜索工具执行失败: {str(e)}")
|
||||||
# 在其他异常情况下,确保 id 仍然是 query (如果它被定义了)
|
# 在其他异常情况下,确保 id 仍然是 query (如果它被定义了)
|
||||||
query_id = query if "query" in locals() else "unknown_query"
|
query_id = query if "query" in locals() else "unknown_query"
|
||||||
return {"type": "info", "id": query_id, "content": f"lpmm知识库搜索失败,炸了: {str(e)}"}
|
return {"type": "info", "id": query_id, "content": f"lpmm知识库搜索失败,炸了: {str(e)}"}
|
||||||
|
|
||||||
# def get_info_from_db(
|
|
||||||
# self, query_embedding: list, limit: int = 1, threshold: float = 0.5, return_raw: bool = False
|
|
||||||
# ) -> Union[str, list]:
|
|
||||||
# """从数据库中获取相关信息
|
|
||||||
|
|
||||||
# Args:
|
|
||||||
# query_embedding: 查询的嵌入向量
|
|
||||||
# limit: 最大返回结果数
|
|
||||||
# threshold: 相似度阈值
|
|
||||||
# return_raw: 是否返回原始结果
|
|
||||||
|
|
||||||
# Returns:
|
|
||||||
# Union[str, list]: 格式化的信息字符串或原始结果列表
|
|
||||||
# """
|
|
||||||
# if not query_embedding:
|
|
||||||
# return "" if not return_raw else []
|
|
||||||
|
|
||||||
# # 使用余弦相似度计算
|
|
||||||
# pipeline = [
|
|
||||||
# {
|
|
||||||
# "$addFields": {
|
|
||||||
# "dotProduct": {
|
|
||||||
# "$reduce": {
|
|
||||||
# "input": {"$range": [0, {"$size": "$embedding"}]},
|
|
||||||
# "initialValue": 0,
|
|
||||||
# "in": {
|
|
||||||
# "$add": [
|
|
||||||
# "$$value",
|
|
||||||
# {
|
|
||||||
# "$multiply": [
|
|
||||||
# {"$arrayElemAt": ["$embedding", "$$this"]},
|
|
||||||
# {"$arrayElemAt": [query_embedding, "$$this"]},
|
|
||||||
# ]
|
|
||||||
# },
|
|
||||||
# ]
|
|
||||||
# },
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "magnitude1": {
|
|
||||||
# "$sqrt": {
|
|
||||||
# "$reduce": {
|
|
||||||
# "input": "$embedding",
|
|
||||||
# "initialValue": 0,
|
|
||||||
# "in": {"$add": ["$$value", {"$multiply": ["$$this", "$$this"]}]},
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "magnitude2": {
|
|
||||||
# "$sqrt": {
|
|
||||||
# "$reduce": {
|
|
||||||
# "input": query_embedding,
|
|
||||||
# "initialValue": 0,
|
|
||||||
# "in": {"$add": ["$$value", {"$multiply": ["$$this", "$$this"]}]},
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# {"$addFields": {"similarity": {"$divide": ["$dotProduct", {"$multiply": ["$magnitude1", "$magnitude2"]}]}}},
|
|
||||||
# {
|
|
||||||
# "$match": {
|
|
||||||
# "similarity": {"$gte": threshold} # 只保留相似度大于等于阈值的结果
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# {"$sort": {"similarity": -1}},
|
|
||||||
# {"$limit": limit},
|
|
||||||
# {"$project": {"content": 1, "similarity": 1}},
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# results = list(db.knowledges.aggregate(pipeline))
|
|
||||||
# logger.debug(f"知识库查询结果数量: {len(results)}")
|
|
||||||
|
|
||||||
# if not results:
|
|
||||||
# return "" if not return_raw else []
|
|
||||||
|
|
||||||
# if return_raw:
|
|
||||||
# return results
|
|
||||||
# else:
|
|
||||||
# # 返回所有找到的内容,用换行分隔
|
|
||||||
# return "\n".join(str(result["content"]) for result in results)
|
|
||||||
|
|
||||||
def _format_results(self, results: list) -> str:
|
|
||||||
"""格式化结果"""
|
|
||||||
if not results:
|
|
||||||
return "未找到相关知识。"
|
|
||||||
|
|
||||||
formatted_string = "我找到了一些相关知识:\n"
|
|
||||||
for i, result in enumerate(results):
|
|
||||||
# chunk_id = result.get("chunk_id")
|
|
||||||
text = result.get("text", "")
|
|
||||||
source = result.get("source", "未知来源")
|
|
||||||
source_type = result.get("source_type", "未知类型")
|
|
||||||
similarity = result.get("similarity", 0.0)
|
|
||||||
|
|
||||||
formatted_string += (
|
|
||||||
f"{i + 1}. (相似度: {similarity:.2f}) 类型: {source_type}, 来源: {source} \n内容片段: {text}\n\n"
|
|
||||||
)
|
|
||||||
# 暂时去掉chunk_id
|
|
||||||
# formatted_string += f"{i + 1}. (相似度: {similarity:.2f}) 类型: {source_type}, 来源: {source}, Chunk ID: {chunk_id} \n内容片段: {text}\n\n"
|
|
||||||
|
|
||||||
return formatted_string
|
|
||||||
|
|
||||||
|
|
||||||
# 注册工具
|
|
||||||
# register_tool(SearchKnowledgeTool)
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@ version = "2.6.0"
|
||||||
#----以上是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
#----以上是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
||||||
|
|
||||||
[bot]
|
[bot]
|
||||||
qq_account = 1145141919810
|
qq_account = 1145141919810 # 麦麦的QQ账号
|
||||||
nickname = "麦麦"
|
nickname = "麦麦" # 麦麦的昵称
|
||||||
alias_names = ["麦叠", "牢麦"]
|
alias_names = ["麦叠", "牢麦"] # 麦麦的别名
|
||||||
|
|
||||||
[personality]
|
[personality]
|
||||||
personality_core = "是一个积极向上的女大学生" # 建议50字以内
|
personality_core = "是一个积极向上的女大学生" # 建议50字以内
|
||||||
|
|
@ -84,8 +84,8 @@ response_willing_amplifier = 1 # 麦麦回复意愿放大系数,一般为1
|
||||||
response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数
|
response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数
|
||||||
|
|
||||||
emoji_response_penalty = 0 # 表情包回复惩罚系数,设为0为不回复单个表情包,减少单独回复表情包的概率
|
emoji_response_penalty = 0 # 表情包回复惩罚系数,设为0为不回复单个表情包,减少单独回复表情包的概率
|
||||||
mentioned_bot_inevitable_reply = false # 提及 bot 必然回复
|
mentioned_bot_inevitable_reply = true # 提及 bot 必然回复
|
||||||
at_bot_inevitable_reply = false # @bot 必然回复
|
at_bot_inevitable_reply = true # @bot 必然回复
|
||||||
|
|
||||||
down_frequency_rate = 3 # 降低回复频率的群组回复意愿降低系数 除法
|
down_frequency_rate = 3 # 降低回复频率的群组回复意愿降低系数 除法
|
||||||
talk_frequency_down_groups = [] #降低回复频率的群号码
|
talk_frequency_down_groups = [] #降低回复频率的群号码
|
||||||
|
|
@ -93,14 +93,14 @@ talk_frequency_down_groups = [] #降低回复频率的群号码
|
||||||
[focus_chat] #专注聊天
|
[focus_chat] #专注聊天
|
||||||
think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗
|
think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗
|
||||||
|
|
||||||
observation_context_size = 15 # 观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖
|
observation_context_size = 16 # 观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖
|
||||||
compressed_length = 5 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度,会压缩,最短压缩长度为5
|
compressed_length = 8 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度,会压缩,最短压缩长度为5
|
||||||
compress_length_limit = 5 #最多压缩份数,超过该数值的压缩上下文会被删除
|
compress_length_limit = 4 #最多压缩份数,超过该数值的压缩上下文会被删除
|
||||||
|
|
||||||
[focus_chat_processor] # 专注聊天处理器,打开可以实现更多功能,但是会增加token消耗
|
[focus_chat_processor] # 专注聊天处理器,打开可以实现更多功能,但是会增加token消耗
|
||||||
self_identify_processor = true # 是否启用自我识别处理器
|
self_identify_processor = true # 是否启用自我识别处理器
|
||||||
tool_use_processor = false # 是否启用工具使用处理器
|
tool_use_processor = false # 是否启用工具使用处理器
|
||||||
working_memory_processor = false # 是否启用工作记忆处理器
|
working_memory_processor = false # 是否启用工作记忆处理器,不稳定,消耗量大
|
||||||
|
|
||||||
[emoji]
|
[emoji]
|
||||||
max_reg_num = 40 # 表情包最大注册数量
|
max_reg_num = 40 # 表情包最大注册数量
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,16 @@ HOST=127.0.0.1
|
||||||
PORT=8000
|
PORT=8000
|
||||||
|
|
||||||
#key and url
|
#key and url
|
||||||
CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1
|
|
||||||
SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/
|
SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/
|
||||||
DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1
|
DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1
|
||||||
|
CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1
|
||||||
|
xxxxxxx_BASE_URL=https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
# 定义你要用的api的key(需要去对应网站申请哦)
|
# 定义你要用的api的key(需要去对应网站申请哦)
|
||||||
DEEP_SEEK_KEY=
|
DEEP_SEEK_KEY=
|
||||||
CHAT_ANY_WHERE_KEY=
|
CHAT_ANY_WHERE_KEY=
|
||||||
SILICONFLOW_KEY=
|
SILICONFLOW_KEY=
|
||||||
|
xxxxxxx_KEY=
|
||||||
|
|
||||||
# 定义日志相关配置
|
# 定义日志相关配置
|
||||||
|
|
||||||
|
|
@ -22,8 +24,8 @@ CONSOLE_LOG_LEVEL=INFO
|
||||||
# 自定义日志的默认文件输出日志级别
|
# 自定义日志的默认文件输出日志级别
|
||||||
FILE_LOG_LEVEL=DEBUG
|
FILE_LOG_LEVEL=DEBUG
|
||||||
|
|
||||||
# 原生日志的控制台输出日志级别(nonebot就是这一类)
|
# 原生日志的控制台输出日志级别
|
||||||
DEFAULT_CONSOLE_LOG_LEVEL=SUCCESS
|
DEFAULT_CONSOLE_LOG_LEVEL=SUCCESS
|
||||||
|
|
||||||
# 原生日志的默认文件输出日志级别(nonebot就是这一类)
|
# 原生日志的默认文件输出日志级别
|
||||||
DEFAULT_FILE_LOG_LEVEL=DEBUG
|
DEFAULT_FILE_LOG_LEVEL=DEBUG
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
from src.config.config import global_config
|
|
||||||
|
|
||||||
|
|
||||||
class TestConfig:
|
|
||||||
def test_load(self):
|
|
||||||
config = global_config
|
|
||||||
print(config)
|
|
||||||
Loading…
Reference in New Issue