v0.1.5推理界面的ui

想用gradio写个ui,无果
放弃
写了一个单独的推理界面可视化ui,有bug之后修
This commit is contained in:
SengokuCola
2025-02-27 20:21:21 +08:00
parent 6fe44aba55
commit 3034c36717
12 changed files with 450 additions and 401 deletions

3
_thingking.bat Normal file
View File

@@ -0,0 +1,3 @@
call conda activate niuniu
cd .

1
reasoning_log.json Normal file
View File

@@ -0,0 +1 @@
{}

Binary file not shown.

View File

@@ -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)}")

View File

@@ -11,7 +11,6 @@ from ..schedule.schedule_generator import bot_schedule
from .willing_manager import willing_manager
# 获取驱动器
driver = get_driver()

View File

@@ -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 # 导入意愿管理器

View 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 = [
#降低回复频率的群
]

View File

@@ -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:

View File

@@ -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'
}

View File

@@ -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()

View File

@@ -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

View 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()