refactor(config): 移除废弃的数据库配置模块

旧的数据库配置模块 `src/common/database/config` 已被完全移除。该模块已被标记为废弃,其功能已统一整合到 `global_config` 中。

本次重构旨在:
- 简化代码库,消除冗余和过时的文件。
- 统一配置管理,提高可维护性。

此外,对 `AttentionOptimizer` 中的类变量添加了 `ClassVar` 类型注解,以增强代码的清晰度和类型安全性。
This commit is contained in:
minecraft1024a
2025-11-13 12:51:42 +08:00
parent 7dc4be3555
commit e1622ca6be
4 changed files with 837 additions and 164 deletions

833
mofox_bot_statistics.html Normal file
View File

@@ -0,0 +1,833 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MoFox-Bot运行统计报告</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>/* General Body Styles */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #F8F9FA; /* Light grey background */
color: #495057; /* Softer text color */
line-height: 1.6;
}
/* Main Container */
.container {
max-width: 1200px;
margin: 20px auto;
background-color: #FFFFFF; /* Pure white background */
padding: 30px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
border: 1px solid #EAEAEA;
}
/* Dashboard Layout */
.dashboard-layout {
display: flex;
gap: 30px;
}
.main-content {
flex: 65%;
min-width: 0;
}
.sidebar-content {
flex: 35%;
min-width: 0;
}
/* Responsive Design for Mobile */
@media (max-width: 992px) {
.dashboard-layout {
flex-direction: column;
}
.main-content, .sidebar-content {
flex: 1;
}
}
/* Typography */
h1, h2 {
color: #212529;
padding-bottom: 10px;
margin-top: 0;
}
h1 {
text-align: center;
font-size: 2.2em;
margin-bottom: 20px;
color: #4A90E2; /* Main blue for title */
}
h2 {
font-size: 1.5em;
margin-top: 40px;
margin-bottom: 15px;
border-bottom: 2px solid #EAEAEA;
}
/* Info Banners */
.info-item {
background-color: #E9ECEF;
padding: 10px 15px;
border-radius: 6px;
margin-bottom: 20px;
font-size: 0.95em;
border: 1px solid #DEE2E6;
}
.info-item strong {
color: #4A90E2;
}
/* Tabs */
.tabs {
border-bottom: 2px solid #DEE2E6;
display: flex;
margin-bottom: 20px;
}
.tabs button {
background: none;
border: none;
outline: none;
padding: 14px 20px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 16px;
color: #6C757D;
border-bottom: 3px solid transparent;
margin-bottom: -2px; /* Align with container border */
}
.tabs button:hover {
color: #212529;
}
.tabs button.active {
color: #4A90E2;
border-bottom-color: #4A90E2;
}
.tab-content {
display: none;
padding-top: 10px;
}
.tab-content.active {
display: block;
}
/* Summary Cards */
.summary-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin: 20px 0;
}
.card {
background-color: #FFFFFF;
padding: 20px;
border-radius: 8px;
text-align: center;
border: 1px solid #EAEAEA;
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 15px rgba(0,0,0,0.08);
}
.card h3 {
margin: 0 0 10px;
font-size: 1em;
color: #6C757D;
}
.card p {
margin: 0;
font-size: 1.8em;
font-weight: bold;
color: #212529;
}
/* Tables */
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
font-size: 0.9em;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #EAEAEA;
}
th {
background-color: #4A90E2;
color: white;
font-weight: bold;
font-size: 0.95em;
text-transform: uppercase;
letter-spacing: 0.5px;
}
tr:nth-child(even) {
background-color: #F8F9FA;
}
tr:hover {
background-color: #E9ECEF;
}
/* Chart Container in Sidebar */
.chart-container {
position: relative;
height: 300px; /* Adjust height as needed */
width: 100%;
margin-bottom: 20px;
}
/* Footer */
.footer {
text-align: center;
margin-top: 40px;
font-size: 0.85em;
color: #6C757D;
}
</style>
</head>
<body>
<div class="container">
<h1>MoFox-Bot运行统计报告</h1>
<p class="info-item"><strong>统计截止时间:</strong> 2025-11-13 12:50:57</p>
<div class="tabs"><button class="tab-link" onclick="showTab(event, 'last_7_days')">最近7天</button>
<button class="tab-link" onclick="showTab(event, 'last_24_hours')">最近24小时</button>
<button class="tab-link" onclick="showTab(event, 'last_3_hours')">最近3小时</button>
<button class="tab-link" onclick="showTab(event, 'last_hour')">最近1小时</button>
<button class="tab-link" onclick="showTab(event, 'all_time')">自部署以来的</button>
<button class="tab-link" onclick="showTab(event, 'charts')">数据图表</button></div>
<div id="last_7_days" class="tab-content">
<div class="dashboard-layout">
<div class="main-content">
<p class="info-item">
<strong>统计时段: </strong>
2025-11-06 12:50:57 ~ 2025-11-13 12:50:57
</p>
<div class="summary-cards">
<div class="card">
<h3>总花费</h3>
<p>0.0000 ¥</p>
</div>
<div class="card">
<h3>总请求数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总Token数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总消息数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总在线时间</h3>
<p>4小时31分钟40秒</p>
</div>
</div>
<h2>按模型分类统计</h2>
<table>
<tr><th>模型名称</th><th>调用次数</th><th>平均Token数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th><th>平均耗时(秒)</th></tr>
<tbody></tbody>
</table>
<h2>按供应商分类统计</h2>
<table>
<tr><th>供应商名称</th><th>调用次数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th></tr>
<tbody></tbody>
</table>
<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></tbody>
</table>
<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></tbody>
</table>
<h2>聊天消息统计</h2>
<table>
<thead>
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="sidebar-content">
<h2>数据总览</h2>
<div class="chart-grid">
<div class="chart-container">
<canvas id="providerCostPieChart_last_7_days"></canvas>
</div>
<div class="chart-container">
<canvas id="modelCostBarChart_last_7_days"></canvas>
</div>
</div>
</div>
</div>
</div>
<div id="last_24_hours" class="tab-content">
<div class="dashboard-layout">
<div class="main-content">
<p class="info-item">
<strong>统计时段: </strong>
2025-11-12 12:50:57 ~ 2025-11-13 12:50:57
</p>
<div class="summary-cards">
<div class="card">
<h3>总花费</h3>
<p>0.0000 ¥</p>
</div>
<div class="card">
<h3>总请求数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总Token数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总消息数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总在线时间</h3>
<p>30分钟8秒</p>
</div>
</div>
<h2>按模型分类统计</h2>
<table>
<tr><th>模型名称</th><th>调用次数</th><th>平均Token数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th><th>平均耗时(秒)</th></tr>
<tbody></tbody>
</table>
<h2>按供应商分类统计</h2>
<table>
<tr><th>供应商名称</th><th>调用次数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th></tr>
<tbody></tbody>
</table>
<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></tbody>
</table>
<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></tbody>
</table>
<h2>聊天消息统计</h2>
<table>
<thead>
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="sidebar-content">
<h2>数据总览</h2>
<div class="chart-grid">
<div class="chart-container">
<canvas id="providerCostPieChart_last_24_hours"></canvas>
</div>
<div class="chart-container">
<canvas id="modelCostBarChart_last_24_hours"></canvas>
</div>
</div>
</div>
</div>
</div>
<div id="last_3_hours" class="tab-content">
<div class="dashboard-layout">
<div class="main-content">
<p class="info-item">
<strong>统计时段: </strong>
2025-11-13 09:50:57 ~ 2025-11-13 12:50:57
</p>
<div class="summary-cards">
<div class="card">
<h3>总花费</h3>
<p>0.0000 ¥</p>
</div>
<div class="card">
<h3>总请求数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总Token数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总消息数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总在线时间</h3>
<p>17分钟39秒</p>
</div>
</div>
<h2>按模型分类统计</h2>
<table>
<tr><th>模型名称</th><th>调用次数</th><th>平均Token数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th><th>平均耗时(秒)</th></tr>
<tbody></tbody>
</table>
<h2>按供应商分类统计</h2>
<table>
<tr><th>供应商名称</th><th>调用次数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th></tr>
<tbody></tbody>
</table>
<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></tbody>
</table>
<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></tbody>
</table>
<h2>聊天消息统计</h2>
<table>
<thead>
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="sidebar-content">
<h2>数据总览</h2>
<div class="chart-grid">
<div class="chart-container">
<canvas id="providerCostPieChart_last_3_hours"></canvas>
</div>
<div class="chart-container">
<canvas id="modelCostBarChart_last_3_hours"></canvas>
</div>
</div>
</div>
</div>
</div>
<div id="last_hour" class="tab-content">
<div class="dashboard-layout">
<div class="main-content">
<p class="info-item">
<strong>统计时段: </strong>
2025-11-13 11:50:57 ~ 2025-11-13 12:50:57
</p>
<div class="summary-cards">
<div class="card">
<h3>总花费</h3>
<p>0.0000 ¥</p>
</div>
<div class="card">
<h3>总请求数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总Token数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总消息数</h3>
<p>0</p>
</div>
<div class="card">
<h3>总在线时间</h3>
<p>13分钟56秒</p>
</div>
</div>
<h2>按模型分类统计</h2>
<table>
<tr><th>模型名称</th><th>调用次数</th><th>平均Token数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th><th>平均耗时(秒)</th></tr>
<tbody></tbody>
</table>
<h2>按供应商分类统计</h2>
<table>
<tr><th>供应商名称</th><th>调用次数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th></tr>
<tbody></tbody>
</table>
<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></tbody>
</table>
<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></tbody>
</table>
<h2>聊天消息统计</h2>
<table>
<thead>
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="sidebar-content">
<h2>数据总览</h2>
<div class="chart-grid">
<div class="chart-container">
<canvas id="providerCostPieChart_last_hour"></canvas>
</div>
<div class="chart-container">
<canvas id="modelCostBarChart_last_hour"></canvas>
</div>
</div>
</div>
</div>
</div>
<div id="all_time" class="tab-content">
<div class="dashboard-layout">
<div class="main-content">
<p class="info-item">
<strong>统计时段: </strong>
2025-10-02 21:26:50 ~ 2025-11-13 12:50:57
</p>
<div class="summary-cards">
<div class="card">
<h3>总花费</h3>
<p>2234.0000 ¥</p>
</div>
<div class="card">
<h3>总请求数</h3>
<p>2234</p>
</div>
<div class="card">
<h3>总Token数</h3>
<p>1218356</p>
</div>
<div class="card">
<h3>总消息数</h3>
<p>994</p>
</div>
<div class="card">
<h3>总在线时间</h3>
<p>4小时57分钟30秒</p>
</div>
</div>
<h2>按模型分类统计</h2>
<table>
<tr><th>模型名称</th><th>调用次数</th><th>平均Token数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th><th>平均耗时(秒)</th></tr>
<tbody><tr><td>BAAI/bge-m3</td><td>1121</td><td>0</td><td>48256</td><td>0.00</td><td>0.0000 ¥</td><td>1121.0000 ¥</td><td>78.608 秒</td></tr>
<tr><td>Qwen/Qwen2.5-VL-72B-Instruct</td><td>50</td><td>0</td><td>66700</td><td>0.00</td><td>0.0000 ¥</td><td>50.0000 ¥</td><td>366.725 秒</td></tr>
<tr><td>Qwen/Qwen3-14B</td><td>70</td><td>0</td><td>90191</td><td>0.00</td><td>0.0000 ¥</td><td>70.0000 ¥</td><td>31.441 秒</td></tr>
<tr><td>Qwen/Qwen3-30B-A3B</td><td>453</td><td>0</td><td>281944</td><td>0.00</td><td>0.0000 ¥</td><td>453.0000 ¥</td><td>75.589 秒</td></tr>
<tr><td>Qwen/Qwen3-8B</td><td>9</td><td>0</td><td>6682</td><td>0.00</td><td>0.0000 ¥</td><td>9.0000 ¥</td><td>8.275 秒</td></tr>
<tr><td>deepseek-ai/DeepSeek-R1-Distill-Qwen-32B</td><td>21</td><td>0</td><td>11811</td><td>0.00</td><td>0.0000 ¥</td><td>21.0000 ¥</td><td>11.524 秒</td></tr>
<tr><td>deepseek-ai/DeepSeek-V3.1-Terminus</td><td>428</td><td>0</td><td>623224</td><td>0.00</td><td>0.0000 ¥</td><td>428.0000 ¥</td><td>2324.821 秒</td></tr>
<tr><td>deepseek-ai/DeepSeek-V3.2-Exp</td><td>3</td><td>0</td><td>4267</td><td>0.00</td><td>0.0000 ¥</td><td>3.0000 ¥</td><td>80.927 秒</td></tr>
<tr><td>gemini-2.5-pro</td><td>69</td><td>0</td><td>68195</td><td>0.00</td><td>0.0000 ¥</td><td>69.0000 ¥</td><td>898.429 秒</td></tr>
<tr><td>moonshotai/Kimi-K2-Instruct</td><td>10</td><td>0</td><td>17086</td><td>0.00</td><td>0.0000 ¥</td><td>10.0000 ¥</td><td>35.644 秒</td></tr></tbody>
</table>
<h2>按供应商分类统计</h2>
<table>
<tr><th>供应商名称</th><th>调用次数</th><th>Token总量</th><th>TPS</th><th>每K Token成本</th><th>累计花费</th></tr>
<tbody></tbody>
</table>
<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><tr><td>action</td><td>372</td><td>210218</td><td>372</td><td>210590</td><td>372.0000 ¥</td><td>61.562 秒</td><td>28.224 秒</td></tr>
<tr><td>chat</td><td>16</td><td>32338</td><td>149</td><td>32487</td><td>16.0000 ¥</td><td>347.109 秒</td><td>1.013 秒</td></tr>
<tr><td>chat_stream_impression_update</td><td>4</td><td>2504</td><td>926</td><td>3430</td><td>4.0000 ¥</td><td>38.162 秒</td><td>0.000 秒</td></tr>
<tr><td>embedding</td><td>356</td><td>31947</td><td>0</td><td>31947</td><td>356.0000 ¥</td><td>71.454 秒</td><td>12.687 秒</td></tr>
<tr><td>emoji</td><td>23</td><td>8057</td><td>744</td><td>8801</td><td>23.0000 ¥</td><td>248.367 秒</td><td>3.224 秒</td></tr>
<tr><td>expressor</td><td>57</td><td>46135</td><td>168</td><td>46303</td><td>57.0000 ¥</td><td>453.845 秒</td><td>282.887 秒</td></tr>
<tr><td>image</td><td>41</td><td>52607</td><td>6169</td><td>58776</td><td>41.0000 ¥</td><td>268.468 秒</td><td>31.588 秒</td></tr>
<tr><td>individuality</td><td>1</td><td>140</td><td>23</td><td>163</td><td>1.0000 ¥</td><td>1.808 秒</td><td>0.000 秒</td></tr>
<tr><td>interest_embedding</td><td>765</td><td>16309</td><td>0</td><td>16309</td><td>765.0000 ¥</td><td>30.717 秒</td><td>6.880 秒</td></tr>
<tr><td>interest_generation</td><td>2</td><td>965</td><td>316</td><td>1281</td><td>2.0000 ¥</td><td>50.171 秒</td><td>0.000 秒</td></tr>
<tr><td>memory</td><td>139</td><td>177182</td><td>31665</td><td>208847</td><td>139.0000 ¥</td><td>1306.658 秒</td><td>45.801 秒</td></tr>
<tr><td>monthly_plan</td><td>2</td><td>740</td><td>353</td><td>1093</td><td>2.0000 ¥</td><td>10.196 秒</td><td>0.000 秒</td></tr>
<tr><td>mood</td><td>126</td><td>40413</td><td>1621</td><td>42034</td><td>126.0000 ¥</td><td>585.337 秒</td><td>298.293 秒</td></tr>
<tr><td>planner</td><td>61</td><td>157672</td><td>10085</td><td>167757</td><td>61.0000 ¥</td><td>310.203 秒</td><td>18.367 秒</td></tr>
<tr><td>plugin</td><td>156</td><td>248583</td><td>12858</td><td>261441</td><td>156.0000 ¥</td><td>576.318 秒</td><td>112.317 秒</td></tr>
<tr><td>relationship_tracker</td><td>1</td><td>995</td><td>232</td><td>1227</td><td>1.0000 ¥</td><td>17.689 秒</td><td>0.000 秒</td></tr>
<tr><td>schedule</td><td>15</td><td>8360</td><td>9461</td><td>17821</td><td>15.0000 ¥</td><td>388.476 秒</td><td>0.000 秒</td></tr>
<tr><td>tool_executor</td><td>70</td><td>88858</td><td>1333</td><td>90191</td><td>70.0000 ¥</td><td>31.441 秒</td><td>4.774 秒</td></tr>
<tr><td>unknown</td><td>21</td><td>4435</td><td>7376</td><td>11811</td><td>21.0000 ¥</td><td>11.524 秒</td><td>3.068 秒</td></tr>
<tr><td>video_analysis</td><td>6</td><td>3306</td><td>2741</td><td>6047</td><td>6.0000 ¥</td><td>18.737 秒</td><td>3.772 秒</td></tr></tbody>
</table>
<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><tr><td>action.judge</td><td>372</td><td>210218</td><td>372</td><td>210590</td><td>372.0000 ¥</td><td>61.562 秒</td><td>28.224 秒</td></tr>
<tr><td>chat.replyer</td><td>16</td><td>32338</td><td>149</td><td>32487</td><td>16.0000 ¥</td><td>347.109 秒</td><td>1.013 秒</td></tr>
<tr><td>chat_stream_impression_update</td><td>4</td><td>2504</td><td>926</td><td>3430</td><td>4.0000 ¥</td><td>38.162 秒</td><td>0.000 秒</td></tr>
<tr><td>embedding</td><td>356</td><td>31947</td><td>0</td><td>31947</td><td>356.0000 ¥</td><td>71.454 秒</td><td>12.687 秒</td></tr>
<tr><td>emoji</td><td>23</td><td>8057</td><td>744</td><td>8801</td><td>23.0000 ¥</td><td>248.367 秒</td><td>3.224 秒</td></tr>
<tr><td>expressor.learner</td><td>57</td><td>46135</td><td>168</td><td>46303</td><td>57.0000 ¥</td><td>453.845 秒</td><td>282.887 秒</td></tr>
<tr><td>image</td><td>41</td><td>52607</td><td>6169</td><td>58776</td><td>41.0000 ¥</td><td>268.468 秒</td><td>31.588 秒</td></tr>
<tr><td>individuality.compress</td><td>1</td><td>140</td><td>23</td><td>163</td><td>1.0000 ¥</td><td>1.808 秒</td><td>0.000 秒</td></tr>
<tr><td>interest_embedding</td><td>765</td><td>16309</td><td>0</td><td>16309</td><td>765.0000 ¥</td><td>30.717 秒</td><td>6.880 秒</td></tr>
<tr><td>interest_generation</td><td>2</td><td>965</td><td>316</td><td>1281</td><td>2.0000 ¥</td><td>50.171 秒</td><td>0.000 秒</td></tr>
<tr><td>memory.extraction</td><td>55</td><td>115032</td><td>22684</td><td>137716</td><td>55.0000 ¥</td><td>1287.156 秒</td><td>0.000 秒</td></tr>
<tr><td>memory.query_planner</td><td>72</td><td>47645</td><td>7262</td><td>54907</td><td>72.0000 ¥</td><td>75.871 秒</td><td>13.881 秒</td></tr>
<tr><td>memory.value_assessment</td><td>12</td><td>14505</td><td>1719</td><td>16224</td><td>12.0000 ¥</td><td>43.272 秒</td><td>0.136 秒</td></tr>
<tr><td>monthly_plan</td><td>2</td><td>740</td><td>353</td><td>1093</td><td>2.0000 ¥</td><td>10.196 秒</td><td>0.000 秒</td></tr>
<tr><td>mood</td><td>126</td><td>40413</td><td>1621</td><td>42034</td><td>126.0000 ¥</td><td>585.337 秒</td><td>298.293 秒</td></tr>
<tr><td>planner</td><td>61</td><td>157672</td><td>10085</td><td>167757</td><td>61.0000 ¥</td><td>310.203 秒</td><td>18.367 秒</td></tr>
<tr><td>plugin.generate</td><td>150</td><td>241689</td><td>12847</td><td>254536</td><td>150.0000 ¥</td><td>572.632 秒</td><td>112.215 秒</td></tr>
<tr><td>plugin.set_emoji_like.select_emoji</td><td>6</td><td>6894</td><td>11</td><td>6905</td><td>6.0000 ¥</td><td>3.686 秒</td><td>0.102 秒</td></tr>
<tr><td>relationship_tracker</td><td>1</td><td>995</td><td>232</td><td>1227</td><td>1.0000 ¥</td><td>17.689 秒</td><td>0.000 秒</td></tr>
<tr><td>schedule</td><td>15</td><td>8360</td><td>9461</td><td>17821</td><td>15.0000 ¥</td><td>388.476 秒</td><td>0.000 秒</td></tr>
<tr><td>tool_executor</td><td>70</td><td>88858</td><td>1333</td><td>90191</td><td>70.0000 ¥</td><td>31.441 秒</td><td>4.774 秒</td></tr>
<tr><td>unknown</td><td>21</td><td>4435</td><td>7376</td><td>11811</td><td>21.0000 ¥</td><td>11.524 秒</td><td>3.068 秒</td></tr>
<tr><td>video_analysis</td><td>6</td><td>3306</td><td>2741</td><td>6047</td><td>6.0000 ¥</td><td>18.737 秒</td><td>3.772 秒</td></tr></tbody>
</table>
<h2>聊天消息统计</h2>
<table>
<thead>
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
</thead>
<tbody><tr><td>MaiCore答疑群(尊王攘夷)</td><td>50</td></tr>
<tr><td>因为没有群了所以只能拿这个测试了</td><td>331</td></tr>
<tr><td>墨狐狐🌟起源之地</td><td>590</td></tr>
<tr><td>亚马逊雨林,一处任何人也找不到的角落(</td><td>4</td></tr>
<tr><td>墨狐</td><td>5</td></tr>
<tr><td>一闪</td><td>14</td></tr></tbody>
</table>
</div>
<div class="sidebar-content">
<h2>数据总览</h2>
<div class="chart-grid">
<div class="chart-container">
<canvas id="providerCostPieChart_all_time"></canvas>
</div>
<div class="chart-container">
<canvas id="modelCostBarChart_all_time"></canvas>
</div>
</div>
</div>
</div>
</div>
<div id="charts" class="tab-content">
<h2>数据图表</h2>
<div style="margin: 20px 0; text-align: center;">
<label style="margin-right: 10px; font-weight: bold;">时间范围:</label>
<button class="time-range-btn" onclick="switchTimeRange('6h')">6小时</button>
<button class="time-range-btn" onclick="switchTimeRange('12h')">12小时</button>
<button class="time-range-btn" onclick="switchTimeRange('24h')">24小时</button>
<button class="time-range-btn" onclick="switchTimeRange('48h')">48小时</button>
</div>
<div style="margin-top: 20px;">
<div style="margin-bottom: 40px;"><canvas id="totalCostChart" width="800" height="400"></canvas></div>
<div style="margin-bottom: 40px;"><canvas id="costByModuleChart" width="800" height="400"></canvas></div>
<div style="margin-bottom: 40px;"><canvas id="costByModelChart" width="800" height="400"></canvas></div>
<div><canvas id="messageByChatChart" width="800" height="400"></canvas></div>
</div>
</div>
</div>
<script>let i, tab_content, tab_links;
tab_content = document.getElementsByClassName("tab-content");
tab_links = document.getElementsByClassName("tab-link");
if (tab_content.length > 0) tab_content[0].classList.add("active");
if (tab_links.length > 0) tab_links[0].classList.add("active");
function showTab(evt, tabName) {
for (i = 0; i < tab_content.length; i++) tab_content[i].classList.remove("active");
for (i = 0; i < tab_links.length; i++) tab_links[i].classList.remove("active");
document.getElementById(tabName).classList.add("active");
evt.currentTarget.classList.add("active");
}
document.addEventListener('DOMContentLoaded', function () {
// This is a placeholder for chart data which will be injected by python.
const allChartData = JSON.parse('{{ all_chart_data }}')
;
let currentCharts = {};
const chartConfigs = {
totalCost: { id: 'totalCostChart', title: '总花费', yAxisLabel: '花费 (¥)', dataKey: 'total_cost_data', fill: true },
costByModule: { id: 'costByModuleChart', title: '各模块花费', yAxisLabel: '花费 (¥)', dataKey: 'cost_by_module', fill: false },
costByModel: { id: 'costByModelChart', title: '各模型花费', yAxisLabel: '花费 (¥)', dataKey: 'cost_by_model', fill: false },
messageByChat: { id: 'messageByChatChart', title: '各聊天流消息数', yAxisLabel: '消息数', dataKey: 'message_by_chat', fill: false }
};
window.switchTimeRange = function(timeRange) {
document.querySelectorAll('.time-range-btn').forEach(btn => btn.classList.remove('active'));
event.target.classList.add('active');
updateAllCharts(allChartData[timeRange], timeRange);
}
function updateAllCharts(data, timeRange) {
Object.values(currentCharts).forEach(chart => chart && chart.destroy());
currentCharts = {};
Object.keys(chartConfigs).forEach(type => createChart(type, data, timeRange));
}
function createChart(chartType, data, timeRange) {
const config = chartConfigs[chartType];
if (!data || !data[config.dataKey]) return;
const colors = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c', '#34495e', '#e67e22', '#95a5a6', '#f1c40f'];
let datasets = [];
if (chartType === 'totalCost') {
datasets = [{ label: config.title, data: data[config.dataKey], borderColor: colors[0], backgroundColor: 'rgba(52, 152, 219, 0.1)', tension: 0.4, fill: config.fill }];
} else {
let i = 0;
Object.entries(data[config.dataKey]).forEach(([name, chartData]) => {
datasets.push({ label: name, data: chartData, borderColor: colors[i % colors.length], backgroundColor: colors[i % colors.length] + '20', tension: 0.4, fill: config.fill });
i++;
});
}
currentCharts[chartType] = new Chart(document.getElementById(config.id), {
type: 'line',
data: { labels: data.time_labels, datasets: datasets },
options: {
responsive: true,
plugins: { title: { display: true, text: `${timeRange}${config.title}趋势`, font: { size: 16 } }, legend: { display: chartType !== 'totalCost', position: 'top' } },
scales: { x: { title: { display: true, text: '时间' }, ticks: { maxTicksLimit: 12 } }, y: { title: { display: true, text: config.yAxisLabel }, beginAtZero: true } },
interaction: { intersect: false, mode: 'index' }
}
});
}
if (allChartData['24h']) {
updateAllCharts(allChartData['24h'], '24h');
// Activate the 24h button by default
document.querySelectorAll('.time-range-btn').forEach(btn => {
if (btn.textContent.includes('24小时')) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
}
// Static charts
const staticChartData = JSON.parse('{{ static_chart_data }}')
;
Object.keys(staticChartData).forEach(period_id => {
const providerCostData = staticChartData[period_id].provider_cost_data;
const modelCostData = staticChartData[period_id].model_cost_data;
const colors = ['#3498db', '#2ecc71', '#f1c40f', '#e74c3c', '#9b59b6', '#1abc9c', '#34495e', '#e67e22'];
// Provider Cost Pie Chart
const providerCtx = document.getElementById(`providerCostPieChart_${period_id}`);
if (providerCtx && providerCostData && providerCostData.data.length > 0) {
new Chart(providerCtx, {
type: 'pie',
data: {
labels: providerCostData.labels,
datasets: [{
label: '按供应商花费',
data: providerCostData.data,
backgroundColor: colors,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: { display: true, text: '按供应商花费分布', font: { size: 16 } },
legend: { position: 'top' }
}
}
});
}
// Model Cost Bar Chart
const modelCtx = document.getElementById(`modelCostBarChart_${period_id}`);
if (modelCtx && modelCostData && modelCostData.data.length > 0) {
new Chart(modelCtx, {
type: 'bar',
data: {
labels: modelCostData.labels,
datasets: [{
label: '按模型花费',
data: modelCostData.data,
backgroundColor: colors,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: { display: true, text: '按模型花费排行', font: { size: 16 } },
legend: { display: false }
},
scales: {
y: { beginAtZero: true, title: { display: true, text: '花费 (¥)' } }
}
}
});
}
});
});</script>
</body>
</html>

View File

@@ -13,7 +13,7 @@
import hashlib
import random
import re
from typing import Any, Literal
from typing import Any, ClassVar, Literal
from src.common.logger import get_logger
from src.config.config import global_config
@@ -26,7 +26,7 @@ class AttentionOptimizer:
# 可交换的block组定义组内block可以随机排序
# 每个组是一个列表包含可以互换位置的block名称
SWAPPABLE_BLOCK_GROUPS = [
SWAPPABLE_BLOCK_GROUPS:ClassVar = [
# 用户相关信息组(记忆、关系、表达习惯)
["memory_block", "relation_info_block", "expression_habits_block"],
# 上下文增强组(工具、知识、跨群)
@@ -37,7 +37,7 @@ class AttentionOptimizer:
# 语义等价的文本替换模板
# 格式: {原始文本: [替换选项1, 替换选项2, ...]}
SEMANTIC_VARIANTS = {
SEMANTIC_VARIANTS:ClassVar = {
"当前时间": ["当前时间", "现在是", "此时此刻", "时间"],
"最近的系统通知": ["最近的系统通知", "系统通知", "通知消息", "最新通知"],
"聊天历史": ["聊天历史", "对话记录", "历史消息", "之前的对话"],
@@ -125,7 +125,7 @@ class AttentionOptimizer:
for group in self.SWAPPABLE_BLOCK_GROUPS:
# 过滤出实际存在且非空的block
existing_blocks = [
block for block in group if block in context_data and context_data[block]
block for block in group if context_data.get(block)
]
if len(existing_blocks) > 1:

View File

@@ -1,11 +0,0 @@
"""数据库配置层
职责:
- 数据库配置现已集成到全局配置中
- 通过 src.config.config.global_config.database 访问
- 优化参数配置
注意:此模块已废弃,配置已迁移到 global_config
"""
__all__ = []

View File

@@ -1,149 +0,0 @@
"""数据库配置管理
统一管理数据库连接配置
"""
import os
from dataclasses import dataclass
from typing import Any
from urllib.parse import quote_plus
from src.common.logger import get_logger
logger = get_logger("database_config")
@dataclass
class DatabaseConfig:
"""数据库配置"""
# 基础配置
db_type: str # "sqlite" 或 "mysql"
url: str # 数据库连接URL
# 引擎配置
engine_kwargs: dict[str, Any]
# SQLite特定配置
sqlite_path: str | None = None
# MySQL特定配置
mysql_host: str | None = None
mysql_port: int | None = None
mysql_user: str | None = None
mysql_password: str | None = None
mysql_database: str | None = None
mysql_charset: str = "utf8mb4"
mysql_unix_socket: str | None = None
_database_config: DatabaseConfig | None = None
def get_database_config() -> DatabaseConfig:
"""获取数据库配置
从全局配置中读取数据库设置并构建配置对象
"""
global _database_config
if _database_config is not None:
return _database_config
from src.config.config import global_config
config = global_config.database
# 构建数据库URL
if config.database_type == "mysql":
# MySQL配置
encoded_user = quote_plus(config.mysql_user)
encoded_password = quote_plus(config.mysql_password)
if config.mysql_unix_socket:
# Unix socket连接
encoded_socket = quote_plus(config.mysql_unix_socket)
url = (
f"mysql+aiomysql://{encoded_user}:{encoded_password}"
f"@/{config.mysql_database}"
f"?unix_socket={encoded_socket}&charset={config.mysql_charset}"
)
else:
# TCP连接
url = (
f"mysql+aiomysql://{encoded_user}:{encoded_password}"
f"@{config.mysql_host}:{config.mysql_port}/{config.mysql_database}"
f"?charset={config.mysql_charset}"
)
engine_kwargs = {
"echo": False,
"future": True,
"pool_size": config.connection_pool_size,
"max_overflow": config.connection_pool_size * 2,
"pool_timeout": config.connection_timeout,
"pool_recycle": 3600,
"pool_pre_ping": True,
"connect_args": {
"autocommit": config.mysql_autocommit,
"charset": config.mysql_charset,
"connect_timeout": config.connection_timeout,
},
}
_database_config = DatabaseConfig(
db_type="mysql",
url=url,
engine_kwargs=engine_kwargs,
mysql_host=config.mysql_host,
mysql_port=config.mysql_port,
mysql_user=config.mysql_user,
mysql_password=config.mysql_password,
mysql_database=config.mysql_database,
mysql_charset=config.mysql_charset,
mysql_unix_socket=config.mysql_unix_socket,
)
logger.info(
f"MySQL配置已加载: "
f"{config.mysql_user}@{config.mysql_host}:{config.mysql_port}/{config.mysql_database}"
)
else:
# SQLite配置
if not os.path.isabs(config.sqlite_path):
ROOT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", ".."))
db_path = os.path.join(ROOT_PATH, config.sqlite_path)
else:
db_path = config.sqlite_path
# 确保数据库目录存在
os.makedirs(os.path.dirname(db_path), exist_ok=True)
url = f"sqlite+aiosqlite:///{db_path}"
engine_kwargs = {
"echo": False,
"future": True,
"connect_args": {
"check_same_thread": False,
"timeout": 60,
},
}
_database_config = DatabaseConfig(
db_type="sqlite",
url=url,
engine_kwargs=engine_kwargs,
sqlite_path=db_path,
)
logger.info(f"SQLite配置已加载: {db_path}")
return _database_config
def reset_database_config():
"""重置数据库配置(用于测试)"""
global _database_config
_database_config = None