better:美化统计界面

pull/1356/head
SengokuCola 2025-11-10 22:13:25 +08:00
parent 837ecf702b
commit fd19170543
4 changed files with 135 additions and 75 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@ data/
data1/
mongodb/
NapCat.Framework.Windows.Once/
NapCat.Framework.Windows.OneKey/
log/
logs/
out/

View File

@ -430,7 +430,7 @@ class HeartFChatting:
asyncio.create_task(extract_and_store_jargon(self.stream_id))
# 添加聊天内容概括任务 - 累积、打包和压缩聊天记录
# 注意后台循环已在start()中启动,这里作为额外触发点,在有思考时立即处理
asyncio.create_task(self.chat_history_summarizer.process())
# asyncio.create_task(self.chat_history_summarizer.process())
cycle_timers, thinking_id = self.start_cycle()

View File

@ -104,7 +104,6 @@ class ChatHistorySummarizer:
if not new_messages:
# 没有新消息,检查是否需要打包
logger.info(f"{self.log_prefix} 无新增消息,尝试对现有批次执行打包检查")
if self.current_batch and self.current_batch.messages:
await self._check_and_package(current_time)
self.last_check_time = current_time
@ -112,10 +111,6 @@ class ChatHistorySummarizer:
# 有新消息,更新最后检查时间
self.last_check_time = current_time
logger.info(
f"{self.log_prefix} 获取到 {len(new_messages)} 条新消息,最新消息时间戳: {new_messages[-1].time if new_messages else 'N/A'}"
)
# 如果有当前批次,添加新消息
if self.current_batch:
@ -123,7 +118,7 @@ class ChatHistorySummarizer:
self.current_batch.messages.extend(new_messages)
self.current_batch.end_time = current_time
logger.info(
f"{self.log_prefix} 扩展现有批次: {before_count} -> {len(self.current_batch.messages)} 条消息,时间范围: {self.current_batch.start_time:.2f} - {self.current_batch.end_time:.2f}"
f"{self.log_prefix} 批次更新: {before_count} -> {len(self.current_batch.messages)} 条消息"
)
else:
# 创建新批次
@ -133,7 +128,7 @@ class ChatHistorySummarizer:
end_time=current_time,
)
logger.info(
f"{self.log_prefix} 创建新批次: 消息数 {len(new_messages)},时间范围: {self.current_batch.start_time:.2f} - {self.current_batch.end_time:.2f}"
f"{self.log_prefix} 批次: {len(new_messages)} 条消息"
)
# 检查是否需要打包

View File

@ -820,48 +820,70 @@ class StatisticOutputTask(AsyncTask):
<strong>统计时段: </strong>
{start_time.strftime("%Y-%m-%d %H:%M:%S")} ~ {now.strftime("%Y-%m-%d %H:%M:%S")}
</p>
<p class=\"info-item\"><strong>总在线时间: </strong>{_format_online_time(stat_data[ONLINE_TIME])}</p>
<p class=\"info-item\"><strong>总消息数: </strong>{stat_data[TOTAL_MSG_CNT]}</p>
<p class=\"info-item\"><strong>总请求数: </strong>{stat_data[TOTAL_REQ_CNT]}</p>
<p class=\"info-item\"><strong>总花费: </strong>{stat_data[TOTAL_COST]:.2f} ¥</p>
<div class=\"kpi-cards\">
<div class=\"kpi-card\">
<div class=\"kpi-title\">总在线时间</div>
<div class=\"kpi-value\">{_format_online_time(stat_data[ONLINE_TIME])}</div>
</div>
<div class=\"kpi-card\">
<div class=\"kpi-title\">总消息数</div>
<div class=\"kpi-value\">{stat_data[TOTAL_MSG_CNT]}</div>
</div>
<div class=\"kpi-card\">
<div class=\"kpi-title\">总请求数</div>
<div class=\"kpi-value\">{stat_data[TOTAL_REQ_CNT]}</div>
</div>
<div class=\"kpi-card\">
<div class=\"kpi-title\">总花费</div>
<div class=\"kpi-value\">{stat_data[TOTAL_COST]:.2f} ¥</div>
</div>
</div>
<h2>按模型分类统计</h2>
<table>
<thead><tr><th>模型名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时()</th><th>标准差()</th></tr></thead>
<tbody>
{model_rows}
</tbody>
</table>
<div class=\"table-wrap\">
<table>
<thead><tr><th>模型名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时()</th><th>标准差()</th></tr></thead>
<tbody>
{model_rows}
</tbody>
</table>
</div>
<h2>按模块分类统计</h2>
<table>
<thead>
<tr><th>模块名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时()</th><th>标准差()</th></tr>
</thead>
<tbody>
{module_rows}
</tbody>
</table>
<div class=\"table-wrap\">
<table>
<thead>
<tr><th>模块名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时()</th><th>标准差()</th></tr>
</thead>
<tbody>
{module_rows}
</tbody>
</table>
</div>
<h2>按请求类型分类统计</h2>
<table>
<thead>
<tr><th>请求类型</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时()</th><th>标准差()</th></tr>
</thead>
<tbody>
{type_rows}
</tbody>
</table>
<div class=\"table-wrap\">
<table>
<thead>
<tr><th>请求类型</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时()</th><th>标准差()</th></tr>
</thead>
<tbody>
{type_rows}
</tbody>
</table>
</div>
<h2>聊天消息统计</h2>
<table>
<thead>
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
</thead>
<tbody>
{chat_rows_html}
</tbody>
</table>
<div class=\"table-wrap\">
<table>
<thead>
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
</thead>
<tbody>
{chat_rows_html}
</tbody>
</table>
</div>
<h2>数据分布图表</h2>
<div style="display: flex; flex-wrap: wrap; gap: 20px; margin-top: 20px;">
@ -1085,21 +1107,22 @@ class StatisticOutputTask(AsyncTask):
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f4f7f6;
color: #333;
background-color: #faf7ff;
color: #3a2f57;
line-height: 1.6;
}
.container {
max-width: 900px;
margin: 20px auto;
background-color: #fff;
background-color: #ffffff;
padding: 25px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
border-radius: 10px;
box-shadow: 0 10px 28px rgba(122, 98, 182, 0.12);
border: 1px solid #e5dcff;
}
h1, h2 {
color: #2c3e50;
border-bottom: 2px solid #3498db;
color: #473673;
border-bottom: 2px solid #9f8efb;
padding-bottom: 10px;
margin-top: 0;
}
@ -1115,33 +1138,62 @@ class StatisticOutputTask(AsyncTask):
margin-bottom: 10px;
}
.info-item {
background-color: #ecf0f1;
background-color: #f3eeff;
padding: 8px 12px;
border-radius: 4px;
border-radius: 6px;
margin-bottom: 8px;
font-size: 0.95em;
}
.info-item strong {
color: #2980b9;
color: #7162bf;
}
/* 新增顶部工具条与按钮 */
.toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.toolbar .right { display: flex; gap: 8px; align-items: center; }
.btn {
border: 1px solid #e3daff;
background-color: #fbf9ff;
color: #4a3c75;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
transition: all .2s ease;
}
.btn:hover { border-color: #9f8efb; color: #7c6bcf; background-color: #f1ecff; }
/* 新增KPI 卡片 */
.kpi-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 12px 0 6px; }
.kpi-card {
background: linear-gradient(145deg, #ffffff 0%, #f6f2ff 100%);
border: 1px solid #e3dbff;
border-radius: 10px;
padding: 14px 16px;
box-shadow: 0 6px 16px rgba(113, 98, 191, 0.1);
}
.kpi-title { font-size: 12px; color: #8579a6; letter-spacing: .3px; margin-bottom: 6px; }
.kpi-value { font-size: 20px; font-weight: 700; letter-spacing: .2px; color: #8b5cf6; }
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
font-size: 0.9em;
}
/* 新增表格包裹容器支持横向滚动 */
.table-wrap { width: 100%; overflow-x: auto; border-radius: 6px; }
th, td {
border: 1px solid #ddd;
border: 1px solid #e6ddff;
padding: 10px;
text-align: left;
}
th {
background-color: #3498db;
background-color: #9f8efb;
color: white;
font-weight: bold;
position: sticky;
top: 0;
z-index: 1;
}
tr:nth-child(even) {
background-color: #f9f9f9;
background-color: #f6f1ff;
}
.footer {
text-align: center;
@ -1151,25 +1203,32 @@ class StatisticOutputTask(AsyncTask):
}
.tabs {
overflow: hidden;
background: #ecf0f1;
background: #f9f6ff;
display: flex;
border: 1px solid #e4dcff;
border-radius: 10px;
box-shadow: 0 8px 18px rgba(120, 101, 179, 0.08);
}
.tabs button {
background: inherit; border: none; outline: none;
padding: 14px 16px; cursor: pointer;
transition: 0.3s; font-size: 16px;
padding: 12px 14px; cursor: pointer;
transition: 0.2s; font-size: 15px;
color: #52467a;
}
.tabs button:hover {
background-color: #d4dbdc;
background-color: #efe9ff;
}
.tabs button.active {
background-color: #b3bbbd;
background-color: rgba(159, 142, 251, 0.25);
color: #6253a9;
}
.tab-content {
display: none;
padding: 20px;
background-color: #fff;
border: 1px solid #ccc;
background-color: #fefcff;
border: 1px solid #e4dcff;
border-top: none;
border-radius: 0 0 10px 10px;
}
.tab-content.active {
display: block;
@ -1180,14 +1239,19 @@ class StatisticOutputTask(AsyncTask):
"""
+ f"""
<div class="container">
<h1>MaiBot运行统计报告</h1>
<p class="info-item"><strong>统计截止时间:</strong> {now.strftime("%Y-%m-%d %H:%M:%S")}</p>
<div class="toolbar">
<h1 style="margin: 0;">MaiBot运行统计报告</h1>
<div class="right">
<span class="info-item" style="margin: 0;"><strong>统计截止时间:</strong> {now.strftime("%Y-%m-%d %H:%M:%S")}</span>
</div>
</div>
<div class="tabs">
{joined_tab_list}
</div>
{joined_tab_content}
<div class="footer">Made with by MaiBot 本页会定期自动覆盖生成</div>
</div>
"""
+ """
@ -1321,16 +1385,16 @@ class StatisticOutputTask(AsyncTask):
# 生成不同颜色的调色板
colors = [
"#3498db",
"#e74c3c",
"#2ecc71",
"#f39c12",
"#9b59b6",
"#1abc9c",
"#34495e",
"#e67e22",
"#95a5a6",
"#f1c40f",
"#8b5cf6",
"#9f8efb",
"#b5a6ff",
"#c7bbff",
"#d9ceff",
"#a78bfa",
"#9073d8",
"#bfaefc",
"#cabdfd",
"#e6e0ff",
]
# 默认使用24小时数据生成数据集
@ -1512,7 +1576,7 @@ class StatisticOutputTask(AsyncTask):
function createChart(chartType, data, timeRange) {{
const config = chartConfigs[chartType];
const colors = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c', '#34495e', '#e67e22', '#95a5a6', '#f1c40f'];
const colors = ['#8b5cf6', '#9f8efb', '#b5a6ff', '#c7bbff', '#d9ceff', '#a78bfa', '#9073d8', '#bfaefc', '#cabdfd', '#e6e0ff'];
let datasets = [];