This commit is contained in:
meng_xi_pan
2025-03-15 20:42:12 +08:00
41 changed files with 1025 additions and 252 deletions

View File

@@ -12,6 +12,23 @@ body:
- label: "我确认在 Issues 列表中并无其他人已经提出过与此问题相同或相似的问题" - label: "我确认在 Issues 列表中并无其他人已经提出过与此问题相同或相似的问题"
required: true required: true
- label: "我使用了 Docker" - label: "我使用了 Docker"
- type: dropdown
attributes:
label: "使用的分支"
description: "请选择您正在使用的版本分支"
options:
- main
- main-fix
- refactor
validations:
required: true
- type: input
attributes:
label: "具体版本号"
description: "请输入您使用的具体版本号"
placeholder: "例如0.5.11、0.5.8"
validations:
required: true
- type: textarea - type: textarea
attributes: attributes:
label: 遇到的问题 label: 遇到的问题

8
.gitignore vendored
View File

@@ -16,6 +16,7 @@ memory_graph.gml
.env.* .env.*
config/bot_config_dev.toml config/bot_config_dev.toml
config/bot_config.toml config/bot_config.toml
config/bot_config.toml.bak
src/plugins/remote/client_uuid.json src/plugins/remote/client_uuid.json
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
@@ -25,7 +26,7 @@ llm_statistics.txt
mongodb mongodb
napcat napcat
run_dev.bat run_dev.bat
elua.confirmed
# C extensions # C extensions
*.so *.so
@@ -205,3 +206,8 @@ jieba.cache
.idea .idea
*.iml *.iml
*.ipr *.ipr
# PyEnv
# If using PyEnv and configured to use a specific Python version locally
# a .local-version file will be created in the root of the project to specify the version.
.python-version

69
EULA.md Normal file
View File

@@ -0,0 +1,69 @@
---
# **MaimBot用户协议**
**生效日期:** 2025.3.14
---
### **特别声明**
1. **MaimBot为遵循GPLv3协议的开源项目**
- 代码托管于GitHub**开发者不持有任何法律实体**,项目由社区共同维护;
- 用户可自由使用、修改、分发代码,但**必须遵守GPLv3许可证要求**(详见项目仓库)。
2. **无责任声明**
- 本项目**不提供任何形式的担保**,开发者及贡献者均不对使用后果负责;
- 所有功能依赖第三方API**生成内容不受我方控制**。
---
### **一、基础说明**
1. **MaimBot是什么**
- MaimBot是基于第三方AI技术如ChatGPT等的自动回复机器人**所有输出内容均由AI自动生成不代表我方观点**。
- 用户可提交自定义指令Prompt经我方内容过滤后调用第三方API生成结果**输出可能存在错误、偏见或不适宜内容**。
---
### **二、用户责任**
1. **禁止内容**
您承诺**不提交或生成以下内容**,否则我方有权永久封禁账号:
- 违法、暴力、色情、歧视性内容;
- 诈骗、谣言、恶意代码等危害他人或社会的内容;
- 侵犯他人隐私、肖像权、知识产权的内容。
2. **后果自负**
- 您需对**输入的指令Prompt和生成内容的使用负全责**
- **禁止将结果用于医疗、法律、投资等专业领域**,否则风险自行承担。
---
### **三、我们不负责什么**
1. **技术问题**
- 因第三方API故障、网络延迟、内容过滤误判导致的服务异常
- AI生成内容的不准确、冒犯性、时效性错误。
2. **用户行为**
- 因您违反本协议或滥用MaimBot导致的任何纠纷、损失
- 他人通过您的账号生成的违规内容。
---
### **四、其他重要条款**
1. **隐私与数据**
- 您提交的指令和生成内容可能被匿名化后用于优化服务,**敏感信息请勿输入**
- **我方会收集部分统计信息(如使用频率、基础指令类型)以改进服务,您可在[bot_config.toml]随时关闭此功能**。
2. **精神健康风险**
⚠️ **MaimBot仅为工具型机器人不具备情感交互能力。建议用户**
- 避免过度依赖AI回复处理现实问题或情绪困扰
- 如感到心理不适,请及时寻求专业心理咨询服务。
- 如遇心理困扰请寻求专业帮助全国心理援助热线12355
3. **封禁权利**
- 我方有权不经通知**删除违规内容、暂停或终止您的访问权限**。
4. **争议解决**
- 本协议适用中国法律,争议提交相关地区法院管辖;
- 若因GPLv3许可产生纠纷以许可证官方解释为准。
---

View File

@@ -21,8 +21,6 @@
> [!WARNING] > [!WARNING]
> 注意3月12日的v0.5.13, 该版本更新较大,建议单独开文件夹部署,然后转移/data文件 和数据库数据库可能需要删除messages下的内容不需要删除记忆 > 注意3月12日的v0.5.13, 该版本更新较大,建议单独开文件夹部署,然后转移/data文件 和数据库数据库可能需要删除messages下的内容不需要删除记忆
<div align="center"> <div align="center">
<a href="https://www.bilibili.com/video/BV1amAneGE3P" target="_blank"> <a href="https://www.bilibili.com/video/BV1amAneGE3P" target="_blank">
<img src="docs/video.png" width="300" alt="麦麦演示视频"> <img src="docs/video.png" width="300" alt="麦麦演示视频">
@@ -45,17 +43,14 @@
- [三群](https://qm.qq.com/q/wlH5eT8OmQ) 1035228475开发和建议相关讨论不一定有空回复会优先写文档和代码 - [三群](https://qm.qq.com/q/wlH5eT8OmQ) 1035228475开发和建议相关讨论不一定有空回复会优先写文档和代码
- [四群](https://qm.qq.com/q/wlH5eT8OmQ) 729957033开发和建议相关讨论不一定有空回复会优先写文档和代码 - [四群](https://qm.qq.com/q/wlH5eT8OmQ) 729957033开发和建议相关讨论不一定有空回复会优先写文档和代码
**📚 有热心网友创作的wiki:** https://maimbot.pages.dev/ **📚 有热心网友创作的wiki:** https://maimbot.pages.dev/
**📚 由SLAPQ制作的B站教程:** https://www.bilibili.com/opus/1041609335464001545
**😊 其他平台版本** **😊 其他平台版本**
- (由 [CabLate](https://github.com/cablate) 贡献) [Telegram 与其他平台(未来可能会有)的版本](https://github.com/cablate/MaiMBot/tree/telegram) - [集中讨论串](https://github.com/SengokuCola/MaiMBot/discussions/149) - (由 [CabLate](https://github.com/cablate) 贡献) [Telegram 与其他平台(未来可能会有)的版本](https://github.com/cablate/MaiMBot/tree/telegram) - [集中讨论串](https://github.com/SengokuCola/MaiMBot/discussions/149)
## 📝 注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意 ## 📝 注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意
**如果你有想法想要提交pr** **如果你有想法想要提交pr**
- 由于本项目在快速迭代和功能调整并且有重构计划目前不接受任何未经过核心开发组讨论的pr合并谢谢如您仍旧希望提交pr可以详情请看置顶issue - 由于本项目在快速迭代和功能调整并且有重构计划目前不接受任何未经过核心开发组讨论的pr合并谢谢如您仍旧希望提交pr可以详情请看置顶issue
@@ -78,8 +73,6 @@
- [🐳 Docker部署指南](docs/docker_deploy.md) - [🐳 Docker部署指南](docs/docker_deploy.md)
### 配置说明 ### 配置说明
- [🎀 新手配置指南](docs/installation_cute.md) - 通俗易懂的配置教程,适合初次使用的猫娘 - [🎀 新手配置指南](docs/installation_cute.md) - 通俗易懂的配置教程,适合初次使用的猫娘

39
bot.py
View File

@@ -2,6 +2,7 @@ import asyncio
import os import os
import shutil import shutil
import sys import sys
from pathlib import Path
import nonebot import nonebot
import time import time
@@ -10,10 +11,11 @@ import uvicorn
from dotenv import load_dotenv from dotenv import load_dotenv
from nonebot.adapters.onebot.v11 import Adapter from nonebot.adapters.onebot.v11 import Adapter
import platform import platform
from src.plugins.utils.logger_config import LogModule, LogClassification from src.common.logger import get_module_logger
# 配置日志格式 # 配置主程序日志格式
logger = get_module_logger("main_bot")
# 获取没有加载env时的环境变量 # 获取没有加载env时的环境变量
env_mask = {key: os.getenv(key) for key in os.environ} env_mask = {key: os.getenv(key) for key in os.environ}
@@ -76,11 +78,11 @@ def init_env():
def load_env(): def load_env():
# 使用闭包实现对加载器的横向扩展,避免大量重复判断 # 使用闭包实现对加载器的横向扩展,避免大量重复判断
def prod(): def prod():
logger.success("加载生产环境变量配置") logger.success("成功加载生产环境变量配置")
load_dotenv(".env.prod", override=True) # override=True 允许覆盖已存在的环境变量 load_dotenv(".env.prod", override=True) # override=True 允许覆盖已存在的环境变量
def dev(): def dev():
logger.success("加载开发环境变量配置") logger.success("成功加载开发环境变量配置")
load_dotenv(".env.dev", override=True) # override=True 允许覆盖已存在的环境变量 load_dotenv(".env.dev", override=True) # override=True 允许覆盖已存在的环境变量
fn_map = {"prod": prod, "dev": dev} fn_map = {"prod": prod, "dev": dev}
@@ -100,11 +102,6 @@ def load_env():
RuntimeError(f"ENVIRONMENT 配置错误,请检查 .env 文件中的 ENVIRONMENT 变量及对应 .env.{env} 是否存在") RuntimeError(f"ENVIRONMENT 配置错误,请检查 .env 文件中的 ENVIRONMENT 变量及对应 .env.{env} 是否存在")
def load_logger():
global logger # 使得bot.py中其他函数也能调用
log_module = LogModule()
logger = log_module.setup_logger(LogClassification.BASE)
def scan_provider(env_config: dict): def scan_provider(env_config: dict):
provider = {} provider = {}
@@ -168,6 +165,26 @@ async def uvicorn_main():
uvicorn_server = server uvicorn_server = server
await server.serve() await server.serve()
def check_eula():
eula_file = Path("elua.confirmed")
# 如果已经确认过EULA直接返回
if eula_file.exists():
return
print("使用MaiMBot前请先阅读ELUA协议继续运行视为同意协议")
print("协议内容https://github.com/SengokuCola/MaiMBot/blob/main/EULA.md")
print('输入"同意""confirmed"继续运行')
while True:
user_input = input().strip().lower() # 转换为小写以忽略大小写
if user_input in ['同意', 'confirmed']:
# 创建确认文件
eula_file.touch()
break
else:
print('请输入"同意""confirmed"以继续运行')
def raw_main(): def raw_main():
# 利用 TZ 环境变量设定程序工作的时区 # 利用 TZ 环境变量设定程序工作的时区
@@ -175,6 +192,8 @@ def raw_main():
if platform.system().lower() != "windows": if platform.system().lower() != "windows":
time.tzset() time.tzset()
check_eula()
easter_egg() easter_egg()
init_config() init_config()
init_env() init_env()
@@ -206,8 +225,6 @@ def raw_main():
if __name__ == "__main__": if __name__ == "__main__":
try: try:
# 配置日志使得主程序直接退出时候也能访问logger
load_logger()
raw_main() raw_main()
app = nonebot.get_asgi() app = nonebot.get_asgi()

View File

@@ -42,8 +42,16 @@ def update_config():
update_dict(target[key], value) update_dict(target[key], value)
else: else:
try: try:
# 直接使用tomlkit的item方法创建新值 # 对数组类型进行特殊处理
target[key] = tomlkit.item(value) if isinstance(value, list):
# 如果是空数组,确保它保持为空数组
if not value:
target[key] = tomlkit.array()
else:
target[key] = tomlkit.array(value)
else:
# 其他类型使用item方法创建新值
target[key] = tomlkit.item(value)
except (TypeError, ValueError): except (TypeError, ValueError):
# 如果转换失败,直接赋值 # 如果转换失败,直接赋值
target[key] = value target[key] = value

198
src/common/logger.py Normal file
View File

@@ -0,0 +1,198 @@
from loguru import logger
from typing import Dict, Optional, Union, List
import sys
import os
from types import ModuleType
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
# 保存原生处理器ID
default_handler_id = None
for handler_id in logger._core.handlers:
default_handler_id = handler_id
break
# 移除默认处理器
if default_handler_id is not None:
logger.remove(default_handler_id)
# 类型别名
LoguruLogger = logger.__class__
# 全局注册表记录模块与处理器ID的映射
_handler_registry: Dict[str, List[int]] = {}
# 获取日志存储根地址
current_file_path = Path(__file__).resolve()
LOG_ROOT = "logs"
# 默认全局配置
DEFAULT_CONFIG = {
# 日志级别配置
"console_level": "INFO",
"file_level": "DEBUG",
# 格式配置
"console_format": (
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{extra[module]: <12}</cyan> | "
"<level>{message}</level>"
),
"file_format": (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
"{extra[module]: <15} | "
"{message}"
),
"log_dir": LOG_ROOT,
"rotation": "00:00",
"retention": "3 days",
"compression": "zip",
}
def is_registered_module(record: dict) -> bool:
"""检查是否为已注册的模块"""
return record["extra"].get("module") in _handler_registry
def is_unregistered_module(record: dict) -> bool:
"""检查是否为未注册的模块"""
return not is_registered_module(record)
def log_patcher(record: dict) -> None:
"""自动填充未设置模块名的日志记录,保留原生模块名称"""
if "module" not in record["extra"]:
# 尝试从name中提取模块名
module_name = record.get("name", "")
if module_name == "":
module_name = "root"
record["extra"]["module"] = module_name
# 应用全局修补器
logger.configure(patcher=log_patcher)
class LogConfig:
"""日志配置类"""
def __init__(self, **kwargs):
self.config = DEFAULT_CONFIG.copy()
self.config.update(kwargs)
def to_dict(self) -> dict:
return self.config.copy()
def update(self, **kwargs):
self.config.update(kwargs)
def get_module_logger(
module: Union[str, ModuleType],
*,
console_level: Optional[str] = None,
file_level: Optional[str] = None,
extra_handlers: Optional[List[dict]] = None,
config: Optional[LogConfig] = None
) -> LoguruLogger:
module_name = module if isinstance(module, str) else module.__name__
current_config = config.config if config else DEFAULT_CONFIG
# 清理旧处理器
if module_name in _handler_registry:
for handler_id in _handler_registry[module_name]:
logger.remove(handler_id)
del _handler_registry[module_name]
handler_ids = []
# 控制台处理器
console_id = logger.add(
sink=sys.stderr,
level=os.getenv("CONSOLE_LOG_LEVEL", console_level or current_config["console_level"]),
format=current_config["console_format"],
filter=lambda record: record["extra"].get("module") == module_name,
enqueue=True,
)
handler_ids.append(console_id)
# 文件处理器
log_dir = Path(current_config["log_dir"])
log_dir.mkdir(parents=True, exist_ok=True)
log_file = log_dir / module_name / f"{{time:YYYY-MM-DD}}.log"
log_file.parent.mkdir(parents=True, exist_ok=True)
file_id = logger.add(
sink=str(log_file),
level=os.getenv("FILE_LOG_LEVEL", file_level or current_config["file_level"]),
format=current_config["file_format"],
rotation=current_config["rotation"],
retention=current_config["retention"],
compression=current_config["compression"],
encoding="utf-8",
filter=lambda record: record["extra"].get("module") == module_name,
enqueue=True,
)
handler_ids.append(file_id)
# 额外处理器
if extra_handlers:
for handler in extra_handlers:
handler_id = logger.add(**handler)
handler_ids.append(handler_id)
# 更新注册表
_handler_registry[module_name] = handler_ids
return logger.bind(module=module_name)
def remove_module_logger(module_name: str) -> None:
"""清理指定模块的日志处理器"""
if module_name in _handler_registry:
for handler_id in _handler_registry[module_name]:
logger.remove(handler_id)
del _handler_registry[module_name]
# 添加全局默认处理器(只处理未注册模块的日志--->控制台)
DEFAULT_GLOBAL_HANDLER = logger.add(
sink=sys.stderr,
level=os.getenv("DEFAULT_CONSOLE_LOG_LEVEL", "SUCCESS"),
format=(
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{name: <12}</cyan> | "
"<level>{message}</level>"
),
filter=is_unregistered_module, # 只处理未注册模块的日志
enqueue=True,
)
# 添加全局默认文件处理器(只处理未注册模块的日志--->logs文件夹
log_dir = Path(DEFAULT_CONFIG["log_dir"])
log_dir.mkdir(parents=True, exist_ok=True)
other_log_dir = log_dir / "other"
other_log_dir.mkdir(parents=True, exist_ok=True)
DEFAULT_FILE_HANDLER = logger.add(
sink=str(other_log_dir / f"{{time:YYYY-MM-DD}}.log"),
level=os.getenv("DEFAULT_FILE_LOG_LEVEL", "DEBUG"),
format=(
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level: <8} | "
"{name: <15} | "
"{message}"
),
rotation=DEFAULT_CONFIG["rotation"],
retention=DEFAULT_CONFIG["retention"],
compression=DEFAULT_CONFIG["compression"],
encoding="utf-8",
filter=is_unregistered_module, # 只处理未注册模块的日志
enqueue=True,
)

347
src/gui/logger_gui.py Normal file
View File

@@ -0,0 +1,347 @@
import customtkinter as ctk
import subprocess
import threading
import queue
import re
import os
import signal
from collections import deque
# 设置应用的外观模式和默认颜色主题
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
class LogViewerApp(ctk.CTk):
"""日志查看器应用的主类继承自customtkinter的CTk类"""
def __init__(self):
"""初始化日志查看器应用的界面和状态"""
super().__init__()
self.title("日志查看器")
self.geometry("1200x800")
# 初始化进程、日志队列、日志数据等变量
self.process = None
self.log_queue = queue.Queue()
self.log_data = deque(maxlen=10000) # 使用固定长度队列
self.available_levels = set()
self.available_modules = set()
self.sorted_modules = []
self.module_checkboxes = {} # 存储模块复选框的字典
# 日志颜色配置
self.color_config = {
"time": "#888888",
"DEBUG": "#2196F3",
"INFO": "#4CAF50",
"WARNING": "#FF9800",
"ERROR": "#F44336",
"module": "#D4D0AB",
"default": "#FFFFFF",
}
# 列可见性配置
self.column_visibility = {"show_time": True, "show_level": True, "show_module": True}
# 选中的日志等级和模块
self.selected_levels = set()
self.selected_modules = set()
# 创建界面组件并启动日志队列处理
self.create_widgets()
self.after(100, self.process_log_queue)
def create_widgets(self):
"""创建应用界面的各个组件"""
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=1)
# 控制面板
control_frame = ctk.CTkFrame(self)
control_frame.grid(row=0, column=0, sticky="ew", padx=10, pady=5)
self.start_btn = ctk.CTkButton(control_frame, text="启动", command=self.start_process)
self.start_btn.pack(side="left", padx=5)
self.stop_btn = ctk.CTkButton(control_frame, text="停止", command=self.stop_process, state="disabled")
self.stop_btn.pack(side="left", padx=5)
self.clear_btn = ctk.CTkButton(control_frame, text="清屏", command=self.clear_logs)
self.clear_btn.pack(side="left", padx=5)
column_filter_frame = ctk.CTkFrame(control_frame)
column_filter_frame.pack(side="left", padx=20)
self.time_check = ctk.CTkCheckBox(column_filter_frame, text="显示时间", command=self.refresh_logs)
self.time_check.pack(side="left", padx=5)
self.time_check.select()
self.level_check = ctk.CTkCheckBox(column_filter_frame, text="显示等级", command=self.refresh_logs)
self.level_check.pack(side="left", padx=5)
self.level_check.select()
self.module_check = ctk.CTkCheckBox(column_filter_frame, text="显示模块", command=self.refresh_logs)
self.module_check.pack(side="left", padx=5)
self.module_check.select()
# 筛选面板
filter_frame = ctk.CTkFrame(self)
filter_frame.grid(row=0, column=1, rowspan=2, sticky="ns", padx=5)
ctk.CTkLabel(filter_frame, text="日志等级筛选").pack(pady=5)
self.level_scroll = ctk.CTkScrollableFrame(filter_frame, width=150, height=200)
self.level_scroll.pack(fill="both", expand=True, padx=5)
ctk.CTkLabel(filter_frame, text="模块筛选").pack(pady=5)
self.module_filter_entry = ctk.CTkEntry(filter_frame, placeholder_text="输入模块过滤词")
self.module_filter_entry.pack(pady=5)
self.module_filter_entry.bind("<KeyRelease>", self.update_module_filter)
self.module_scroll = ctk.CTkScrollableFrame(filter_frame, width=300, height=200)
self.module_scroll.pack(fill="both", expand=True, padx=5)
self.log_text = ctk.CTkTextbox(self, wrap="word")
self.log_text.grid(row=1, column=0, sticky="nsew", padx=10, pady=5)
self.init_text_tags()
def update_module_filter(self, event):
"""根据模块过滤词更新模块复选框的显示"""
filter_text = self.module_filter_entry.get().strip().lower()
for module, checkbox in self.module_checkboxes.items():
if filter_text in module.lower():
checkbox.pack(anchor="w", padx=5, pady=2)
else:
checkbox.pack_forget()
def update_filters(self, level, module):
"""更新日志等级和模块的筛选器"""
if level not in self.available_levels:
self.available_levels.add(level)
self.add_checkbox(self.level_scroll, level, "level")
module_key = self.get_module_key(module)
if module_key not in self.available_modules:
self.available_modules.add(module_key)
self.sorted_modules = sorted(self.available_modules, key=lambda x: x.lower())
self.rebuild_module_checkboxes()
def rebuild_module_checkboxes(self):
"""重新构建模块复选框"""
# 清空现有复选框
for widget in self.module_scroll.winfo_children():
widget.destroy()
self.module_checkboxes.clear()
# 重建排序后的复选框
for module in self.sorted_modules:
self.add_checkbox(self.module_scroll, module, "module")
def add_checkbox(self, parent, text, type_):
"""在指定父组件中添加复选框"""
def update_filter():
current = cb.get()
if type_ == "level":
(self.selected_levels.add if current else self.selected_levels.discard)(text)
else:
(self.selected_modules.add if current else self.selected_modules.discard)(text)
self.refresh_logs()
cb = ctk.CTkCheckBox(parent, text=text, command=update_filter)
cb.select() # 初始选中
# 手动同步初始状态到集合(关键修复)
if type_ == "level":
self.selected_levels.add(text)
else:
self.selected_modules.add(text)
if type_ == "module":
self.module_checkboxes[text] = cb
cb.pack(anchor="w", padx=5, pady=2)
return cb
def check_filter(self, entry):
"""检查日志条目是否符合当前筛选条件"""
level_ok = not self.selected_levels or entry["level"] in self.selected_levels
module_key = self.get_module_key(entry["module"])
module_ok = not self.selected_modules or module_key in self.selected_modules
return level_ok and module_ok
def init_text_tags(self):
"""初始化日志文本的颜色标签"""
for tag, color in self.color_config.items():
self.log_text.tag_config(tag, foreground=color)
self.log_text.tag_config("default", foreground=self.color_config["default"])
def start_process(self):
"""启动日志进程并开始读取输出"""
self.process = subprocess.Popen(
["nb", "run"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
encoding="utf-8",
errors="ignore",
)
self.start_btn.configure(state="disabled")
self.stop_btn.configure(state="normal")
threading.Thread(target=self.read_output, daemon=True).start()
def stop_process(self):
"""停止日志进程并清理相关资源"""
if self.process:
try:
if hasattr(self.process, "pid"):
if os.name == "nt":
subprocess.run(
["taskkill", "/F", "/T", "/PID", str(self.process.pid)], check=True, capture_output=True
)
else:
os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
except (subprocess.CalledProcessError, ProcessLookupError, OSError) as e:
print(f"终止进程失败: {e}")
finally:
self.process = None
self.log_queue.queue.clear()
self.start_btn.configure(state="normal")
self.stop_btn.configure(state="disabled")
self.refresh_logs()
def read_output(self):
"""读取日志进程的输出并放入队列"""
try:
while self.process and self.process.poll() is None:
line = self.process.stdout.readline()
if line:
self.log_queue.put(line)
else:
break # 避免空循环
self.process.stdout.close() # 确保关闭文件描述符
except ValueError: # 处理可能的I/O操作异常
pass
def process_log_queue(self):
"""处理日志队列中的日志条目"""
while not self.log_queue.empty():
line = self.log_queue.get()
self.process_log_line(line)
self.after(100, self.process_log_queue)
def process_log_line(self, line):
"""解析单行日志并更新日志数据和筛选器"""
match = re.match(
r"""^
(?:(?P<time>\d{2}:\d{2}(?::\d{2})?)\s*\|\s*)?
(?P<level>\w+)\s*\|\s*
(?P<module>.*?)
\s*[-|]\s*
(?P<message>.*)
$""",
line.strip(),
re.VERBOSE,
)
if match:
groups = match.groupdict()
time = groups.get("time", "")
level = groups.get("level", "OTHER")
module = groups.get("module", "UNKNOWN").strip()
message = groups.get("message", "").strip()
raw_line = line
else:
time, level, module, message = "", "OTHER", "UNKNOWN", line
raw_line = line
self.update_filters(level, module)
log_entry = {"raw": raw_line, "time": time, "level": level, "module": module, "message": message}
self.log_data.append(log_entry)
if self.check_filter(log_entry):
self.display_log(log_entry)
def get_module_key(self, module_name):
"""获取模块名称的标准化键"""
cleaned = module_name.strip()
return re.sub(r":\d+$", "", cleaned)
def display_log(self, entry):
"""在日志文本框中显示日志条目"""
parts = []
tags = []
if self.column_visibility["show_time"] and entry["time"]:
parts.append(f"{entry['time']} ")
tags.append("time")
if self.column_visibility["show_level"]:
level_tag = entry["level"] if entry["level"] in self.color_config else "default"
parts.append(f"{entry['level']:<8} ")
tags.append(level_tag)
if self.column_visibility["show_module"]:
parts.append(f"{entry['module']} ")
tags.append("module")
parts.append(f"- {entry['message']}\n")
tags.append("default")
self.log_text.configure(state="normal")
for part, tag in zip(parts, tags):
self.log_text.insert("end", part, tag)
self.log_text.see("end")
self.log_text.configure(state="disabled")
def refresh_logs(self):
"""刷新日志显示,根据筛选条件重新显示日志"""
self.column_visibility = {
"show_time": self.time_check.get(),
"show_level": self.level_check.get(),
"show_module": self.module_check.get(),
}
self.log_text.configure(state="normal")
self.log_text.delete("1.0", "end")
filtered_logs = [entry for entry in self.log_data if self.check_filter(entry)]
for entry in filtered_logs:
parts = []
tags = []
if self.column_visibility["show_time"] and entry["time"]:
parts.append(f"{entry['time']} ")
tags.append("time")
if self.column_visibility["show_level"]:
level_tag = entry["level"] if entry["level"] in self.color_config else "default"
parts.append(f"{entry['level']:<8} ")
tags.append(level_tag)
if self.column_visibility["show_module"]:
parts.append(f"{entry['module']} ")
tags.append("module")
parts.append(f"- {entry['message']}\n")
tags.append("default")
for part, tag in zip(parts, tags):
self.log_text.insert("end", part, tag)
self.log_text.see("end")
self.log_text.configure(state="disabled")
def clear_logs(self):
"""清空日志文本框中的内容"""
self.log_text.configure(state="normal")
self.log_text.delete("1.0", "end")
self.log_text.configure(state="disabled")
if __name__ == "__main__":
# 启动日志查看器应用
app = LogViewerApp()
app.mainloop()

View File

@@ -5,13 +5,14 @@ import threading
import time import time
from datetime import datetime from datetime import datetime
from typing import Dict, List from typing import Dict, List
from loguru import logger
from typing import Optional from typing import Optional
from src.common.logger import get_module_logger
import customtkinter as ctk import customtkinter as ctk
from dotenv import load_dotenv from dotenv import load_dotenv
logger = get_module_logger("gui")
# 获取当前文件的目录 # 获取当前文件的目录
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
# 获取项目根目录 # 获取项目根目录
@@ -30,6 +31,7 @@ else:
logger.error("未找到环境配置文件") logger.error("未找到环境配置文件")
sys.exit(1) sys.exit(1)
class ReasoningGUI: class ReasoningGUI:
def __init__(self): def __init__(self):
# 记录启动时间戳转换为Unix时间戳 # 记录启动时间戳转换为Unix时间戳

View File

@@ -2,7 +2,6 @@ import asyncio
import time import time
import os import os
from loguru import logger
from nonebot import get_driver, on_message, on_notice, require from nonebot import get_driver, on_message, on_notice, require
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment, MessageEvent, NoticeEvent from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment, MessageEvent, NoticeEvent
@@ -21,6 +20,9 @@ from ..memory_system.memory import hippocampus, memory_graph
from .bot import ChatBot from .bot import ChatBot
from .message_sender import message_manager, message_sender from .message_sender import message_manager, message_sender
from .storage import MessageStorage from .storage import MessageStorage
from src.common.logger import get_module_logger
logger = get_module_logger("chat_init")
# 创建LLM统计实例 # 创建LLM统计实例
llm_stats = LLMStatistics("llm_statistics.txt") llm_stats = LLMStatistics("llm_statistics.txt")

View File

@@ -12,6 +12,7 @@ from nonebot.adapters.onebot.v11 import (
FriendRecallNoticeEvent, FriendRecallNoticeEvent,
) )
from src.common.logger import get_module_logger
from ..memory_system.memory import hippocampus from ..memory_system.memory import hippocampus
from ..moods.moods import MoodManager # 导入情绪管理器 from ..moods.moods import MoodManager # 导入情绪管理器
from .config import global_config from .config import global_config
@@ -31,11 +32,8 @@ from .utils_image import image_path_to_base64
from .utils_user import get_user_nickname, get_user_cardname, get_groupname from .utils_user import get_user_nickname, get_user_cardname, get_groupname
from ..willing.willing_manager import willing_manager # 导入意愿管理器 from ..willing.willing_manager import willing_manager # 导入意愿管理器
from .message_base import UserInfo, GroupInfo, Seg from .message_base import UserInfo, GroupInfo, Seg
from ..utils.logger_config import LogClassification, LogModule
# 配置日志 logger = get_module_logger("chat_bot")
log_module = LogModule()
logger = log_module.setup_logger(LogClassification.CHAT)
class ChatBot: class ChatBot:
@@ -336,9 +334,12 @@ class ChatBot:
platform="qq", platform="qq",
) )
group_info = GroupInfo( if isinstance(event, GroupRecallNoticeEvent):
group_id=event.group_id, group_name=None, platform="qq" group_info = GroupInfo(
) group_id=event.group_id, group_name=None, platform="qq"
)
else:
group_info = None
chat = await chat_manager.get_or_create_stream( chat = await chat_manager.get_or_create_stream(
platform=user_info.platform, user_info=user_info, group_info=group_info platform=user_info.platform, user_info=user_info, group_info=group_info

View File

@@ -4,11 +4,14 @@ import time
import copy import copy
from typing import Dict, Optional from typing import Dict, Optional
from loguru import logger
from ...common.database import db from ...common.database import db
from .message_base import GroupInfo, UserInfo from .message_base import GroupInfo, UserInfo
from src.common.logger import get_module_logger
logger = get_module_logger("chat_stream")
class ChatStream: class ChatStream:
"""聊天流对象,存储一个完整的聊天上下文""" """聊天流对象,存储一个完整的聊天上下文"""

View File

@@ -4,11 +4,14 @@ from dataclasses import dataclass, field
from typing import Dict, List, Optional from typing import Dict, List, Optional
import tomli import tomli
from loguru import logger
from packaging import version from packaging import version
from packaging.version import Version, InvalidVersion from packaging.version import Version, InvalidVersion
from packaging.specifiers import SpecifierSet, InvalidSpecifier from packaging.specifiers import SpecifierSet, InvalidSpecifier
from src.common.logger import get_module_logger
logger = get_module_logger("config")
@dataclass @dataclass
class BotConfig: class BotConfig:
@@ -440,10 +443,3 @@ else:
global_config = BotConfig.load_config(config_path=bot_config_path) global_config = BotConfig.load_config(config_path=bot_config_path)
if not global_config.enable_advance_output:
logger.remove()
# 调试输出功能
if global_config.enable_debug_output:
logger.remove()
logger.add(sys.stdout, level="DEBUG")

View File

@@ -7,7 +7,7 @@ from typing import Dict, List, Optional, Union
import ssl import ssl
import os import os
import aiohttp import aiohttp
from loguru import logger from src.common.logger import get_module_logger
from nonebot import get_driver from nonebot import get_driver
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
@@ -24,6 +24,7 @@ config = driver.config
ssl_context = ssl.create_default_context() ssl_context = ssl.create_default_context()
ssl_context.set_ciphers("AES128-GCM-SHA256") ssl_context.set_ciphers("AES128-GCM-SHA256")
logger = get_module_logger("cq_code")
@dataclass @dataclass
class CQCode: class CQCode:
@@ -249,6 +250,13 @@ class CQCode:
if self.reply_message is None: if self.reply_message is None:
return None return None
if hasattr(self.reply_message, "group_id"):
group_info = GroupInfo(
platform="qq", group_id=self.reply_message.group_id, group_name=""
)
else:
group_info = None
if self.reply_message.sender.user_id: if self.reply_message.sender.user_id:
message_obj = MessageRecvCQ( message_obj = MessageRecvCQ(
user_info=UserInfo( user_info=UserInfo(
@@ -256,7 +264,7 @@ class CQCode:
), ),
message_id=self.reply_message.message_id, message_id=self.reply_message.message_id,
raw_message=str(self.reply_message.message), raw_message=str(self.reply_message.message),
group_info=GroupInfo(group_id=self.reply_message.group_id), group_info=group_info,
) )
await message_obj.initialize() await message_obj.initialize()

View File

@@ -9,7 +9,6 @@ from typing import Optional, Tuple
from PIL import Image from PIL import Image
import io import io
from loguru import logger
from nonebot import get_driver from nonebot import get_driver
from ...common.database import db from ...common.database import db
@@ -17,12 +16,10 @@ from ..chat.config import global_config
from ..chat.utils import get_embedding from ..chat.utils import get_embedding
from ..chat.utils_image import ImageManager, image_path_to_base64 from ..chat.utils_image import ImageManager, image_path_to_base64
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
from src.common.logger import get_module_logger
from ..utils.logger_config import LogClassification, LogModule logger = get_module_logger("emoji")
# 配置日志
log_module = LogModule()
logger = log_module.setup_logger(LogClassification.EMOJI)
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@@ -3,7 +3,6 @@ import time
from typing import List, Optional, Tuple, Union from typing import List, Optional, Tuple, Union
from nonebot import get_driver from nonebot import get_driver
from loguru import logger
from ...common.database import db from ...common.database import db
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
@@ -12,6 +11,9 @@ from .message import MessageRecv, MessageThinking, Message
from .prompt_builder import prompt_builder from .prompt_builder import prompt_builder
from .relationship_manager import relationship_manager from .relationship_manager import relationship_manager
from .utils import process_llm_response from .utils import process_llm_response
from src.common.logger import get_module_logger
logger = get_module_logger("response_gen")
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@@ -6,12 +6,14 @@ from dataclasses import dataclass
from typing import Dict, List, Optional from typing import Dict, List, Optional
import urllib3 import urllib3
from loguru import logger
from .utils_image import image_manager from .utils_image import image_manager
from .message_base import Seg, GroupInfo, UserInfo, BaseMessageInfo, MessageBase from .message_base import Seg, GroupInfo, UserInfo, BaseMessageInfo, MessageBase
from .chat_stream import ChatStream, chat_manager from .chat_stream import ChatStream, chat_manager
from src.common.logger import get_module_logger
logger = get_module_logger("chat_message")
# 禁用SSL警告 # 禁用SSL警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

View File

@@ -2,7 +2,7 @@ import asyncio
import time import time
from typing import Dict, List, Optional, Union from typing import Dict, List, Optional, Union
from loguru import logger from src.common.logger import get_module_logger
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
from ...common.database import db from ...common.database import db
from .message_cq import MessageSendCQ from .message_cq import MessageSendCQ
@@ -12,6 +12,7 @@ from .storage import MessageStorage
from .config import global_config from .config import global_config
from .utils import truncate_message from .utils import truncate_message
logger = get_module_logger("msg_sender")
class Message_Sender: class Message_Sender:
"""发送器""" """发送器"""
@@ -50,7 +51,6 @@ class Message_Sender:
if not is_recalled: if not is_recalled:
message_json = message.to_dict() message_json = message.to_dict()
message_send = MessageSendCQ(data=message_json) message_send = MessageSendCQ(data=message_json)
# logger.debug(message_send.message_info,message_send.raw_message)
message_preview = truncate_message(message.processed_plain_text) message_preview = truncate_message(message.processed_plain_text)
if message_send.message_info.group_info and message_send.message_info.group_info.group_id: if message_send.message_info.group_info and message_send.message_info.group_info.group_id:
try: try:
@@ -188,16 +188,17 @@ class MessageManager:
else: else:
if ( if (
message_earliest.is_head message_earliest.is_head
and message_earliest.update_thinking_time() > 30 and message_earliest.update_thinking_time() > 10
and not message_earliest.is_private_message() # 避免在私聊时插入reply and not message_earliest.is_private_message() # 避免在私聊时插入reply
): ):
message_earliest.set_reply() message_earliest.set_reply()
await message_sender.send_message(message_earliest)
await message_earliest.process() await message_earliest.process()
print( await message_sender.send_message(message_earliest)
f"\033[1;34m[调试]\033[0m 消息“{truncate_message(message_earliest.processed_plain_text)}”正在发送中"
)
await self.storage.store_message(message_earliest, message_earliest.chat_stream, None) await self.storage.store_message(message_earliest, message_earliest.chat_stream, None)
@@ -217,11 +218,11 @@ class MessageManager:
and not message_earliest.is_private_message() # 避免在私聊时插入reply and not message_earliest.is_private_message() # 避免在私聊时插入reply
): ):
msg.set_reply() msg.set_reply()
await msg.process()
await message_sender.send_message(msg) await message_sender.send_message(msg)
# if msg.is_emoji:
# msg.processed_plain_text = "[表情包]"
await msg.process()
await self.storage.store_message(msg, msg.chat_stream, None) await self.storage.store_message(msg, msg.chat_stream, None)
if not container.remove_message(msg): if not container.remove_message(msg):

View File

@@ -9,12 +9,13 @@ from ..schedule.schedule_generator import bot_schedule
from .config import global_config from .config import global_config
from .utils import get_embedding, get_recent_group_detailed_plain_text, get_recent_group_speaker from .utils import get_embedding, get_recent_group_detailed_plain_text, get_recent_group_speaker
from .chat_stream import chat_manager from .chat_stream import chat_manager
<<<<<<< HEAD
from .relationship_manager import relationship_manager from .relationship_manager import relationship_manager
=======
from src.common.logger import get_module_logger
>>>>>>> main-fix
from ..utils.logger_config import LogClassification, LogModule logger = get_module_logger("prompt")
log_module = LogModule()
logger = log_module.setup_logger(LogClassification.PBUILDER)
logger.info("初始化Prompt系统") logger.info("初始化Prompt系统")

View File

@@ -1,12 +1,14 @@
import asyncio import asyncio
from typing import Optional from typing import Optional
from loguru import logger from src.common.logger import get_module_logger
from ...common.database import db from ...common.database import db
from .message_base import UserInfo from .message_base import UserInfo
from .chat_stream import ChatStream from .chat_stream import ChatStream
import math import math
logger = get_module_logger("rel_manager")
class Impression: class Impression:
traits: str = None traits: str = None
called: str = None called: str = None

View File

@@ -3,7 +3,9 @@ from typing import Optional, Union
from ...common.database import db from ...common.database import db
from .message import MessageSending, MessageRecv from .message import MessageSending, MessageRecv
from .chat_stream import ChatStream from .chat_stream import ChatStream
from loguru import logger from src.common.logger import get_module_logger
logger = get_module_logger("message_storage")
class MessageStorage: class MessageStorage:

View File

@@ -4,7 +4,9 @@ from nonebot import get_driver
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
from .config import global_config from .config import global_config
from loguru import logger from src.common.logger import get_module_logger
logger = get_module_logger("topic_identifier")
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@@ -7,7 +7,7 @@ from typing import Dict, List
import jieba import jieba
import numpy as np import numpy as np
from nonebot import get_driver from nonebot import get_driver
from loguru import logger from src.common.logger import get_module_logger
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
from ..utils.typo_generator import ChineseTypoGenerator from ..utils.typo_generator import ChineseTypoGenerator
@@ -21,6 +21,8 @@ from ...common.database import db
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config
logger = get_module_logger("chat_utils")
def db_message_to_str(message_dict: Dict) -> str: def db_message_to_str(message_dict: Dict) -> str:

View File

@@ -7,13 +7,16 @@ from typing import Optional, Union
from PIL import Image from PIL import Image
import io import io
from loguru import logger
from nonebot import get_driver from nonebot import get_driver
from ...common.database import db from ...common.database import db
from ..chat.config import global_config from ..chat.config import global_config
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
from src.common.logger import get_module_logger
logger = get_module_logger("chat_image")
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@@ -1,10 +1,11 @@
from nonebot import get_app from nonebot import get_app
from .api import router from .api import router
from loguru import logger from src.common.logger import get_module_logger
# 获取主应用实例并挂载路由 # 获取主应用实例并挂载路由
app = get_app() app = get_app()
app.include_router(router, prefix="/api") app.include_router(router, prefix="/api")
# 打印日志方便确认API已注册 # 打印日志方便确认API已注册
logger = get_module_logger("cfg_reload")
logger.success("配置重载API已注册可通过 /api/reload-config 访问") logger.success("配置重载API已注册可通过 /api/reload-config 访问")

View File

@@ -7,7 +7,9 @@ import jieba
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import networkx as nx import networkx as nx
from dotenv import load_dotenv from dotenv import load_dotenv
from loguru import logger from src.common.logger import get_module_logger
logger = get_module_logger("draw_memory")
# 添加项目根目录到 Python 路径 # 添加项目根目录到 Python 路径
root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")) root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))

View File

@@ -3,7 +3,6 @@ import datetime
import math import math
import random import random
import time import time
import os
import jieba import jieba
import networkx as nx import networkx as nx
@@ -18,14 +17,10 @@ from ..chat.utils import (
text_to_vector, text_to_vector,
) )
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
from src.common.logger import get_module_logger
from ..utils.logger_config import LogClassification, LogModule logger = get_module_logger("memory_sys")
# 配置日志
log_module = LogModule()
logger = log_module.setup_logger(LogClassification.MEMORY)
logger.info("初始化记忆系统")
class Memory_graph: class Memory_graph:
def __init__(self): def __init__(self):
@@ -46,9 +41,9 @@ class Memory_graph:
else: else:
# 如果是新边,初始化 strength 为 1 # 如果是新边,初始化 strength 为 1
self.G.add_edge(concept1, concept2, self.G.add_edge(concept1, concept2,
strength=1, strength=1,
created_time=current_time, # 添加创建时间 created_time=current_time, # 添加创建时间
last_modified=current_time) # 添加最后修改时间 last_modified=current_time) # 添加最后修改时间
def add_dot(self, concept, memory): def add_dot(self, concept, memory):
current_time = datetime.datetime.now().timestamp() current_time = datetime.datetime.now().timestamp()
@@ -69,9 +64,9 @@ class Memory_graph:
else: else:
# 如果是新节点,创建新的记忆列表 # 如果是新节点,创建新的记忆列表
self.G.add_node(concept, self.G.add_node(concept,
memory_items=[memory], memory_items=[memory],
created_time=current_time, # 添加创建时间 created_time=current_time, # 添加创建时间
last_modified=current_time) # 添加最后修改时间 last_modified=current_time) # 添加最后修改时间
def get_dot(self, concept): def get_dot(self, concept):
# 检查节点是否存在于图中 # 检查节点是否存在于图中
@@ -210,7 +205,8 @@ class Hippocampus:
# 成功抽取短期消息样本 # 成功抽取短期消息样本
# 数据写回:增加记忆次数 # 数据写回:增加记忆次数
for message in messages: for message in messages:
db.messages.update_one({"_id": message["_id"]}, {"$set": {"memorized_times": message["memorized_times"] + 1}}) db.messages.update_one({"_id": message["_id"]},
{"$set": {"memorized_times": message["memorized_times"] + 1}})
return messages return messages
try_count += 1 try_count += 1
# 三次尝试均失败 # 三次尝试均失败
@@ -387,9 +383,9 @@ class Hippocampus:
strength = int(similarity * 10) strength = int(similarity * 10)
logger.info(f"连接相似节点: {topic}{similar_topic} (强度: {strength})") logger.info(f"连接相似节点: {topic}{similar_topic} (强度: {strength})")
self.memory_graph.G.add_edge(topic, similar_topic, self.memory_graph.G.add_edge(topic, similar_topic,
strength=strength, strength=strength,
created_time=current_time, created_time=current_time,
last_modified=current_time) last_modified=current_time)
# 连接同批次的相关话题 # 连接同批次的相关话题
for i in range(len(all_topics)): for i in range(len(all_topics)):
@@ -533,9 +529,9 @@ class Hippocampus:
# 添加节点到图中 # 添加节点到图中
self.memory_graph.G.add_node(concept, self.memory_graph.G.add_node(concept,
memory_items=memory_items, memory_items=memory_items,
created_time=created_time, created_time=created_time,
last_modified=last_modified) last_modified=last_modified)
# 从数据库加载所有边 # 从数据库加载所有边
edges = list(db.graph_data.edges.find()) edges = list(db.graph_data.edges.find())
@@ -567,9 +563,9 @@ class Hippocampus:
# 只有当源节点和目标节点都存在时才添加边 # 只有当源节点和目标节点都存在时才添加边
if source in self.memory_graph.G and target in self.memory_graph.G: if source in self.memory_graph.G and target in self.memory_graph.G:
self.memory_graph.G.add_edge(source, target, self.memory_graph.G.add_edge(source, target,
strength=strength, strength=strength,
created_time=created_time, created_time=created_time,
last_modified=last_modified) last_modified=last_modified)
if need_update: if need_update:
logger.success("[数据库] 已为缺失的时间字段进行补充") logger.success("[数据库] 已为缺失的时间字段进行补充")
@@ -612,7 +608,7 @@ class Hippocampus:
edge_data = self.memory_graph.G[source][target] edge_data = self.memory_graph.G[source][target]
last_modified = edge_data.get('last_modified') last_modified = edge_data.get('last_modified')
if current_time - last_modified > 3600*global_config.memory_forget_time: if current_time - last_modified > 3600 * global_config.memory_forget_time:
current_strength = edge_data.get('strength', 1) current_strength = edge_data.get('strength', 1)
new_strength = current_strength - 1 new_strength = current_strength - 1
@@ -632,7 +628,7 @@ class Hippocampus:
node_data = self.memory_graph.G.nodes[node] node_data = self.memory_graph.G.nodes[node]
last_modified = node_data.get('last_modified', current_time) last_modified = node_data.get('last_modified', current_time)
if current_time - last_modified > 3600*24: if current_time - last_modified > 3600 * 24:
memory_items = node_data.get('memory_items', []) memory_items = node_data.get('memory_items', [])
if not isinstance(memory_items, list): if not isinstance(memory_items, list):
memory_items = [memory_items] if memory_items else [] memory_items = [memory_items] if memory_items else []
@@ -882,8 +878,8 @@ class Hippocampus:
matched_topics.add(input_topic) matched_topics.add(input_topic)
adjusted_sim = sim * penalty adjusted_sim = sim * penalty
topic_similarities[input_topic] = max(topic_similarities.get(input_topic, 0), adjusted_sim) topic_similarities[input_topic] = max(topic_similarities.get(input_topic, 0), adjusted_sim)
logger.debug( # logger.debug(
f"[激活] 主题「{input_topic}」-> 「{memory_topic}」(内容数: {content_count}, 相似度: {adjusted_sim:.3f})") # f"[激活] 主题「{input_topic}」-> 「{memory_topic}」(内容数: {content_count}, 相似度: {adjusted_sim:.3f})")
# 计算主题匹配率和平均相似度 # 计算主题匹配率和平均相似度
topic_match = len(matched_topics) / len(identified_topics) topic_match = len(matched_topics) / len(identified_topics)
@@ -943,6 +939,7 @@ def segment_text(text):
seg_text = list(jieba.cut(text)) seg_text = list(jieba.cut(text))
return seg_text return seg_text
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@@ -11,7 +11,7 @@ from pathlib import Path
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import networkx as nx import networkx as nx
from dotenv import load_dotenv from dotenv import load_dotenv
from loguru import logger from src.common.logger import get_module_logger
import jieba import jieba
# from chat.config import global_config # from chat.config import global_config
@@ -29,6 +29,8 @@ project_root = current_dir.parent.parent.parent
# env.dev文件路径 # env.dev文件路径
env_path = project_root / ".env.dev" env_path = project_root / ".env.dev"
logger = get_module_logger("mem_manual_bd")
# 加载环境变量 # 加载环境变量
if env_path.exists(): if env_path.exists():
logger.info(f"{env_path} 加载环境变量") logger.info(f"{env_path} 加载环境变量")

View File

@@ -12,9 +12,11 @@ import matplotlib.pyplot as plt
import networkx as nx import networkx as nx
import pymongo import pymongo
from dotenv import load_dotenv from dotenv import load_dotenv
from loguru import logger from src.common.logger import get_module_logger
import jieba import jieba
logger = get_module_logger("mem_test")
''' '''
该理论认为,当两个或多个事物在形态上具有相似性时, 该理论认为,当两个或多个事物在形态上具有相似性时,
它们在记忆中会形成关联。 它们在记忆中会形成关联。

View File

@@ -5,8 +5,9 @@ from typing import Tuple, Union
import aiohttp import aiohttp
import requests import requests
from loguru import logger from src.common.logger import get_module_logger
logger = get_module_logger("offline_llm")
class LLMModel: class LLMModel:
def __init__(self, model_name="deepseek-ai/DeepSeek-V3", **kwargs): def __init__(self, model_name="deepseek-ai/DeepSeek-V3", **kwargs):

View File

@@ -5,7 +5,7 @@ from datetime import datetime
from typing import Tuple, Union from typing import Tuple, Union
import aiohttp import aiohttp
from loguru import logger from src.common.logger import get_module_logger
from nonebot import get_driver from nonebot import get_driver
import base64 import base64
from PIL import Image from PIL import Image
@@ -16,6 +16,8 @@ from ..chat.config import global_config
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config
logger = get_module_logger("model_utils")
class LLM_request: class LLM_request:
# 定义需要转换的模型列表,作为类变量避免重复 # 定义需要转换的模型列表,作为类变量避免重复
@@ -165,8 +167,8 @@ class LLM_request:
# 判断是否为流式 # 判断是否为流式
stream_mode = self.params.get("stream", False) stream_mode = self.params.get("stream", False)
logger_msg = "进入流式输出模式," if stream_mode else "" logger_msg = "进入流式输出模式," if stream_mode else ""
logger.debug(f"{logger_msg}发送请求到URL: {api_url}") # logger.debug(f"{logger_msg}发送请求到URL: {api_url}")
logger.info(f"使用模型: {self.model_name}") # logger.info(f"使用模型: {self.model_name}")
# 构建请求体 # 构建请求体
if image_base64: if image_base64:

View File

@@ -4,7 +4,9 @@ import time
from dataclasses import dataclass from dataclasses import dataclass
from ..chat.config import global_config from ..chat.config import global_config
from loguru import logger from src.common.logger import get_module_logger
logger = get_module_logger("mood_manager")
@dataclass @dataclass
class MoodState: class MoodState:

View File

@@ -5,7 +5,9 @@ import platform
import os import os
import json import json
import threading import threading
from loguru import logger from src.common.logger import get_module_logger
logger = get_module_logger("remote")
# UUID文件路径 # UUID文件路径
UUID_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "client_uuid.json") UUID_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "client_uuid.json")
@@ -30,9 +32,9 @@ def get_unique_id():
try: try:
with open(UUID_FILE, "w") as f: with open(UUID_FILE, "w") as f:
json.dump({"client_id": client_id}, f) json.dump({"client_id": client_id}, f)
print("已保存新生成的客户端ID到本地文件") logger.info("已保存新生成的客户端ID到本地文件")
except IOError as e: except IOError as e:
print(f"保存UUID时出错: {e}") logger.error(f"保存UUID时出错: {e}")
return client_id return client_id

View File

@@ -3,13 +3,15 @@ import json
import re import re
from typing import Dict, Union from typing import Dict, Union
from loguru import logger
from nonebot import get_driver from nonebot import get_driver
from src.plugins.chat.config import global_config from src.plugins.chat.config import global_config
from ...common.database import db # 使用正确的导入语法 from ...common.database import db # 使用正确的导入语法
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
from src.common.logger import get_module_logger
logger = get_module_logger("scheduler")
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@@ -3,10 +3,11 @@ import time
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, Dict from typing import Any, Dict
from loguru import logger from src.common.logger import get_module_logger
from ...common.database import db from ...common.database import db
logger = get_module_logger("llm_statistics")
class LLMStatistics: class LLMStatistics:
def __init__(self, output_file: str = "llm_statistics.txt"): def __init__(self, output_file: str = "llm_statistics.txt"):

View File

@@ -13,8 +13,9 @@ from pathlib import Path
import jieba import jieba
from pypinyin import Style, pinyin from pypinyin import Style, pinyin
from loguru import logger from src.common.logger import get_module_logger
logger = get_module_logger("typo_gen")
class ChineseTypoGenerator: class ChineseTypoGenerator:
def __init__(self, def __init__(self,

View File

@@ -2,7 +2,9 @@ import asyncio
import random import random
import time import time
from typing import Dict from typing import Dict
from loguru import logger from src.common.logger import get_module_logger
logger = get_module_logger("mode_dynamic")
from ..chat.config import global_config from ..chat.config import global_config

View File

@@ -1,11 +1,13 @@
from typing import Optional from typing import Optional
from loguru import logger from src.common.logger import get_module_logger
from ..chat.config import global_config from ..chat.config import global_config
from .mode_classical import WillingManager as ClassicalWillingManager from .mode_classical import WillingManager as ClassicalWillingManager
from .mode_dynamic import WillingManager as DynamicWillingManager from .mode_dynamic import WillingManager as DynamicWillingManager
from .mode_custom import WillingManager as CustomWillingManager from .mode_custom import WillingManager as CustomWillingManager
logger = get_module_logger("willing")
def init_willing_manager() -> Optional[object]: def init_willing_manager() -> Optional[object]:
""" """
根据配置初始化并返回对应的WillingManager实例 根据配置初始化并返回对应的WillingManager实例

View File

@@ -23,7 +23,13 @@ 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
#定义你要用的api的key(需要去对应网站申请哦) # 定义你要用的api的key(需要去对应网站申请哦)
DEEP_SEEK_KEY= DEEP_SEEK_KEY=
CHAT_ANY_WHERE_KEY= CHAT_ANY_WHERE_KEY=
SILICONFLOW_KEY= SILICONFLOW_KEY=
# 定义日志相关配置
CONSOLE_LOG_LEVEL=INFO # 自定义日志的默认控制台输出日志级别
FILE_LOG_LEVEL=DEBUG # 自定义日志的默认文件输出日志级别
DEFAULT_CONSOLE_LOG_LEVEL=SUCCESS # 原生日志的控制台输出日志级别nonebot就是这一类
DEFAULT_FILE_LOG_LEVEL=DEBUG # 原生日志的默认文件输出日志级别nonebot就是这一类

215
webui.py
View File

@@ -2,16 +2,20 @@ import gradio as gr
import os import os
import sys import sys
import toml import toml
from loguru import logger from src.common.logger import get_module_logger
import shutil import shutil
import ast import ast
import json import json
logger = get_module_logger("webui")
is_share = False is_share = False
debug = True debug = True
config_data = toml.load("config/bot_config.toml") config_data = toml.load("config/bot_config.toml")
CONFIG_VERSION = config_data["inner"]["version"]
PARSED_CONFIG_VERSION = float(CONFIG_VERSION[2:])
#============================================== #==============================================
#env环境配置文件读取部分 #env环境配置文件读取部分
def parse_env_config(config_file): def parse_env_config(config_file):
@@ -69,8 +73,29 @@ def save_to_env_file(env_variables, filename=".env.prod"):
f.write(f"{var[4:]}={value}\n") # 移除env_前缀 f.write(f"{var[4:]}={value}\n") # 移除env_前缀
logger.info(f"配置已保存到 {filename}") logger.info(f"配置已保存到 {filename}")
#载入env文件并解析
env_config_file = ".env.prod" # 配置文件路径 env_config_file = ".env.prod" # 配置文件路径
env_config_data = parse_env_config(env_config_file) env_config_data = parse_env_config(env_config_file)
if "env_VOLCENGINE_BASE_URL" in env_config_data:
logger.info("VOLCENGINE_BASE_URL 已存在,使用默认值")
env_config_data["env_VOLCENGINE_BASE_URL"] = "https://ark.cn-beijing.volces.com/api/v3"
else:
logger.info("VOLCENGINE_BASE_URL 不存在,已创建并使用默认值")
env_config_data["env_VOLCENGINE_BASE_URL"] = "https://ark.cn-beijing.volces.com/api/v3"
if "env_VOLCENGINE_KEY" in env_config_data:
logger.info("VOLCENGINE_KEY 已存在,保持不变")
else:
logger.info("VOLCENGINE_KEY 不存在,已创建并使用默认值")
env_config_data["env_VOLCENGINE_KEY"] = "volc_key"
save_to_env_file(env_config_data, env_config_file)
MODEL_PROVIDER_LIST = [
"VOLCENGINE",
"CHAT_ANY_WHERE",
"SILICONFLOW",
"DEEP_SEEK"
]
#env读取保存结束 #env读取保存结束
#============================================== #==============================================
@@ -157,37 +182,9 @@ def format_list_to_str(lst):
res = res[:-1] res = res[:-1]
return "[" + res + "]" return "[" + res + "]"
def format_list_to_str_alias(lst):
"""
将Python列表转换为形如["src2.plugins.chat"]的字符串格式
format_list_to_str(['src2.plugins.chat'])
'["src2.plugins.chat"]'
format_list_to_str([1, "two", 3.0])
'[1, "two", 3.0]'
"""
resarr = []
if len(lst) != 0:
resarr = lst.split(", ")
return resarr
def format_list_to_int(lst):
resarr = []
if len(lst) != 0:
resarr = lst.split(", ")
# print(resarr)
# print(type(resarr))
ans = []
if len(resarr) != 0:
for lsts in resarr:
temp = int(lsts)
ans.append(temp)
# print(ans)
# print(type(ans))
return ans
#env保存函数 #env保存函数
def save_trigger(server_address, server_port, final_result_list,t_mongodb_host,t_mongodb_port,t_mongodb_database_name,t_chatanywhere_base_url,t_chatanywhere_key,t_siliconflow_base_url,t_siliconflow_key,t_deepseek_base_url,t_deepseek_key): def save_trigger(server_address, server_port, final_result_list,t_mongodb_host,t_mongodb_port,t_mongodb_database_name,t_chatanywhere_base_url,t_chatanywhere_key,t_siliconflow_base_url,t_siliconflow_key,t_deepseek_base_url,t_deepseek_key,t_volcengine_base_url,t_volcengine_key):
final_result_lists = format_list_to_str(final_result_list) final_result_lists = format_list_to_str(final_result_list)
env_config_data["env_HOST"] = server_address env_config_data["env_HOST"] = server_address
env_config_data["env_PORT"] = server_port env_config_data["env_PORT"] = server_port
@@ -201,6 +198,8 @@ def save_trigger(server_address, server_port, final_result_list,t_mongodb_host,t
env_config_data["env_SILICONFLOW_KEY"] = t_siliconflow_key env_config_data["env_SILICONFLOW_KEY"] = t_siliconflow_key
env_config_data["env_DEEP_SEEK_BASE_URL"] = t_deepseek_base_url env_config_data["env_DEEP_SEEK_BASE_URL"] = t_deepseek_base_url
env_config_data["env_DEEP_SEEK_KEY"] = t_deepseek_key env_config_data["env_DEEP_SEEK_KEY"] = t_deepseek_key
env_config_data["env_VOLCENGINE_BASE_URL"] = t_volcengine_base_url
env_config_data["env_VOLCENGINE_KEY"] = t_volcengine_key
save_to_env_file(env_config_data) save_to_env_file(env_config_data)
logger.success("配置已保存到 .env.prod 文件中") logger.success("配置已保存到 .env.prod 文件中")
return "配置已保存" return "配置已保存"
@@ -228,7 +227,7 @@ def save_config_to_file(t_config_data):
def save_bot_config(t_qqbot_qq, t_nickname,t_nickname_final_result): def save_bot_config(t_qqbot_qq, t_nickname,t_nickname_final_result):
config_data["bot"]["qq"] = int(t_qqbot_qq) config_data["bot"]["qq"] = int(t_qqbot_qq)
config_data["bot"]["nickname"] = t_nickname config_data["bot"]["nickname"] = t_nickname
config_data["bot"]["alias_names"] = format_list_to_str_alias(t_nickname_final_result) config_data["bot"]["alias_names"] = t_nickname_final_result
save_config_to_file(config_data) save_config_to_file(config_data)
logger.info("Bot配置已保存") logger.info("Bot配置已保存")
return "Bot配置已保存" return "Bot配置已保存"
@@ -298,8 +297,8 @@ def save_message_and_emoji_config(t_min_text_length,
config_data["message"]["response_willing_amplifier"] = t_response_willing_amplifier config_data["message"]["response_willing_amplifier"] = t_response_willing_amplifier
config_data["message"]["response_interested_rate_amplifier"] = t_response_interested_rate_amplifier config_data["message"]["response_interested_rate_amplifier"] = t_response_interested_rate_amplifier
config_data["message"]["down_frequency_rate"] = t_down_frequency_rate config_data["message"]["down_frequency_rate"] = t_down_frequency_rate
config_data["message"]["ban_words"] = format_list_to_str_alias(t_ban_words_final_result) config_data["message"]["ban_words"] =t_ban_words_final_result
config_data["message"]["ban_msgs_regex"] = format_list_to_str_alias(t_ban_msgs_regex_final_result) config_data["message"]["ban_msgs_regex"] = t_ban_msgs_regex_final_result
config_data["emoji"]["check_interval"] = t_check_interval config_data["emoji"]["check_interval"] = t_check_interval
config_data["emoji"]["register_interval"] = t_register_interval config_data["emoji"]["register_interval"] = t_register_interval
config_data["emoji"]["auto_save"] = t_auto_save config_data["emoji"]["auto_save"] = t_auto_save
@@ -358,7 +357,7 @@ def save_memory_mood_config(t_build_memory_interval, t_memory_compress_rate, t_f
config_data["memory"]["forget_memory_interval"] = t_forget_memory_interval config_data["memory"]["forget_memory_interval"] = t_forget_memory_interval
config_data["memory"]["memory_forget_time"] = t_memory_forget_time config_data["memory"]["memory_forget_time"] = t_memory_forget_time
config_data["memory"]["memory_forget_percentage"] = t_memory_forget_percentage config_data["memory"]["memory_forget_percentage"] = t_memory_forget_percentage
config_data["memory"]["memory_ban_words"] = format_list_to_str_alias(t_memory_ban_words_final_result) config_data["memory"]["memory_ban_words"] = t_memory_ban_words_final_result
config_data["mood"]["update_interval"] = t_mood_update_interval config_data["mood"]["update_interval"] = t_mood_update_interval
config_data["mood"]["decay_rate"] = t_mood_decay_rate config_data["mood"]["decay_rate"] = t_mood_decay_rate
config_data["mood"]["intensity_factor"] = t_mood_intensity_factor config_data["mood"]["intensity_factor"] = t_mood_intensity_factor
@@ -366,16 +365,19 @@ def save_memory_mood_config(t_build_memory_interval, t_memory_compress_rate, t_f
logger.info("记忆和心情设置已保存到 bot_config.toml 文件中") logger.info("记忆和心情设置已保存到 bot_config.toml 文件中")
return "记忆和心情设置已保存" return "记忆和心情设置已保存"
def save_other_config(t_keywords_reaction_enabled,t_enable_advance_output, t_enable_kuuki_read, t_enable_debug_output, t_enable_friend_chat, t_chinese_typo_enabled, t_error_rate, t_min_freq, t_tone_error_rate, t_word_replace_rate): def save_other_config(t_keywords_reaction_enabled,t_enable_advance_output, t_enable_kuuki_read, t_enable_debug_output, t_enable_friend_chat, t_chinese_typo_enabled, t_error_rate, t_min_freq, t_tone_error_rate, t_word_replace_rate,t_remote_status):
config_data['keywords_reaction']['enable'] = t_keywords_reaction_enabled config_data['keywords_reaction']['enable'] = t_keywords_reaction_enabled
config_data['others']['enable_advance_output'] = t_enable_advance_output config_data['others']['enable_advance_output'] = t_enable_advance_output
config_data['others']['enable_kuuki_read'] = t_enable_kuuki_read config_data['others']['enable_kuuki_read'] = t_enable_kuuki_read
config_data['others']['enable_debug_output'] = t_enable_debug_output config_data['others']['enable_debug_output'] = t_enable_debug_output
config_data['others']['enable_friend_chat'] = t_enable_friend_chat
config_data["chinese_typo"]["enable"] = t_chinese_typo_enabled config_data["chinese_typo"]["enable"] = t_chinese_typo_enabled
config_data["chinese_typo"]["error_rate"] = t_error_rate config_data["chinese_typo"]["error_rate"] = t_error_rate
config_data["chinese_typo"]["min_freq"] = t_min_freq config_data["chinese_typo"]["min_freq"] = t_min_freq
config_data["chinese_typo"]["tone_error_rate"] = t_tone_error_rate config_data["chinese_typo"]["tone_error_rate"] = t_tone_error_rate
config_data["chinese_typo"]["word_replace_rate"] = t_word_replace_rate config_data["chinese_typo"]["word_replace_rate"] = t_word_replace_rate
if PARSED_CONFIG_VERSION > 0.8:
config_data["remote"]["enable"] = t_remote_status
save_config_to_file(config_data) save_config_to_file(config_data)
logger.info("其他设置已保存到 bot_config.toml 文件中") logger.info("其他设置已保存到 bot_config.toml 文件中")
return "其他设置已保存" return "其他设置已保存"
@@ -383,9 +385,9 @@ def save_other_config(t_keywords_reaction_enabled,t_enable_advance_output, t_ena
def save_group_config(t_talk_allowed_final_result, def save_group_config(t_talk_allowed_final_result,
t_talk_frequency_down_final_result, t_talk_frequency_down_final_result,
t_ban_user_id_final_result,): t_ban_user_id_final_result,):
config_data["groups"]["talk_allowed"] = format_list_to_int(t_talk_allowed_final_result) config_data["groups"]["talk_allowed"] = t_talk_allowed_final_result
config_data["groups"]["talk_frequency_down"] = format_list_to_int(t_talk_frequency_down_final_result) config_data["groups"]["talk_frequency_down"] = t_talk_frequency_down_final_result
config_data["groups"]["ban_user_id"] = format_list_to_int(t_ban_user_id_final_result) config_data["groups"]["ban_user_id"] = t_ban_user_id_final_result
save_config_to_file(config_data) save_config_to_file(config_data)
logger.info("群聊设置已保存到 bot_config.toml 文件中") logger.info("群聊设置已保存到 bot_config.toml 文件中")
return "群聊设置已保存" return "群聊设置已保存"
@@ -393,11 +395,11 @@ def save_group_config(t_talk_allowed_final_result,
with gr.Blocks(title="MaimBot配置文件编辑") as app: with gr.Blocks(title="MaimBot配置文件编辑") as app:
gr.Markdown( gr.Markdown(
value=""" value="""
欢迎使用由墨梓柒MotricSeven编写的MaimBot配置文件编辑器\n ### 欢迎使用由墨梓柒MotricSeven编写的MaimBot配置文件编辑器\n
""" """
) )
gr.Markdown( gr.Markdown(
value="配置文件版本:" + config_data["inner"]["version"] value="### 配置文件版本:" + config_data["inner"]["version"]
) )
with gr.Tabs(): with gr.Tabs():
with gr.TabItem("0-环境设置"): with gr.TabItem("0-环境设置"):
@@ -539,11 +541,23 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
interactive=True interactive=True
) )
with gr.Row(): with gr.Row():
save_env_btn = gr.Button("保存环境配置") volcengine_base_url = gr.Textbox(
label="VolcEngine的BaseURL",
value=env_config_data["env_VOLCENGINE_BASE_URL"],
interactive=True
)
with gr.Row():
volcengine_key = gr.Textbox(
label="VolcEngine的key",
value=env_config_data["env_VOLCENGINE_KEY"],
interactive=True
)
with gr.Row():
save_env_btn = gr.Button("保存环境配置",variant="primary")
with gr.Row(): with gr.Row():
save_env_btn.click( save_env_btn.click(
save_trigger, save_trigger,
inputs=[server_address,server_port,final_result,mongodb_host,mongodb_port,mongodb_database_name,chatanywhere_base_url,chatanywhere_key,siliconflow_base_url,siliconflow_key,deepseek_base_url,deepseek_key], inputs=[server_address,server_port,final_result,mongodb_host,mongodb_port,mongodb_database_name,chatanywhere_base_url,chatanywhere_key,siliconflow_base_url,siliconflow_key,deepseek_base_url,deepseek_key,volcengine_base_url,volcengine_key],
outputs=[gr.Textbox( outputs=[gr.Textbox(
label="保存结果", label="保存结果",
interactive=False interactive=False
@@ -608,7 +622,7 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
elem_classes="save_bot_btn" elem_classes="save_bot_btn"
).click( ).click(
save_bot_config, save_bot_config,
inputs=[qqbot_qq, nickname,nickname_final_result], inputs=[qqbot_qq, nickname,nickname_list_state],
outputs=[gr.Textbox( outputs=[gr.Textbox(
label="保存Bot结果" label="保存Bot结果"
)] )]
@@ -658,18 +672,19 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
interactive=True interactive=True
) )
with gr.Row(): with gr.Row():
gr.Button( personal_save_btn = gr.Button(
"保存人格配置", "保存人格配置",
variant="primary", variant="primary",
elem_id="save_personality_btn", elem_id="save_personality_btn",
elem_classes="save_personality_btn" elem_classes="save_personality_btn"
).click(
save_personality_config,
inputs=[personality_1, personality_2, personality_3, prompt_schedule],
outputs=[gr.Textbox(
label="保存人格结果"
)]
) )
with gr.Row():
personal_save_message = gr.Textbox(label="保存人格结果")
personal_save_btn.click(
save_personality_config,
inputs=[personality_1, personality_2, personality_3, prompt_schedule],
outputs=[personal_save_message]
)
with gr.TabItem("3-消息&表情包设置"): with gr.TabItem("3-消息&表情包设置"):
with gr.Row(): with gr.Row():
with gr.Column(scale=3): with gr.Column(scale=3):
@@ -783,33 +798,36 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
with gr.Row(): with gr.Row():
check_prompt = gr.Textbox(value=config_data['emoji']['check_prompt'], label="表情包过滤要求") check_prompt = gr.Textbox(value=config_data['emoji']['check_prompt'], label="表情包过滤要求")
with gr.Row(): with gr.Row():
gr.Button( emoji_save_btn = gr.Button(
"保存消息&表情包设置", "保存消息&表情包设置",
variant="primary", variant="primary",
elem_id="save_personality_btn", elem_id="save_personality_btn",
elem_classes="save_personality_btn" elem_classes="save_personality_btn"
).click(
save_message_and_emoji_config,
inputs=[
min_text_length,
max_context_size,
emoji_chance,
thinking_timeout,
response_willing_amplifier,
response_interested_rate_amplifier,
down_frequency_rate,
ban_words_final_result,
ban_msgs_regex_final_result,
check_interval,
register_interval,
auto_save,
enable_check,
check_prompt
],
outputs=[gr.Textbox(
label="消息&表情包设置保存结果"
)]
) )
with gr.Row():
emoji_save_message = gr.Textbox(
label="消息&表情包设置保存结果"
)
emoji_save_btn.click(
save_message_and_emoji_config,
inputs=[
min_text_length,
max_context_size,
emoji_chance,
thinking_timeout,
response_willing_amplifier,
response_interested_rate_amplifier,
down_frequency_rate,
ban_words_list_state,
ban_msgs_regex_list_state,
check_interval,
register_interval,
auto_save,
enable_check,
check_prompt
],
outputs=[emoji_save_message]
)
with gr.TabItem("4-回复&模型设置"): with gr.TabItem("4-回复&模型设置"):
with gr.Row(): with gr.Row():
with gr.Column(scale=3): with gr.Column(scale=3):
@@ -841,12 +859,20 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
gr.Markdown( gr.Markdown(
"""### 模型设置""" """### 模型设置"""
) )
with gr.Row():
gr.Markdown(
"""### 注意\n
如果你是用的是火山引擎的API建议查看[这篇文档](https://zxmucttizt8.feishu.cn/wiki/MQj7wp6dki6X8rkplApc2v6Enkd)中的修改火山API部分\n
因为修改至火山API涉及到修改源码部分由于自己修改源码造成的问题MaiMBot官方并不因此负责\n
感谢理解感谢你使用MaiMBot
"""
)
with gr.Tabs(): with gr.Tabs():
with gr.TabItem("1-主要回复模型"): with gr.TabItem("1-主要回复模型"):
with gr.Row(): with gr.Row():
model1_name = gr.Textbox(value=config_data['model']['llm_reasoning']['name'], label="模型1的名称") model1_name = gr.Textbox(value=config_data['model']['llm_reasoning']['name'], label="模型1的名称")
with gr.Row(): with gr.Row():
model1_provider = gr.Dropdown(choices=["SILICONFLOW","DEEP_SEEK", "CHAT_ANY_WHERE"], value=config_data['model']['llm_reasoning']['provider'], label="模型1主要回复模型提供商") model1_provider = gr.Dropdown(choices=MODEL_PROVIDER_LIST, value=config_data['model']['llm_reasoning']['provider'], label="模型1主要回复模型提供商")
with gr.Row(): with gr.Row():
model1_pri_in = gr.Number(value=config_data['model']['llm_reasoning']['pri_in'], label="模型1主要回复模型的输入价格非必填可以记录消耗") model1_pri_in = gr.Number(value=config_data['model']['llm_reasoning']['pri_in'], label="模型1主要回复模型的输入价格非必填可以记录消耗")
with gr.Row(): with gr.Row():
@@ -855,12 +881,12 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
with gr.Row(): with gr.Row():
model2_name = gr.Textbox(value=config_data['model']['llm_normal']['name'], label="模型2的名称") model2_name = gr.Textbox(value=config_data['model']['llm_normal']['name'], label="模型2的名称")
with gr.Row(): with gr.Row():
model2_provider = gr.Dropdown(choices=["SILICONFLOW","DEEP_SEEK", "CHAT_ANY_WHERE"], value=config_data['model']['llm_normal']['provider'], label="模型2提供商") model2_provider = gr.Dropdown(choices=MODEL_PROVIDER_LIST, value=config_data['model']['llm_normal']['provider'], label="模型2提供商")
with gr.TabItem("3-次要模型"): with gr.TabItem("3-次要模型"):
with gr.Row(): with gr.Row():
model3_name = gr.Textbox(value=config_data['model']['llm_reasoning_minor']['name'], label="模型3的名称") model3_name = gr.Textbox(value=config_data['model']['llm_reasoning_minor']['name'], label="模型3的名称")
with gr.Row(): with gr.Row():
model3_provider = gr.Dropdown(choices=["SILICONFLOW","DEEP_SEEK", "CHAT_ANY_WHERE"], value=config_data['model']['llm_reasoning_minor']['provider'], label="模型3提供商") model3_provider = gr.Dropdown(choices=MODEL_PROVIDER_LIST, value=config_data['model']['llm_reasoning_minor']['provider'], label="模型3提供商")
with gr.TabItem("4-情感&主题模型"): with gr.TabItem("4-情感&主题模型"):
with gr.Row(): with gr.Row():
gr.Markdown( gr.Markdown(
@@ -869,7 +895,7 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
with gr.Row(): with gr.Row():
emotion_model_name = gr.Textbox(value=config_data['model']['llm_emotion_judge']['name'], label="情感模型名称") emotion_model_name = gr.Textbox(value=config_data['model']['llm_emotion_judge']['name'], label="情感模型名称")
with gr.Row(): with gr.Row():
emotion_model_provider = gr.Dropdown(choices=["SILICONFLOW","DEEP_SEEK", "CHAT_ANY_WHERE"], value=config_data['model']['llm_emotion_judge']['provider'], label="情感模型提供商") emotion_model_provider = gr.Dropdown(choices=MODEL_PROVIDER_LIST, value=config_data['model']['llm_emotion_judge']['provider'], label="情感模型提供商")
with gr.Row(): with gr.Row():
gr.Markdown( gr.Markdown(
"""### 主题模型设置""" """### 主题模型设置"""
@@ -877,11 +903,11 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
with gr.Row(): with gr.Row():
topic_judge_model_name = gr.Textbox(value=config_data['model']['llm_topic_judge']['name'], label="主题判断模型名称") topic_judge_model_name = gr.Textbox(value=config_data['model']['llm_topic_judge']['name'], label="主题判断模型名称")
with gr.Row(): with gr.Row():
topic_judge_model_provider = gr.Dropdown(choices=["SILICONFLOW","DEEP_SEEK", "CHAT_ANY_WHERE"], value=config_data['model']['llm_topic_judge']['provider'], label="主题判断模型提供商") topic_judge_model_provider = gr.Dropdown(choices=MODEL_PROVIDER_LIST, value=config_data['model']['llm_topic_judge']['provider'], label="主题判断模型提供商")
with gr.Row(): with gr.Row():
summary_by_topic_model_name = gr.Textbox(value=config_data['model']['llm_summary_by_topic']['name'], label="主题总结模型名称") summary_by_topic_model_name = gr.Textbox(value=config_data['model']['llm_summary_by_topic']['name'], label="主题总结模型名称")
with gr.Row(): with gr.Row():
summary_by_topic_model_provider = gr.Dropdown(choices=["SILICONFLOW","DEEP_SEEK", "CHAT_ANY_WHERE"], value=config_data['model']['llm_summary_by_topic']['provider'], label="主题总结模型提供商") summary_by_topic_model_provider = gr.Dropdown(choices=MODEL_PROVIDER_LIST, value=config_data['model']['llm_summary_by_topic']['provider'], label="主题总结模型提供商")
with gr.TabItem("5-识图模型"): with gr.TabItem("5-识图模型"):
with gr.Row(): with gr.Row():
gr.Markdown( gr.Markdown(
@@ -890,9 +916,9 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
with gr.Row(): with gr.Row():
vlm_model_name = gr.Textbox(value=config_data['model']['vlm']['name'], label="识图模型名称") vlm_model_name = gr.Textbox(value=config_data['model']['vlm']['name'], label="识图模型名称")
with gr.Row(): with gr.Row():
vlm_model_provider = gr.Dropdown(choices=["SILICONFLOW","DEEP_SEEK", "CHAT_ANY_WHERE"], value=config_data['model']['vlm']['provider'], label="识图模型提供商") vlm_model_provider = gr.Dropdown(choices=MODEL_PROVIDER_LIST, value=config_data['model']['vlm']['provider'], label="识图模型提供商")
with gr.Row(): with gr.Row():
save_model_btn = gr.Button("保存回复&模型设置") save_model_btn = gr.Button("保存回复&模型设置",variant="primary", elem_id="save_model_btn")
with gr.Row(): with gr.Row():
save_btn_message = gr.Textbox() save_btn_message = gr.Textbox()
save_model_btn.click( save_model_btn.click(
@@ -961,13 +987,13 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
with gr.Row(): with gr.Row():
mood_intensity_factor = gr.Number(value=config_data['mood']['mood_intensity_factor'], label="心情强度因子") mood_intensity_factor = gr.Number(value=config_data['mood']['mood_intensity_factor'], label="心情强度因子")
with gr.Row(): with gr.Row():
save_memory_mood_btn = gr.Button("保存 [Memory] 配置") save_memory_mood_btn = gr.Button("保存记忆&心情设置",variant="primary")
with gr.Row(): with gr.Row():
save_memory_mood_message = gr.Textbox() save_memory_mood_message = gr.Textbox()
with gr.Row(): with gr.Row():
save_memory_mood_btn.click( save_memory_mood_btn.click(
save_memory_mood_config, save_memory_mood_config,
inputs=[build_memory_interval, memory_compress_rate, forget_memory_interval, memory_forget_time, memory_forget_percentage, memory_ban_words_final_result, mood_update_interval, mood_decay_rate, mood_intensity_factor], inputs=[build_memory_interval, memory_compress_rate, forget_memory_interval, memory_forget_time, memory_forget_percentage, memory_ban_words_list_state, mood_update_interval, mood_decay_rate, mood_intensity_factor],
outputs=[save_memory_mood_message] outputs=[save_memory_mood_message]
) )
with gr.TabItem("6-群组设置"): with gr.TabItem("6-群组设置"):
@@ -1093,16 +1119,16 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
outputs=[ban_user_id_list_state, ban_user_id_list_display, ban_user_id_item_to_delete, ban_user_id_final_result] outputs=[ban_user_id_list_state, ban_user_id_list_display, ban_user_id_item_to_delete, ban_user_id_final_result]
) )
with gr.Row(): with gr.Row():
save_group_btn = gr.Button("保存群组设置") save_group_btn = gr.Button("保存群组设置",variant="primary")
with gr.Row(): with gr.Row():
save_group_btn_message = gr.Textbox() save_group_btn_message = gr.Textbox()
with gr.Row(): with gr.Row():
save_group_btn.click( save_group_btn.click(
save_group_config, save_group_config,
inputs=[ inputs=[
talk_allowed_final_result, talk_allowed_list_state,
talk_frequency_down_final_result, talk_frequency_down_list_state,
ban_user_id_final_result, ban_user_id_list_state,
], ],
outputs=[save_group_btn_message] outputs=[save_group_btn_message]
) )
@@ -1123,6 +1149,17 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
enable_debug_output = gr.Checkbox(value=config_data['others']['enable_debug_output'], label="是否开启调试输出") enable_debug_output = gr.Checkbox(value=config_data['others']['enable_debug_output'], label="是否开启调试输出")
with gr.Row(): with gr.Row():
enable_friend_chat = gr.Checkbox(value=config_data['others']['enable_friend_chat'], label="是否开启好友聊天") enable_friend_chat = gr.Checkbox(value=config_data['others']['enable_friend_chat'], label="是否开启好友聊天")
if PARSED_CONFIG_VERSION > 0.8:
with gr.Row():
gr.Markdown(
"""### 远程统计设置\n
测试功能,发送统计信息,主要是看全球有多少只麦麦
"""
)
with gr.Row():
remote_status = gr.Checkbox(value=config_data['remote']['enable'], label="是否开启麦麦在线全球统计")
with gr.Row(): with gr.Row():
gr.Markdown( gr.Markdown(
"""### 中文错别字设置""" """### 中文错别字设置"""
@@ -1138,13 +1175,15 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app:
with gr.Row(): with gr.Row():
word_replace_rate = gr.Slider(minimum=0, maximum=1, step=0.001, value=config_data['chinese_typo']['word_replace_rate'], label="整词替换概率") word_replace_rate = gr.Slider(minimum=0, maximum=1, step=0.001, value=config_data['chinese_typo']['word_replace_rate'], label="整词替换概率")
with gr.Row(): with gr.Row():
save_other_config_btn = gr.Button("保存其他配置") save_other_config_btn = gr.Button("保存其他配置",variant="primary")
with gr.Row(): with gr.Row():
save_other_config_message = gr.Textbox() save_other_config_message = gr.Textbox()
with gr.Row(): with gr.Row():
if PARSED_CONFIG_VERSION <= 0.8:
remote_status = gr.Checkbox(value=False,visible=False)
save_other_config_btn.click( save_other_config_btn.click(
save_other_config, save_other_config,
inputs=[keywords_reaction_enabled,enable_advance_output, enable_kuuki_read, enable_debug_output, enable_friend_chat, chinese_typo_enabled, error_rate, min_freq, tone_error_rate, word_replace_rate], inputs=[keywords_reaction_enabled,enable_advance_output, enable_kuuki_read, enable_debug_output, enable_friend_chat, chinese_typo_enabled, error_rate, min_freq, tone_error_rate, word_replace_rate,remote_status],
outputs=[save_other_config_message] outputs=[save_other_config_message]
) )
app.queue().launch(#concurrency_count=511, max_size=1022 app.queue().launch(#concurrency_count=511, max_size=1022

28
webui_conda.bat Normal file
View File

@@ -0,0 +1,28 @@
@echo on
echo Starting script...
echo Activating conda environment: maimbot
call conda activate maimbot
if errorlevel 1 (
echo Failed to activate conda environment
pause
exit /b 1
)
echo Conda environment activated successfully
echo Changing directory to C:\GitHub\MaiMBot
cd /d C:\GitHub\MaiMBot
if errorlevel 1 (
echo Failed to change directory
pause
exit /b 1
)
echo Current directory is:
cd
python webui.py
if errorlevel 1 (
echo Command failed with error code %errorlevel%
pause
exit /b 1
)
echo Script completed successfully
pause