v0.1.5推理界面的ui
想用gradio写个ui,无果 放弃 写了一个单独的推理界面可视化ui,有bug之后修
This commit is contained in:
@@ -1,201 +0,0 @@
|
||||
from typing import Dict, List, Optional
|
||||
from dataclasses import dataclass
|
||||
import time
|
||||
import threading
|
||||
import asyncio
|
||||
from .message import Message
|
||||
from .storage import MessageStorage
|
||||
from .topic_identifier import TopicIdentifier
|
||||
from ...common.database import Database
|
||||
import random
|
||||
|
||||
@dataclass
|
||||
class Topic:
|
||||
id: str
|
||||
name: str
|
||||
messages: List[Message]
|
||||
created_time: float
|
||||
last_active_time: float
|
||||
message_count: int
|
||||
is_active: bool = True
|
||||
|
||||
class MessageStream:
|
||||
def __init__(self):
|
||||
self.storage = MessageStorage()
|
||||
self.active_topics: Dict[int, List[Topic]] = {} # group_id -> topics
|
||||
self.topic_identifier = TopicIdentifier()
|
||||
self.db = Database.get_instance()
|
||||
self.topic_lock = threading.Lock()
|
||||
|
||||
async def start(self):
|
||||
"""异步初始化"""
|
||||
asyncio.create_task(self._monitor_topics())
|
||||
|
||||
async def _monitor_topics(self):
|
||||
"""定时监控主题状态"""
|
||||
while True:
|
||||
await asyncio.sleep(30)
|
||||
self._print_active_topics()
|
||||
self._check_inactive_topics()
|
||||
self._remove_small_topic()
|
||||
|
||||
def _print_active_topics(self):
|
||||
"""打印当前活跃主题"""
|
||||
print("\n" + "="*50)
|
||||
print("\033[1;36m【当前活跃主题】\033[0m") # 青色
|
||||
for group_id, topics in self.active_topics.items():
|
||||
active_topics = [t for t in topics if t.is_active]
|
||||
if active_topics:
|
||||
print(f"\n\033[1;33m群组 {group_id}:\033[0m") # 黄色
|
||||
for topic in active_topics:
|
||||
print(f"\033[1;32m- {topic.name}\033[0m (消息数: {topic.message_count})") # 绿色
|
||||
|
||||
def _check_inactive_topics(self):
|
||||
"""检查并处理不活跃主题"""
|
||||
current_time = time.time()
|
||||
INACTIVE_TIME = 600 # 60秒内没有新增内容
|
||||
# MAX_MESSAGES_WITHOUT_TOPIC = 5 # 最新5条消息都不是这个主题就归档
|
||||
|
||||
with self.topic_lock:
|
||||
for group_id, topics in self.active_topics.items():
|
||||
|
||||
for topic in topics:
|
||||
if not topic.is_active:
|
||||
continue
|
||||
|
||||
# 检查是否超过不活跃时间
|
||||
time_inactive = current_time - topic.last_active_time
|
||||
if time_inactive > INACTIVE_TIME:
|
||||
# print(f"\033[1;33m[主题超时]\033[0m {topic.name} 已有 {int(time_inactive)} 秒未更新")
|
||||
self._archive_topic(group_id, topic)
|
||||
topic.is_active = False
|
||||
continue
|
||||
|
||||
|
||||
def _archive_topic(self, group_id: int, topic: Topic):
|
||||
"""将主题存档到数据库"""
|
||||
# 查找是否有同名主题
|
||||
existing_topic = self.db.db.archived_topics.find_one({
|
||||
"name": topic.name
|
||||
})
|
||||
|
||||
if existing_topic:
|
||||
# 合并消息列表并去重
|
||||
existing_messages = existing_topic.get("messages", [])
|
||||
new_messages = [
|
||||
{
|
||||
"user_id": msg.user_id,
|
||||
"plain_text": msg.plain_text,
|
||||
"time": msg.time
|
||||
} for msg in topic.messages
|
||||
]
|
||||
|
||||
# 使用集合去重
|
||||
seen_texts = set()
|
||||
unique_messages = []
|
||||
|
||||
# 先处理现有消息
|
||||
for msg in existing_messages:
|
||||
if msg["plain_text"] not in seen_texts:
|
||||
seen_texts.add(msg["plain_text"])
|
||||
unique_messages.append(msg)
|
||||
|
||||
# 再处理新消息
|
||||
for msg in new_messages:
|
||||
if msg["plain_text"] not in seen_texts:
|
||||
seen_texts.add(msg["plain_text"])
|
||||
unique_messages.append(msg)
|
||||
|
||||
# 更新主题信息
|
||||
self.db.db.archived_topics.update_one(
|
||||
{"_id": existing_topic["_id"]},
|
||||
{
|
||||
"$set": {
|
||||
"messages": unique_messages,
|
||||
"message_count": len(unique_messages),
|
||||
"last_active_time": max(existing_topic["last_active_time"], topic.last_active_time),
|
||||
"last_merged_time": time.time()
|
||||
}
|
||||
}
|
||||
)
|
||||
print(f"\033[1;33m[主题合并]\033[0m 主题 {topic.name} 已合并,总消息数: {len(unique_messages)}")
|
||||
|
||||
else:
|
||||
# 存储新主题
|
||||
self.db.db.archived_topics.insert_one({
|
||||
"topic_id": topic.id,
|
||||
"name": topic.name,
|
||||
"messages": [
|
||||
{
|
||||
"user_id": msg.user_id,
|
||||
"plain_text": msg.plain_text,
|
||||
"time": msg.time
|
||||
} for msg in topic.messages
|
||||
],
|
||||
"created_time": topic.created_time,
|
||||
"last_active_time": topic.last_active_time,
|
||||
"message_count": topic.message_count
|
||||
})
|
||||
print(f"\033[1;32m[主题存档]\033[0m {topic.name} (群组: {group_id})")
|
||||
|
||||
async def process_message(self, message: Message,topic:List[str]):
|
||||
"""处理新消息,返回识别出的主题列表"""
|
||||
# 存储消息(包含主题)
|
||||
await self.storage.store_message(message, topic)
|
||||
self._update_topics(message.group_id, topic, message)
|
||||
|
||||
def _update_topics(self, group_id: int, topic_names: List[str], message: Message) -> None:
|
||||
"""更新群组主题"""
|
||||
current_time = time.time()
|
||||
|
||||
# 确保群组存在
|
||||
if group_id not in self.active_topics:
|
||||
self.active_topics[group_id] = []
|
||||
|
||||
# 查找现有主题
|
||||
for topic_name in topic_names:
|
||||
for topic in self.active_topics[group_id]:
|
||||
if topic.name == topic_name:
|
||||
topic.messages.append(message)
|
||||
topic.last_active_time = current_time
|
||||
topic.message_count += 1
|
||||
print(f"\033[1;35m[更新主题]\033[0m {topic_name}") # 绿色
|
||||
break
|
||||
else:
|
||||
# 创建新主题
|
||||
new_topic = Topic(
|
||||
id=f"{group_id}_{int(current_time)}",
|
||||
name=topic_name,
|
||||
messages=[message],
|
||||
created_time=current_time,
|
||||
last_active_time=current_time,
|
||||
message_count=1
|
||||
)
|
||||
self.active_topics[group_id].append(new_topic)
|
||||
|
||||
self._check_inactive_topics()
|
||||
|
||||
def _remove_small_topic(self):
|
||||
"""随机移除一个12小时内没有新增内容的小主题"""
|
||||
try:
|
||||
current_time = time.time()
|
||||
inactive_time = 12 * 3600 # 24小时
|
||||
|
||||
# 获取所有符合条件的主题
|
||||
topics = list(self.db.db.archived_topics.find({
|
||||
"message_count": {"$lt": 3}, # 消息数小于2
|
||||
"last_active_time": {"$lt": current_time - inactive_time}
|
||||
}))
|
||||
|
||||
if not topics:
|
||||
return
|
||||
|
||||
# 随机选择一个主题删除
|
||||
topic_to_remove = random.choice(topics)
|
||||
inactive_hours = (current_time - topic_to_remove.get("last_active_time", 0)) / 3600
|
||||
|
||||
self.db.db.archived_topics.delete_one({"_id": topic_to_remove["_id"]})
|
||||
print(f"\033[1;31m[主题清理]\033[0m 已移除小主题: {topic_to_remove['name']} "
|
||||
f"不活跃时间: {int(inactive_hours)}小时)")
|
||||
except Exception as e:
|
||||
print(f"\033[1;31m[错误]\033[0m 移除小主题失败: {str(e)}")
|
||||
@@ -11,7 +11,6 @@ from ..schedule.schedule_generator import bot_schedule
|
||||
from .willing_manager import willing_manager
|
||||
|
||||
|
||||
|
||||
# 获取驱动器
|
||||
driver = get_driver()
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ from .llm_generator import LLMResponseGenerator
|
||||
from .message_stream import MessageStream, MessageStreamContainer
|
||||
from .topic_identifier import topic_identifier
|
||||
from random import random
|
||||
from nonebot.log import logger
|
||||
from .group_info_manager import GroupInfoManager # 导入群信息管理器
|
||||
from .emoji_manager import emoji_manager # 导入表情包管理器
|
||||
import time
|
||||
@@ -15,7 +14,6 @@ from .cq_code import CQCode # 导入CQCode模块
|
||||
from .message_send_control import message_sender # 导入消息发送控制器
|
||||
from .message import Message_Thinking # 导入 Message_Thinking 类
|
||||
from .relationship_manager import relationship_manager
|
||||
from .prompt_builder import prompt_builder
|
||||
from .willing_manager import willing_manager # 导入意愿管理器
|
||||
|
||||
|
||||
@@ -132,7 +130,7 @@ class ChatBot:
|
||||
)
|
||||
current_willing = willing_manager.get_willing(event.group_id)
|
||||
|
||||
|
||||
|
||||
print(f"\033[1;32m[{current_time}][{message.group_name}]{message.user_nickname}:\033[0m {message.processed_plain_text}\033[1;36m[回复意愿:{current_willing:.2f}][概率:{reply_probability:.1f}]\033[0m")
|
||||
response = ""
|
||||
if random() < reply_probability:
|
||||
|
||||
33
src/plugins/chat/bot_config_toml
Normal file
33
src/plugins/chat/bot_config_toml
Normal file
@@ -0,0 +1,33 @@
|
||||
[database]
|
||||
host = "127.0.0.1"
|
||||
port = 27017
|
||||
name = "MegBot"
|
||||
|
||||
[bot]
|
||||
qq = #填入你的机器人QQ
|
||||
|
||||
[message]
|
||||
min_text_length = 2
|
||||
max_context_size = 15
|
||||
emoji_chance = 0.2
|
||||
|
||||
[emoji]
|
||||
check_interval = 120
|
||||
register_interval = 10
|
||||
|
||||
[response]
|
||||
model_r1_probability = 0.2 #使用R1回复的概率(没启用)
|
||||
|
||||
|
||||
[groups]
|
||||
read_allowed = [
|
||||
#可以读取消息的群
|
||||
]
|
||||
|
||||
talk_allowed = [
|
||||
#可以回复消息的群
|
||||
]
|
||||
|
||||
talk_frequency_down = [
|
||||
#降低回复频率的群
|
||||
]
|
||||
@@ -10,11 +10,11 @@ import tomli # 添加这行导入
|
||||
# logger.remove()
|
||||
|
||||
# # 只禁用 INFO 级别的日志输出到控制台
|
||||
# logging.getLogger('nonebot').handlers.clear()
|
||||
# console_handler = logging.StreamHandler()
|
||||
# console_handler.setLevel(logging.WARNING) # 只输出 WARNING 及以上级别
|
||||
# logging.getLogger('nonebot').addHandler(console_handler)
|
||||
# logging.getLogger('nonebot').setLevel(logging.WARNING)
|
||||
logging.getLogger('nonebot').handlers.clear()
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.WARNING) # 只输出 WARNING 及以上级别
|
||||
logging.getLogger('nonebot').addHandler(console_handler)
|
||||
logging.getLogger('nonebot').setLevel(logging.WARNING)
|
||||
|
||||
@dataclass
|
||||
class BotConfig:
|
||||
|
||||
@@ -98,12 +98,10 @@ class CQCode:
|
||||
|
||||
# 添加更多请求头
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
||||
'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
|
||||
'User-Agent': 'QQ/8.9.68.11565 CFNetwork/1220.1 Darwin/20.3.0',
|
||||
'Accept': 'image/*;q=0.8',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Connection': 'keep-alive',
|
||||
'Referer': 'https://multimedia.nt.qq.com.cn',
|
||||
'Origin': 'https://multimedia.nt.qq.com.cn',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Pragma': 'no-cache'
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import gradio as gr
|
||||
import time
|
||||
import threading
|
||||
from typing import Dict, List
|
||||
from .message import Message
|
||||
|
||||
class MessageWindow:
|
||||
def __init__(self):
|
||||
self.interface = None
|
||||
self._running = False
|
||||
self.messages_history = []
|
||||
|
||||
def _create_window(self):
|
||||
"""创建Gradio界面"""
|
||||
with gr.Blocks(title="实时消息监控") as self.interface:
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
self.message_box = gr.Dataframe(
|
||||
headers=["时间", "群号", "发送者", "消息内容"],
|
||||
datatype=["str", "str", "str", "str"],
|
||||
row_count=20,
|
||||
col_count=(4, "fixed"),
|
||||
interactive=False,
|
||||
wrap=True
|
||||
)
|
||||
|
||||
# 每1秒自动刷新
|
||||
self.interface.load(self._update_display, None, [self.message_box], every=1)
|
||||
|
||||
# 启动界面
|
||||
self.interface.queue()
|
||||
self._running = True
|
||||
self.interface.launch(share=False, server_port=7860)
|
||||
|
||||
def _update_display(self):
|
||||
"""更新消息显示"""
|
||||
display_data = []
|
||||
for msg in self.messages_history[-1000:]: # 只显示最近1000条消息
|
||||
time_str = time.strftime("%H:%M:%S", time.localtime(msg["time"]))
|
||||
display_data.append([
|
||||
time_str,
|
||||
str(msg["group_id"]),
|
||||
f"{msg['user_nickname']}({msg['user_id']})",
|
||||
msg["plain_text"]
|
||||
])
|
||||
return display_data
|
||||
|
||||
def update_messages(self, group_id: int, messages: List[Message]):
|
||||
"""接收新消息更新"""
|
||||
for msg in messages:
|
||||
self.messages_history.append({
|
||||
"time": msg.time,
|
||||
"group_id": group_id,
|
||||
"user_id": msg.user_id,
|
||||
"user_nickname": msg.user_nickname,
|
||||
"plain_text": msg.plain_text
|
||||
})
|
||||
|
||||
# 保持最多存储1000条消息
|
||||
if len(self.messages_history) > 1000:
|
||||
self.messages_history = self.messages_history[-1000:]
|
||||
|
||||
def start(self):
|
||||
"""启动窗口"""
|
||||
# 在新线程中启动窗口
|
||||
threading.Thread(target=self._create_window, daemon=True).start()
|
||||
|
||||
def stop(self):
|
||||
"""停止窗口"""
|
||||
self._running = False
|
||||
if self.interface:
|
||||
self.interface.close()
|
||||
|
||||
# 创建全局实例
|
||||
message_window = MessageWindow()
|
||||
|
||||
@@ -8,96 +8,21 @@ from .config import BotConfig
|
||||
from ...common.database import Database
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import queue
|
||||
import numpy as np
|
||||
from dotenv import load_dotenv
|
||||
from .relationship_manager import relationship_manager
|
||||
from ..schedule.schedule_generator import bot_schedule
|
||||
from .prompt_builder import prompt_builder
|
||||
from .config import llm_config
|
||||
from .willing_manager import willing_manager
|
||||
from .utils import get_embedding, split_into_sentences, process_text_with_typos
|
||||
import aiohttp
|
||||
|
||||
|
||||
# 获取当前文件的绝对路径
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
root_dir = os.path.abspath(os.path.join(current_dir, '..', '..', '..'))
|
||||
load_dotenv(os.path.join(root_dir, '.env'))
|
||||
|
||||
class ReasoningWindow:
|
||||
def __init__(self):
|
||||
self.process = None
|
||||
self.message_queue = queue.Queue()
|
||||
self.is_running = False
|
||||
self.content_file = "reasoning_content.txt"
|
||||
|
||||
def start(self):
|
||||
if self.process is None:
|
||||
# 创建用于显示的批处理文件
|
||||
with open("reasoning_window.bat", "w", encoding="utf-8") as f:
|
||||
f.write('@echo off\n')
|
||||
f.write('chcp 65001\n') # 设置UTF-8编码
|
||||
f.write('title Magellan Reasoning Process\n')
|
||||
f.write('echo Waiting for reasoning content...\n')
|
||||
f.write(':loop\n')
|
||||
f.write('if exist "reasoning_update.txt" (\n')
|
||||
f.write(' type "reasoning_update.txt" >> "reasoning_content.txt"\n')
|
||||
f.write(' del "reasoning_update.txt"\n')
|
||||
f.write(' cls\n')
|
||||
f.write(' type "reasoning_content.txt"\n')
|
||||
f.write(')\n')
|
||||
f.write('timeout /t 1 /nobreak >nul\n')
|
||||
f.write('goto loop\n')
|
||||
|
||||
# 清空内容文件
|
||||
with open(self.content_file, "w", encoding="utf-8") as f:
|
||||
f.write("")
|
||||
|
||||
# 启动新窗口
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
self.process = subprocess.Popen(['cmd', '/c', 'start', 'reasoning_window.bat'],
|
||||
shell=True,
|
||||
startupinfo=startupinfo)
|
||||
self.is_running = True
|
||||
|
||||
# 启动处理线程
|
||||
threading.Thread(target=self._process_messages, daemon=True).start()
|
||||
|
||||
def _process_messages(self):
|
||||
while self.is_running:
|
||||
try:
|
||||
# 获取新消息
|
||||
text = self.message_queue.get(timeout=1)
|
||||
# 写入更新文件
|
||||
with open("reasoning_update.txt", "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
except queue.Empty:
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"处理推理内容时出错: {e}")
|
||||
|
||||
def update_content(self, text: str):
|
||||
if self.is_running:
|
||||
self.message_queue.put(text)
|
||||
|
||||
def stop(self):
|
||||
self.is_running = False
|
||||
if self.process:
|
||||
self.process.terminate()
|
||||
self.process = None
|
||||
# 清理文件
|
||||
for file in ["reasoning_window.bat", "reasoning_content.txt", "reasoning_update.txt"]:
|
||||
if os.path.exists(file):
|
||||
os.remove(file)
|
||||
|
||||
# 创建全局单例
|
||||
reasoning_window = ReasoningWindow()
|
||||
|
||||
class LLMResponseGenerator:
|
||||
def __init__(self, config: BotConfig):
|
||||
self.config = config
|
||||
@@ -107,7 +32,7 @@ class LLMResponseGenerator:
|
||||
)
|
||||
|
||||
self.db = Database.get_instance()
|
||||
reasoning_window.start()
|
||||
|
||||
# 当前使用的模型类型
|
||||
self.current_model_type = 'r1' # 默认使用 R1
|
||||
|
||||
@@ -115,7 +40,7 @@ class LLMResponseGenerator:
|
||||
"""根据当前模型类型选择对应的生成函数"""
|
||||
# 使用随机数选择模型
|
||||
rand = random.random()
|
||||
if rand < 0.6: # 60%概率使用 R1
|
||||
if rand < 0.8: # 60%概率使用 R1
|
||||
self.current_model_type = "r1"
|
||||
elif rand < 0.5: # 20%概率使用 V3
|
||||
self.current_model_type = "v3"
|
||||
@@ -170,11 +95,26 @@ class LLMResponseGenerator:
|
||||
response = await loop.run_in_executor(None, create_completion)
|
||||
if response.choices[0].message.content:
|
||||
content = response.choices[0].message.content
|
||||
reasoning_content = response.choices[0].message.reasoning_content
|
||||
# 获取推理内容
|
||||
reasoning_content = "模型思考过程:\n" + prompt
|
||||
if hasattr(response.choices[0].message, "reasoning"):
|
||||
reasoning_content = response.choices[0].message.reasoning or reasoning_content
|
||||
elif hasattr(response.choices[0].message, "reasoning_content"):
|
||||
reasoning_content = response.choices[0].message.reasoning_content or reasoning_content
|
||||
|
||||
# 保存推理结果到数据库
|
||||
self.db.db.reasoning_logs.insert_one({
|
||||
'time': time.time(),
|
||||
'group_id': message.group_id,
|
||||
'user': sender_name,
|
||||
'message': message.processed_plain_text,
|
||||
'model': "DeepSeek-R1",
|
||||
'reasoning': reasoning_content,
|
||||
'response': content,
|
||||
'prompt': prompt
|
||||
})
|
||||
else:
|
||||
return None
|
||||
# 更新推理窗口
|
||||
self._update_reasoning_window(message, prompt, reasoning_content, content, sender_name)
|
||||
|
||||
return content
|
||||
|
||||
@@ -207,8 +147,18 @@ class LLMResponseGenerator:
|
||||
|
||||
if response.choices[0].message.content:
|
||||
content = response.choices[0].message.content
|
||||
# V3 模型没有 reasoning_content
|
||||
self._update_reasoning_window(message, prompt, "V3模型无推理过程", content, sender_name)
|
||||
# 保存推理结果到数据库
|
||||
self.db.db.reasoning_logs.insert_one({
|
||||
'time': time.time(),
|
||||
'group_id': message.group_id,
|
||||
'user': sender_name,
|
||||
'message': message.processed_plain_text,
|
||||
'model': "DeepSeek-V3",
|
||||
'reasoning': "V3模型无推理过程",
|
||||
'response': content,
|
||||
'prompt': prompt
|
||||
})
|
||||
|
||||
return content
|
||||
else:
|
||||
print(f"[ERROR] V3 回复发送生成失败: {response}")
|
||||
@@ -246,11 +196,27 @@ class LLMResponseGenerator:
|
||||
response = await loop.run_in_executor(None, create_completion)
|
||||
if response.choices[0].message.content:
|
||||
content = response.choices[0].message.content
|
||||
reasoning_content = response.choices[0].message.reasoning_content
|
||||
# 获取推理内容
|
||||
reasoning_content = "模型思考过程:\n" + prompt
|
||||
if hasattr(response.choices[0].message, "reasoning"):
|
||||
reasoning_content = response.choices[0].message.reasoning or reasoning_content
|
||||
elif hasattr(response.choices[0].message, "reasoning_content"):
|
||||
reasoning_content = response.choices[0].message.reasoning_content or reasoning_content
|
||||
|
||||
# 保存推理结果到数据库
|
||||
self.db.db.reasoning_logs.insert_one({
|
||||
'time': time.time(),
|
||||
'group_id': message.group_id,
|
||||
'user': sender_name,
|
||||
'message': message.processed_plain_text,
|
||||
'model': "DeepSeek-R1-Distill",
|
||||
'reasoning': reasoning_content,
|
||||
'response': content,
|
||||
'prompt': prompt
|
||||
})
|
||||
else:
|
||||
return None
|
||||
# 更新推理窗口
|
||||
self._update_reasoning_window(message, prompt, reasoning_content, content, sender_name)
|
||||
|
||||
|
||||
return content
|
||||
|
||||
@@ -272,31 +238,6 @@ class LLMResponseGenerator:
|
||||
|
||||
return group_chat
|
||||
|
||||
def _update_reasoning_window(self, message, prompt, reasoning_content, content, sender_name):
|
||||
"""更新推理窗口内容"""
|
||||
current_time = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 获取当前使用的模型名称
|
||||
model_name = {
|
||||
'r1': 'DeepSeek-R1',
|
||||
'v3': 'DeepSeek-V3',
|
||||
'r1_distill': 'DeepSeek-R1-Distill-Qwen-32B'
|
||||
}.get(self.current_model_type, '未知模型')
|
||||
|
||||
display_text = (
|
||||
f"Time: {current_time}\n"
|
||||
f"Group: {message.group_name}\n"
|
||||
f"User: {sender_name}\n"
|
||||
f"Model: {model_name}\n"
|
||||
f"\033[1;32mMessage:\033[0m {message.processed_plain_text}\n\n"
|
||||
f"\033[1;32mPrompt:\033[0m \n{prompt}\n"
|
||||
f"\n-------------------------------------------------------"
|
||||
f"\n\033[1;32mReasoning Process:\033[0m\n{reasoning_content}\n"
|
||||
f"\n\033[1;32mResponse Content:\033[0m\n{content}\n"
|
||||
f"\n{'='*50}\n"
|
||||
)
|
||||
reasoning_window.update_content(display_text)
|
||||
|
||||
async def _get_emotion_tags(self, content: str) -> List[str]:
|
||||
"""提取情感标签"""
|
||||
try:
|
||||
@@ -335,6 +276,11 @@ class LLMResponseGenerator:
|
||||
if not content:
|
||||
return None, []
|
||||
|
||||
# 检查回复是否过长(超过200个字符)
|
||||
if len(content) > 200:
|
||||
print(f"回复过长 ({len(content)} 字符),返回默认回复")
|
||||
return "麦麦不知道哦", ["angry"]
|
||||
|
||||
emotion_tags = await self._get_emotion_tags(content)
|
||||
|
||||
# 添加错别字和处理标点符号
|
||||
@@ -359,6 +305,11 @@ class LLMResponseGenerator:
|
||||
if current_message:
|
||||
messages.append(current_message.strip())
|
||||
|
||||
# 检查分割后的消息数量是否过多(超过3条)
|
||||
if len(messages) > 3:
|
||||
print(f"分割后消息数量过多 ({len(messages)} 条),返回默认回复")
|
||||
return "麦麦不知道哦", ["angry"]
|
||||
|
||||
return messages, emotion_tags
|
||||
|
||||
return processed_response, emotion_tags
|
||||
|
||||
343
src/plugins/chat/reasoning_gui.py
Normal file
343
src/plugins/chat/reasoning_gui.py
Normal file
@@ -0,0 +1,343 @@
|
||||
import customtkinter as ctk
|
||||
from typing import Dict, List
|
||||
import json
|
||||
from datetime import datetime
|
||||
import time
|
||||
import threading
|
||||
import queue
|
||||
import sys
|
||||
import os
|
||||
|
||||
from pymongo import MongoClient
|
||||
from typing import Optional
|
||||
|
||||
class Database:
|
||||
_instance: Optional["Database"] = None
|
||||
|
||||
def __init__(self, host: str, port: int, db_name: str):
|
||||
self.client = MongoClient(host, port)
|
||||
self.db = self.client[db_name]
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, host: str, port: int, db_name: str) -> "Database":
|
||||
if cls._instance is None:
|
||||
cls._instance = cls(host, port, db_name)
|
||||
return cls._instance
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls) -> "Database":
|
||||
if cls._instance is None:
|
||||
raise RuntimeError("Database not initialized")
|
||||
return cls._instance
|
||||
|
||||
|
||||
|
||||
class ReasoningGUI:
|
||||
def __init__(self):
|
||||
# 记录启动时间戳,转换为Unix时间戳
|
||||
self.start_timestamp = datetime.now().timestamp()
|
||||
print(f"程序启动时间戳: {self.start_timestamp}")
|
||||
|
||||
# 设置主题
|
||||
ctk.set_appearance_mode("dark")
|
||||
ctk.set_default_color_theme("blue")
|
||||
|
||||
# 创建主窗口
|
||||
self.root = ctk.CTk()
|
||||
self.root.title('麦麦推理')
|
||||
self.root.geometry('800x600')
|
||||
self.root.protocol("WM_DELETE_WINDOW", self._on_closing)
|
||||
|
||||
# 初始化数据库连接
|
||||
try:
|
||||
self.db = Database.get_instance().db
|
||||
print("数据库连接成功")
|
||||
except RuntimeError:
|
||||
print("数据库未初始化,正在尝试初始化...")
|
||||
try:
|
||||
Database.initialize("localhost", 27017, "maimai_bot")
|
||||
self.db = Database.get_instance().db
|
||||
print("数据库初始化成功")
|
||||
except Exception as e:
|
||||
print(f"数据库初始化失败: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# 存储群组数据
|
||||
self.group_data: Dict[str, List[dict]] = {}
|
||||
|
||||
# 创建更新队列
|
||||
self.update_queue = queue.Queue()
|
||||
|
||||
# 创建主框架
|
||||
self.frame = ctk.CTkFrame(self.root)
|
||||
self.frame.pack(pady=20, padx=20, fill="both", expand=True)
|
||||
|
||||
# 添加标题
|
||||
self.title = ctk.CTkLabel(self.frame, text="AI推理监控系统", font=("Arial", 24))
|
||||
self.title.pack(pady=10, padx=10)
|
||||
|
||||
# 创建左右分栏
|
||||
self.paned = ctk.CTkFrame(self.frame)
|
||||
self.paned.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
# 左侧群组列表
|
||||
self.left_frame = ctk.CTkFrame(self.paned, width=200)
|
||||
self.left_frame.pack(side="left", fill="y", padx=5, pady=5)
|
||||
|
||||
self.group_label = ctk.CTkLabel(self.left_frame, text="群组列表", font=("Arial", 16))
|
||||
self.group_label.pack(pady=5)
|
||||
|
||||
self.group_listbox = ctk.CTkTextbox(self.left_frame, width=180, height=400)
|
||||
self.group_listbox.pack(pady=5, padx=5)
|
||||
|
||||
# 右侧内容显示
|
||||
self.right_frame = ctk.CTkFrame(self.paned)
|
||||
self.right_frame.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
||||
|
||||
self.content_label = ctk.CTkLabel(self.right_frame, text="推理内容", font=("Arial", 16))
|
||||
self.content_label.pack(pady=5)
|
||||
|
||||
# 创建富文本显示框
|
||||
self.content_text = ctk.CTkTextbox(self.right_frame, width=500, height=400)
|
||||
self.content_text.pack(pady=5, padx=5, fill="both", expand=True)
|
||||
|
||||
# 配置文本标签 - 只使用颜色
|
||||
self.content_text.tag_config("timestamp", foreground="#888888") # 时间戳使用灰色
|
||||
self.content_text.tag_config("user", foreground="#4CAF50") # 用户名使用绿色
|
||||
self.content_text.tag_config("message", foreground="#2196F3") # 消息使用蓝色
|
||||
self.content_text.tag_config("model", foreground="#9C27B0") # 模型名称使用紫色
|
||||
self.content_text.tag_config("reasoning", foreground="#FF9800") # 推理过程使用橙色
|
||||
self.content_text.tag_config("response", foreground="#E91E63") # 回复使用粉色
|
||||
self.content_text.tag_config("separator", foreground="#666666") # 分隔符使用深灰色
|
||||
|
||||
# 底部控制栏
|
||||
self.control_frame = ctk.CTkFrame(self.frame)
|
||||
self.control_frame.pack(fill="x", padx=10, pady=5)
|
||||
|
||||
self.clear_button = ctk.CTkButton(
|
||||
self.control_frame,
|
||||
text="清除显示",
|
||||
command=self.clear_display,
|
||||
width=120
|
||||
)
|
||||
self.clear_button.pack(side="left", padx=5)
|
||||
|
||||
# 添加群组点击事件
|
||||
self.group_listbox.bind('<Button-1>', self._on_group_click)
|
||||
|
||||
# 启动自动更新线程
|
||||
self.update_thread = threading.Thread(target=self._auto_update, daemon=True)
|
||||
self.update_thread.start()
|
||||
|
||||
# 启动GUI更新检查
|
||||
self.root.after(100, self._process_queue)
|
||||
|
||||
def _on_closing(self):
|
||||
"""处理窗口关闭事件"""
|
||||
self.root.quit()
|
||||
sys.exit(0)
|
||||
|
||||
def _process_queue(self):
|
||||
"""处理更新队列中的任务"""
|
||||
try:
|
||||
while True:
|
||||
task = self.update_queue.get_nowait()
|
||||
if task['type'] == 'update_group_list':
|
||||
self._update_group_list_gui()
|
||||
elif task['type'] == 'update_display':
|
||||
self._update_display_gui(task['group_id'])
|
||||
except queue.Empty:
|
||||
pass
|
||||
finally:
|
||||
# 继续检查队列
|
||||
self.root.after(100, self._process_queue)
|
||||
|
||||
def _update_group_list_gui(self):
|
||||
"""在主线程中更新群组列表"""
|
||||
self.group_listbox.delete("1.0", "end")
|
||||
for group_id in self.group_data.keys():
|
||||
self.group_listbox.insert("end", f"群号: {group_id}\n")
|
||||
|
||||
def _update_display_gui(self, group_id: str):
|
||||
"""在主线程中更新显示内容"""
|
||||
if group_id in self.group_data:
|
||||
self.content_text.delete("1.0", "end")
|
||||
for item in self.group_data[group_id]:
|
||||
# 时间戳
|
||||
time_str = item['time'].strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.content_text.insert("end", f"[{time_str}]\n", "timestamp")
|
||||
|
||||
# 用户信息
|
||||
self.content_text.insert("end", "用户: ", "timestamp")
|
||||
self.content_text.insert("end", f"{item.get('user', '未知')}\n", "user")
|
||||
|
||||
# 消息内容
|
||||
self.content_text.insert("end", "消息: ", "timestamp")
|
||||
self.content_text.insert("end", f"{item.get('message', '')}\n", "message")
|
||||
|
||||
# 模型信息
|
||||
self.content_text.insert("end", "模型: ", "timestamp")
|
||||
self.content_text.insert("end", f"{item.get('model', '')}\n", "model")
|
||||
|
||||
# 推理过程
|
||||
self.content_text.insert("end", "推理过程:\n", "timestamp")
|
||||
reasoning_text = item.get('reasoning', '')
|
||||
# 处理推理过程中的Markdown格式
|
||||
lines = reasoning_text.split('\n')
|
||||
for line in lines:
|
||||
if line.strip():
|
||||
# 添加缩进
|
||||
self.content_text.insert("end", " " + line + "\n", "reasoning")
|
||||
|
||||
# 回复内容
|
||||
self.content_text.insert("end", "回复: ", "timestamp")
|
||||
self.content_text.insert("end", f"{item.get('response', '')}\n", "response")
|
||||
|
||||
# 分隔符
|
||||
self.content_text.insert("end", f"\n{'='*50}\n\n", "separator")
|
||||
|
||||
# 滚动到顶部
|
||||
self.content_text.see("1.0")
|
||||
|
||||
def _auto_update(self):
|
||||
"""自动更新函数"""
|
||||
while True:
|
||||
try:
|
||||
# 从数据库获取最新数据,只获取启动时间之后的记录
|
||||
query = {"time": {"$gt": self.start_timestamp}}
|
||||
print(f"查询条件: {query}")
|
||||
|
||||
# 先获取一条记录检查时间格式
|
||||
sample = self.db.reasoning_logs.find_one()
|
||||
if sample:
|
||||
print(f"样本记录时间格式: {type(sample['time'])} 值: {sample['time']}")
|
||||
|
||||
cursor = self.db.reasoning_logs.find(query).sort("time", -1)
|
||||
new_data = {}
|
||||
total_count = 0
|
||||
|
||||
for item in cursor:
|
||||
# 调试输出
|
||||
if total_count == 0:
|
||||
print(f"记录时间: {item['time']}, 类型: {type(item['time'])}")
|
||||
|
||||
total_count += 1
|
||||
group_id = str(item.get('group_id', 'unknown'))
|
||||
if group_id not in new_data:
|
||||
new_data[group_id] = []
|
||||
|
||||
# 转换时间戳为datetime对象
|
||||
if isinstance(item['time'], (int, float)):
|
||||
time_obj = datetime.fromtimestamp(item['time'])
|
||||
elif isinstance(item['time'], datetime):
|
||||
time_obj = item['time']
|
||||
else:
|
||||
print(f"未知的时间格式: {type(item['time'])}")
|
||||
time_obj = datetime.now() # 使用当前时间作为后备
|
||||
|
||||
new_data[group_id].append({
|
||||
'time': time_obj,
|
||||
'user': item.get('user_nickname', item.get('user_id', '未知')),
|
||||
'message': item.get('message', ''),
|
||||
'model': item.get('model', '未知'),
|
||||
'reasoning': item.get('reasoning', ''),
|
||||
'response': item.get('response', ''),
|
||||
})
|
||||
|
||||
print(f"从数据库加载了 {total_count} 条记录,分布在 {len(new_data)} 个群组中")
|
||||
|
||||
# 更新数据
|
||||
if new_data != self.group_data:
|
||||
self.group_data = new_data
|
||||
print("数据已更新,正在刷新显示...")
|
||||
# 将更新任务添加到队列
|
||||
self.update_queue.put({'type': 'update_group_list'})
|
||||
if self.group_data:
|
||||
latest_group = next(iter(self.group_data))
|
||||
self.update_queue.put({
|
||||
'type': 'update_display',
|
||||
'group_id': latest_group
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"自动更新出错: {e}")
|
||||
|
||||
# 每5秒更新一次
|
||||
time.sleep(5)
|
||||
|
||||
def _on_group_click(self, event):
|
||||
"""处理群组点击事件"""
|
||||
try:
|
||||
# 获取点击位置的文本行
|
||||
index = self.group_listbox.index(f"@{event.x},{event.y}")
|
||||
line = self.group_listbox.get(f"{index} linestart", f"{index} lineend")
|
||||
if line.startswith("群号: "):
|
||||
group_id = line.replace("群号: ", "").strip()
|
||||
self.update_display(group_id)
|
||||
except Exception as e:
|
||||
print(f"处理群组点击事件出错: {e}")
|
||||
|
||||
def update_display(self, group_id: str):
|
||||
"""更新显示指定群组的内容"""
|
||||
if group_id in self.group_data:
|
||||
self.content_text.delete("1.0", "end")
|
||||
for item in self.group_data[group_id]:
|
||||
# 时间戳
|
||||
time_str = item['time'].strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.content_text.insert("end", f"[{time_str}]\n", "timestamp")
|
||||
|
||||
# 用户信息
|
||||
self.content_text.insert("end", "用户: ", "timestamp")
|
||||
self.content_text.insert("end", f"** {item.get('user', '未知')} **\n", "user")
|
||||
|
||||
# 消息内容
|
||||
self.content_text.insert("end", "消息: ", "timestamp")
|
||||
self.content_text.insert("end", f"{item.get('message', '')}\n", "message")
|
||||
|
||||
# 模型信息
|
||||
self.content_text.insert("end", "模型: ", "timestamp")
|
||||
self.content_text.insert("end", f"{item.get('model', '')}\n", "model")
|
||||
|
||||
# 推理过程
|
||||
self.content_text.insert("end", "推理过程:\n", "timestamp")
|
||||
reasoning_text = item.get('reasoning', '')
|
||||
# 处理推理过程中的Markdown格式
|
||||
lines = reasoning_text.split('\n')
|
||||
for line in lines:
|
||||
if line.strip():
|
||||
# 添加缩进
|
||||
self.content_text.insert("end", " " + line + "\n", "reasoning")
|
||||
|
||||
# 回复内容
|
||||
self.content_text.insert("end", "回复: ", "timestamp")
|
||||
self.content_text.insert("end", f"{item.get('response', '')}\n", "response")
|
||||
|
||||
# 分隔符
|
||||
self.content_text.insert("end", f"\n{'='*50}\n\n", "separator")
|
||||
|
||||
# 滚动到顶部
|
||||
self.content_text.see("1.0")
|
||||
|
||||
def clear_display(self):
|
||||
"""清除显示内容"""
|
||||
self.content_text.delete("1.0", "end")
|
||||
|
||||
def run(self):
|
||||
"""运行GUI"""
|
||||
self.root.mainloop()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
Database.initialize(
|
||||
"127.0.0.1",
|
||||
27017,
|
||||
"MegBot"
|
||||
)
|
||||
|
||||
app = ReasoningGUI()
|
||||
app.run()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user