合并
This commit is contained in:
17
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
17
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -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
8
.gitignore
vendored
@@ -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
69
EULA.md
Normal 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许可产生纠纷,以许可证官方解释为准。
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
@@ -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) - 通俗易懂的配置教程,适合初次使用的猫娘
|
||||||
|
|||||||
41
bot.py
41
bot.py
@@ -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,13 +165,35 @@ 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 环境变量设定程序工作的时区
|
||||||
# 仅保证行为一致,不依赖 localtime(),实际对生产环境几乎没有作用
|
# 仅保证行为一致,不依赖 localtime(),实际对生产环境几乎没有作用
|
||||||
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()
|
||||||
|
|||||||
@@ -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
198
src/common/logger.py
Normal 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
347
src/gui/logger_gui.py
Normal 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()
|
||||||
@@ -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时间戳
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
"""聊天流对象,存储一个完整的聊天上下文"""
|
"""聊天流对象,存储一个完整的聊天上下文"""
|
||||||
|
|||||||
@@ -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")
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
await message_sender.send_message(message_earliest)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
print(
|
|
||||||
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):
|
||||||
|
|||||||
@@ -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系统")
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 访问")
|
||||||
@@ -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__), "../../.."))
|
||||||
|
|||||||
@@ -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):
|
||||||
@@ -35,9 +30,9 @@ class Memory_graph:
|
|||||||
# 避免自连接
|
# 避免自连接
|
||||||
if concept1 == concept2:
|
if concept1 == concept2:
|
||||||
return
|
return
|
||||||
|
|
||||||
current_time = datetime.datetime.now().timestamp()
|
current_time = datetime.datetime.now().timestamp()
|
||||||
|
|
||||||
# 如果边已存在,增加 strength
|
# 如果边已存在,增加 strength
|
||||||
if self.G.has_edge(concept1, concept2):
|
if self.G.has_edge(concept1, concept2):
|
||||||
self.G[concept1][concept2]['strength'] = self.G[concept1][concept2].get('strength', 1) + 1
|
self.G[concept1][concept2]['strength'] = self.G[concept1][concept2].get('strength', 1) + 1
|
||||||
@@ -45,14 +40,14 @@ class Memory_graph:
|
|||||||
self.G[concept1][concept2]['last_modified'] = current_time
|
self.G[concept1][concept2]['last_modified'] = current_time
|
||||||
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()
|
||||||
|
|
||||||
if concept in self.G:
|
if concept in self.G:
|
||||||
if 'memory_items' in self.G.nodes[concept]:
|
if 'memory_items' in self.G.nodes[concept]:
|
||||||
if not isinstance(self.G.nodes[concept]['memory_items'], list):
|
if not isinstance(self.G.nodes[concept]['memory_items'], list):
|
||||||
@@ -68,10 +63,10 @@ class Memory_graph:
|
|||||||
self.G.nodes[concept]['last_modified'] = current_time
|
self.G.nodes[concept]['last_modified'] = current_time
|
||||||
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,12 +205,13 @@ 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
|
||||||
# 三次尝试均失败
|
# 三次尝试均失败
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_memory_sample(self, chat_size=20, time_frequency: dict = {'near': 2, 'mid': 4, 'far': 3}):
|
def get_memory_sample(self, chat_size=20, time_frequency: dict = {'near': 2, 'mid': 4, 'far': 3}):
|
||||||
"""获取记忆样本
|
"""获取记忆样本
|
||||||
|
|
||||||
@@ -225,7 +221,7 @@ class Hippocampus:
|
|||||||
# 硬编码:每条消息最大记忆次数
|
# 硬编码:每条消息最大记忆次数
|
||||||
# 如有需求可写入global_config
|
# 如有需求可写入global_config
|
||||||
max_memorized_time_per_msg = 3
|
max_memorized_time_per_msg = 3
|
||||||
|
|
||||||
current_timestamp = datetime.datetime.now().timestamp()
|
current_timestamp = datetime.datetime.now().timestamp()
|
||||||
chat_samples = []
|
chat_samples = []
|
||||||
|
|
||||||
@@ -324,20 +320,20 @@ class Hippocampus:
|
|||||||
# 为每个话题查找相似的已存在主题
|
# 为每个话题查找相似的已存在主题
|
||||||
existing_topics = list(self.memory_graph.G.nodes())
|
existing_topics = list(self.memory_graph.G.nodes())
|
||||||
similar_topics = []
|
similar_topics = []
|
||||||
|
|
||||||
for existing_topic in existing_topics:
|
for existing_topic in existing_topics:
|
||||||
topic_words = set(jieba.cut(topic))
|
topic_words = set(jieba.cut(topic))
|
||||||
existing_words = set(jieba.cut(existing_topic))
|
existing_words = set(jieba.cut(existing_topic))
|
||||||
|
|
||||||
all_words = topic_words | existing_words
|
all_words = topic_words | existing_words
|
||||||
v1 = [1 if word in topic_words else 0 for word in all_words]
|
v1 = [1 if word in topic_words else 0 for word in all_words]
|
||||||
v2 = [1 if word in existing_words else 0 for word in all_words]
|
v2 = [1 if word in existing_words else 0 for word in all_words]
|
||||||
|
|
||||||
similarity = cosine_similarity(v1, v2)
|
similarity = cosine_similarity(v1, v2)
|
||||||
|
|
||||||
if similarity >= 0.6:
|
if similarity >= 0.6:
|
||||||
similar_topics.append((existing_topic, similarity))
|
similar_topics.append((existing_topic, similarity))
|
||||||
|
|
||||||
similar_topics.sort(key=lambda x: x[1], reverse=True)
|
similar_topics.sort(key=lambda x: x[1], reverse=True)
|
||||||
similar_topics = similar_topics[:5]
|
similar_topics = similar_topics[:5]
|
||||||
similar_topics_dict[topic] = similar_topics
|
similar_topics_dict[topic] = similar_topics
|
||||||
@@ -358,7 +354,7 @@ class Hippocampus:
|
|||||||
async def operation_build_memory(self, chat_size=20):
|
async def operation_build_memory(self, chat_size=20):
|
||||||
time_frequency = {'near': 1, 'mid': 4, 'far': 4}
|
time_frequency = {'near': 1, 'mid': 4, 'far': 4}
|
||||||
memory_samples = self.get_memory_sample(chat_size, time_frequency)
|
memory_samples = self.get_memory_sample(chat_size, time_frequency)
|
||||||
|
|
||||||
for i, messages in enumerate(memory_samples, 1):
|
for i, messages in enumerate(memory_samples, 1):
|
||||||
all_topics = []
|
all_topics = []
|
||||||
# 加载进度可视化
|
# 加载进度可视化
|
||||||
@@ -371,14 +367,14 @@ class Hippocampus:
|
|||||||
compress_rate = global_config.memory_compress_rate
|
compress_rate = global_config.memory_compress_rate
|
||||||
compressed_memory, similar_topics_dict = await self.memory_compress(messages, compress_rate)
|
compressed_memory, similar_topics_dict = await self.memory_compress(messages, compress_rate)
|
||||||
logger.info(f"压缩后记忆数量: {len(compressed_memory)},似曾相识的话题: {len(similar_topics_dict)}")
|
logger.info(f"压缩后记忆数量: {len(compressed_memory)},似曾相识的话题: {len(similar_topics_dict)}")
|
||||||
|
|
||||||
current_time = datetime.datetime.now().timestamp()
|
current_time = datetime.datetime.now().timestamp()
|
||||||
|
|
||||||
for topic, memory in compressed_memory:
|
for topic, memory in compressed_memory:
|
||||||
logger.info(f"添加节点: {topic}")
|
logger.info(f"添加节点: {topic}")
|
||||||
self.memory_graph.add_dot(topic, memory)
|
self.memory_graph.add_dot(topic, memory)
|
||||||
all_topics.append(topic)
|
all_topics.append(topic)
|
||||||
|
|
||||||
# 连接相似的已存在主题
|
# 连接相似的已存在主题
|
||||||
if topic in similar_topics_dict:
|
if topic in similar_topics_dict:
|
||||||
similar_topics = similar_topics_dict[topic]
|
similar_topics = similar_topics_dict[topic]
|
||||||
@@ -386,11 +382,11 @@ class Hippocampus:
|
|||||||
if topic != similar_topic:
|
if topic != similar_topic:
|
||||||
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)):
|
||||||
for j in range(i + 1, len(all_topics)):
|
for j in range(i + 1, len(all_topics)):
|
||||||
@@ -416,7 +412,7 @@ class Hippocampus:
|
|||||||
|
|
||||||
# 计算内存中节点的特征值
|
# 计算内存中节点的特征值
|
||||||
memory_hash = self.calculate_node_hash(concept, memory_items)
|
memory_hash = self.calculate_node_hash(concept, memory_items)
|
||||||
|
|
||||||
# 获取时间信息
|
# 获取时间信息
|
||||||
created_time = data.get('created_time', datetime.datetime.now().timestamp())
|
created_time = data.get('created_time', datetime.datetime.now().timestamp())
|
||||||
last_modified = data.get('last_modified', datetime.datetime.now().timestamp())
|
last_modified = data.get('last_modified', datetime.datetime.now().timestamp())
|
||||||
@@ -466,7 +462,7 @@ class Hippocampus:
|
|||||||
edge_hash = self.calculate_edge_hash(source, target)
|
edge_hash = self.calculate_edge_hash(source, target)
|
||||||
edge_key = (source, target)
|
edge_key = (source, target)
|
||||||
strength = data.get('strength', 1)
|
strength = data.get('strength', 1)
|
||||||
|
|
||||||
# 获取边的时间信息
|
# 获取边的时间信息
|
||||||
created_time = data.get('created_time', datetime.datetime.now().timestamp())
|
created_time = data.get('created_time', datetime.datetime.now().timestamp())
|
||||||
last_modified = data.get('last_modified', datetime.datetime.now().timestamp())
|
last_modified = data.get('last_modified', datetime.datetime.now().timestamp())
|
||||||
@@ -499,7 +495,7 @@ class Hippocampus:
|
|||||||
"""从数据库同步数据到内存中的图结构"""
|
"""从数据库同步数据到内存中的图结构"""
|
||||||
current_time = datetime.datetime.now().timestamp()
|
current_time = datetime.datetime.now().timestamp()
|
||||||
need_update = False
|
need_update = False
|
||||||
|
|
||||||
# 清空当前图
|
# 清空当前图
|
||||||
self.memory_graph.G.clear()
|
self.memory_graph.G.clear()
|
||||||
|
|
||||||
@@ -510,7 +506,7 @@ class Hippocampus:
|
|||||||
memory_items = node.get('memory_items', [])
|
memory_items = node.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 []
|
||||||
|
|
||||||
# 检查时间字段是否存在
|
# 检查时间字段是否存在
|
||||||
if 'created_time' not in node or 'last_modified' not in node:
|
if 'created_time' not in node or 'last_modified' not in node:
|
||||||
need_update = True
|
need_update = True
|
||||||
@@ -520,22 +516,22 @@ class Hippocampus:
|
|||||||
update_data['created_time'] = current_time
|
update_data['created_time'] = current_time
|
||||||
if 'last_modified' not in node:
|
if 'last_modified' not in node:
|
||||||
update_data['last_modified'] = current_time
|
update_data['last_modified'] = current_time
|
||||||
|
|
||||||
db.graph_data.nodes.update_one(
|
db.graph_data.nodes.update_one(
|
||||||
{'concept': concept},
|
{'concept': concept},
|
||||||
{'$set': update_data}
|
{'$set': update_data}
|
||||||
)
|
)
|
||||||
logger.info(f"[时间更新] 节点 {concept} 添加缺失的时间字段")
|
logger.info(f"[时间更新] 节点 {concept} 添加缺失的时间字段")
|
||||||
|
|
||||||
# 获取时间信息(如果不存在则使用当前时间)
|
# 获取时间信息(如果不存在则使用当前时间)
|
||||||
created_time = node.get('created_time', current_time)
|
created_time = node.get('created_time', current_time)
|
||||||
last_modified = node.get('last_modified', current_time)
|
last_modified = node.get('last_modified', current_time)
|
||||||
|
|
||||||
# 添加节点到图中
|
# 添加节点到图中
|
||||||
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())
|
||||||
@@ -543,7 +539,7 @@ class Hippocampus:
|
|||||||
source = edge['source']
|
source = edge['source']
|
||||||
target = edge['target']
|
target = edge['target']
|
||||||
strength = edge.get('strength', 1)
|
strength = edge.get('strength', 1)
|
||||||
|
|
||||||
# 检查时间字段是否存在
|
# 检查时间字段是否存在
|
||||||
if 'created_time' not in edge or 'last_modified' not in edge:
|
if 'created_time' not in edge or 'last_modified' not in edge:
|
||||||
need_update = True
|
need_update = True
|
||||||
@@ -553,24 +549,24 @@ class Hippocampus:
|
|||||||
update_data['created_time'] = current_time
|
update_data['created_time'] = current_time
|
||||||
if 'last_modified' not in edge:
|
if 'last_modified' not in edge:
|
||||||
update_data['last_modified'] = current_time
|
update_data['last_modified'] = current_time
|
||||||
|
|
||||||
db.graph_data.edges.update_one(
|
db.graph_data.edges.update_one(
|
||||||
{'source': source, 'target': target},
|
{'source': source, 'target': target},
|
||||||
{'$set': update_data}
|
{'$set': update_data}
|
||||||
)
|
)
|
||||||
logger.info(f"[时间更新] 边 {source} - {target} 添加缺失的时间字段")
|
logger.info(f"[时间更新] 边 {source} - {target} 添加缺失的时间字段")
|
||||||
|
|
||||||
# 获取时间信息(如果不存在则使用当前时间)
|
# 获取时间信息(如果不存在则使用当前时间)
|
||||||
created_time = edge.get('created_time', current_time)
|
created_time = edge.get('created_time', current_time)
|
||||||
last_modified = edge.get('last_modified', current_time)
|
last_modified = edge.get('last_modified', current_time)
|
||||||
|
|
||||||
# 只有当源节点和目标节点都存在时才添加边
|
# 只有当源节点和目标节点都存在时才添加边
|
||||||
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("[数据库] 已为缺失的时间字段进行补充")
|
||||||
|
|
||||||
@@ -578,44 +574,44 @@ class Hippocampus:
|
|||||||
"""随机选择图中一定比例的节点和边进行检查,根据时间条件决定是否遗忘"""
|
"""随机选择图中一定比例的节点和边进行检查,根据时间条件决定是否遗忘"""
|
||||||
# 检查数据库是否为空
|
# 检查数据库是否为空
|
||||||
# logger.remove()
|
# logger.remove()
|
||||||
|
|
||||||
logger.info(f"[遗忘] 开始检查数据库... 当前Logger信息:")
|
logger.info(f"[遗忘] 开始检查数据库... 当前Logger信息:")
|
||||||
# logger.info(f"- Logger名称: {logger.name}")
|
# logger.info(f"- Logger名称: {logger.name}")
|
||||||
logger.info(f"- Logger等级: {logger.level}")
|
logger.info(f"- Logger等级: {logger.level}")
|
||||||
# logger.info(f"- Logger处理器: {[handler.__class__.__name__ for handler in logger.handlers]}")
|
# logger.info(f"- Logger处理器: {[handler.__class__.__name__ for handler in logger.handlers]}")
|
||||||
|
|
||||||
# logger2 = setup_logger(LogModule.MEMORY)
|
# logger2 = setup_logger(LogModule.MEMORY)
|
||||||
# logger2.info(f"[遗忘] 开始检查数据库... 当前Logger信息:")
|
# logger2.info(f"[遗忘] 开始检查数据库... 当前Logger信息:")
|
||||||
# logger.info(f"[遗忘] 开始检查数据库... 当前Logger信息:")
|
# logger.info(f"[遗忘] 开始检查数据库... 当前Logger信息:")
|
||||||
|
|
||||||
all_nodes = list(self.memory_graph.G.nodes())
|
all_nodes = list(self.memory_graph.G.nodes())
|
||||||
all_edges = list(self.memory_graph.G.edges())
|
all_edges = list(self.memory_graph.G.edges())
|
||||||
|
|
||||||
if not all_nodes and not all_edges:
|
if not all_nodes and not all_edges:
|
||||||
logger.info("[遗忘] 记忆图为空,无需进行遗忘操作")
|
logger.info("[遗忘] 记忆图为空,无需进行遗忘操作")
|
||||||
return
|
return
|
||||||
|
|
||||||
check_nodes_count = max(1, int(len(all_nodes) * percentage))
|
check_nodes_count = max(1, int(len(all_nodes) * percentage))
|
||||||
check_edges_count = max(1, int(len(all_edges) * percentage))
|
check_edges_count = max(1, int(len(all_edges) * percentage))
|
||||||
|
|
||||||
nodes_to_check = random.sample(all_nodes, check_nodes_count)
|
nodes_to_check = random.sample(all_nodes, check_nodes_count)
|
||||||
edges_to_check = random.sample(all_edges, check_edges_count)
|
edges_to_check = random.sample(all_edges, check_edges_count)
|
||||||
|
|
||||||
edge_changes = {'weakened': 0, 'removed': 0}
|
edge_changes = {'weakened': 0, 'removed': 0}
|
||||||
node_changes = {'reduced': 0, 'removed': 0}
|
node_changes = {'reduced': 0, 'removed': 0}
|
||||||
|
|
||||||
current_time = datetime.datetime.now().timestamp()
|
current_time = datetime.datetime.now().timestamp()
|
||||||
|
|
||||||
# 检查并遗忘连接
|
# 检查并遗忘连接
|
||||||
logger.info("[遗忘] 开始检查连接...")
|
logger.info("[遗忘] 开始检查连接...")
|
||||||
for source, target in edges_to_check:
|
for source, target in edges_to_check:
|
||||||
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
|
||||||
|
|
||||||
if new_strength <= 0:
|
if new_strength <= 0:
|
||||||
self.memory_graph.G.remove_edge(source, target)
|
self.memory_graph.G.remove_edge(source, target)
|
||||||
edge_changes['removed'] += 1
|
edge_changes['removed'] += 1
|
||||||
@@ -625,23 +621,23 @@ class Hippocampus:
|
|||||||
edge_data['last_modified'] = current_time
|
edge_data['last_modified'] = current_time
|
||||||
edge_changes['weakened'] += 1
|
edge_changes['weakened'] += 1
|
||||||
logger.info(f"[遗忘] 连接减弱: {source} -> {target} (强度: {current_strength} -> {new_strength})")
|
logger.info(f"[遗忘] 连接减弱: {source} -> {target} (强度: {current_strength} -> {new_strength})")
|
||||||
|
|
||||||
# 检查并遗忘话题
|
# 检查并遗忘话题
|
||||||
logger.info("[遗忘] 开始检查节点...")
|
logger.info("[遗忘] 开始检查节点...")
|
||||||
for node in nodes_to_check:
|
for node in nodes_to_check:
|
||||||
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 []
|
||||||
|
|
||||||
if memory_items:
|
if memory_items:
|
||||||
current_count = len(memory_items)
|
current_count = len(memory_items)
|
||||||
removed_item = random.choice(memory_items)
|
removed_item = random.choice(memory_items)
|
||||||
memory_items.remove(removed_item)
|
memory_items.remove(removed_item)
|
||||||
|
|
||||||
if memory_items:
|
if memory_items:
|
||||||
self.memory_graph.G.nodes[node]['memory_items'] = memory_items
|
self.memory_graph.G.nodes[node]['memory_items'] = memory_items
|
||||||
self.memory_graph.G.nodes[node]['last_modified'] = current_time
|
self.memory_graph.G.nodes[node]['last_modified'] = current_time
|
||||||
@@ -651,7 +647,7 @@ class Hippocampus:
|
|||||||
self.memory_graph.G.remove_node(node)
|
self.memory_graph.G.remove_node(node)
|
||||||
node_changes['removed'] += 1
|
node_changes['removed'] += 1
|
||||||
logger.info(f"[遗忘] 节点移除: {node}")
|
logger.info(f"[遗忘] 节点移除: {node}")
|
||||||
|
|
||||||
if any(count > 0 for count in edge_changes.values()) or any(count > 0 for count in node_changes.values()):
|
if any(count > 0 for count in edge_changes.values()) or any(count > 0 for count in node_changes.values()):
|
||||||
self.sync_memory_to_db()
|
self.sync_memory_to_db()
|
||||||
logger.info("[遗忘] 统计信息:")
|
logger.info("[遗忘] 统计信息:")
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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} 加载环境变量")
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
'''
|
'''
|
||||||
该理论认为,当两个或多个事物在形态上具有相似性时,
|
该理论认为,当两个或多个事物在形态上具有相似性时,
|
||||||
它们在记忆中会形成关联。
|
它们在记忆中会形成关联。
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"):
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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实例
|
||||||
|
|||||||
@@ -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
215
webui.py
@@ -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
28
webui_conda.bat
Normal 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
|
||||||
Reference in New Issue
Block a user