feat(report): 丰富统计报告,增加多种高级可视化图表
新增了五种高级数据可视化图表,以提供更深入的模型性能和使用情况分析: - **Token使用对比图**: 直观展示各模型的输入与输出Token数量。 - **供应商请求占比图**: 显示不同服务供应商的请求分布。 - **平均响应时间图**: 对比各模型的平均响应速度。 - **模型效率雷达图**: 从多个维度(请求量、TPS、速度、成本、容量)综合评估模型性能。 - **响应时间分布散点图**: 展示每次请求的具体响应时间,分析性能稳定性。 此外,对报告的UI/UX进行了全面优化,包括采用卡片式布局、实现可交互的图例以筛选数据、以及改进图表样式和提示信息,提升了报告的可读性和易用性。
This commit is contained in:
@@ -302,6 +302,13 @@ class StatisticOutputTask(AsyncTask):
|
||||
PIE_CHART_COST_BY_MODULE: {},
|
||||
BAR_CHART_COST_BY_MODEL: {},
|
||||
BAR_CHART_REQ_BY_MODEL: {},
|
||||
BAR_CHART_TOKEN_COMPARISON: {},
|
||||
SCATTER_CHART_RESPONSE_TIME: {},
|
||||
RADAR_CHART_MODEL_EFFICIENCY: {},
|
||||
HEATMAP_CHAT_ACTIVITY: {},
|
||||
DOUGHNUT_CHART_PROVIDER_REQUESTS: {},
|
||||
LINE_CHART_COST_TREND: {},
|
||||
BAR_CHART_AVG_RESPONSE_TIME: {},
|
||||
}
|
||||
for period_key, _ in collect_period
|
||||
}
|
||||
@@ -475,6 +482,91 @@ class StatisticOutputTask(AsyncTask):
|
||||
"labels": [item[0] for item in sorted_models],
|
||||
"data": [round(item[1], 4) for item in sorted_models],
|
||||
}
|
||||
|
||||
# 1. Token输入输出对比条形图
|
||||
model_names = list(period_stats[REQ_CNT_BY_MODEL].keys())
|
||||
if model_names:
|
||||
period_stats[BAR_CHART_TOKEN_COMPARISON] = {
|
||||
"labels": model_names,
|
||||
"input_tokens": [period_stats[IN_TOK_BY_MODEL].get(m, 0) for m in model_names],
|
||||
"output_tokens": [period_stats[OUT_TOK_BY_MODEL].get(m, 0) for m in model_names],
|
||||
}
|
||||
|
||||
# 2. 响应时间分布散点图数据(限制数据点以提高加载速度)
|
||||
scatter_data = []
|
||||
max_points_per_model = 50 # 每个模型最多50个点
|
||||
for model_name, time_costs in period_stats[TIME_COST_BY_MODEL].items():
|
||||
# 如果数据点太多,进行采样
|
||||
if len(time_costs) > max_points_per_model:
|
||||
step = len(time_costs) // max_points_per_model
|
||||
sampled_costs = time_costs[::step][:max_points_per_model]
|
||||
else:
|
||||
sampled_costs = time_costs
|
||||
|
||||
for idx, time_cost in enumerate(sampled_costs):
|
||||
scatter_data.append({
|
||||
"model": model_name,
|
||||
"x": idx,
|
||||
"y": round(time_cost, 3),
|
||||
"tokens": period_stats[TOTAL_TOK_BY_MODEL].get(model_name, 0) // len(time_costs) if time_costs else 0
|
||||
})
|
||||
period_stats[SCATTER_CHART_RESPONSE_TIME] = scatter_data
|
||||
|
||||
# 3. 模型效率雷达图
|
||||
if model_names:
|
||||
# 取前5个最常用的模型
|
||||
top_models = sorted(period_stats[REQ_CNT_BY_MODEL].items(), key=lambda x: x[1], reverse=True)[:5]
|
||||
radar_data = []
|
||||
for model_name, _ in top_models:
|
||||
# 归一化各项指标到0-100
|
||||
req_count = period_stats[REQ_CNT_BY_MODEL].get(model_name, 0)
|
||||
tps = period_stats[TPS_BY_MODEL].get(model_name, 0)
|
||||
avg_time = period_stats[AVG_TIME_COST_BY_MODEL].get(model_name, 0)
|
||||
cost_per_ktok = period_stats[COST_PER_KTOK_BY_MODEL].get(model_name, 0)
|
||||
avg_tokens = period_stats[AVG_TOK_BY_MODEL].get(model_name, 0)
|
||||
|
||||
# 简单的归一化(反向归一化时间和成本,值越小越好)
|
||||
max_req = max([period_stats[REQ_CNT_BY_MODEL].get(m[0], 1) for m in top_models])
|
||||
max_tps = max([period_stats[TPS_BY_MODEL].get(m[0], 1) for m in top_models])
|
||||
max_time = max([period_stats[AVG_TIME_COST_BY_MODEL].get(m[0], 0.1) for m in top_models])
|
||||
max_cost = max([period_stats[COST_PER_KTOK_BY_MODEL].get(m[0], 0.001) for m in top_models])
|
||||
max_tokens = max([period_stats[AVG_TOK_BY_MODEL].get(m[0], 1) for m in top_models])
|
||||
|
||||
radar_data.append({
|
||||
"model": model_name,
|
||||
"metrics": [
|
||||
round((req_count / max_req) * 100, 2) if max_req > 0 else 0, # 请求量
|
||||
round((tps / max_tps) * 100, 2) if max_tps > 0 else 0, # TPS
|
||||
round((1 - avg_time / max_time) * 100, 2) if max_time > 0 else 100, # 速度(反向)
|
||||
round((1 - cost_per_ktok / max_cost) * 100, 2) if max_cost > 0 else 100, # 成本效益(反向)
|
||||
round((avg_tokens / max_tokens) * 100, 2) if max_tokens > 0 else 0, # Token容量
|
||||
]
|
||||
})
|
||||
period_stats[RADAR_CHART_MODEL_EFFICIENCY] = {
|
||||
"labels": ["请求量", "TPS", "响应速度", "成本效益", "Token容量"],
|
||||
"datasets": radar_data
|
||||
}
|
||||
|
||||
# 4. 供应商请求占比环形图
|
||||
provider_requests = period_stats[REQ_CNT_BY_PROVIDER]
|
||||
if provider_requests:
|
||||
sorted_provider_reqs = sorted(provider_requests.items(), key=lambda item: item[1], reverse=True)
|
||||
period_stats[DOUGHNUT_CHART_PROVIDER_REQUESTS] = {
|
||||
"labels": [item[0] for item in sorted_provider_reqs],
|
||||
"data": [item[1] for item in sorted_provider_reqs],
|
||||
}
|
||||
|
||||
# 5. 平均响应时间条形图
|
||||
if model_names:
|
||||
sorted_by_time = sorted(
|
||||
[(m, period_stats[AVG_TIME_COST_BY_MODEL].get(m, 0)) for m in model_names],
|
||||
key=lambda x: x[1],
|
||||
reverse=True
|
||||
)
|
||||
period_stats[BAR_CHART_AVG_RESPONSE_TIME] = {
|
||||
"labels": [item[0] for item in sorted_by_time],
|
||||
"data": [round(item[1], 3) for item in sorted_by_time],
|
||||
}
|
||||
return stats
|
||||
|
||||
@staticmethod
|
||||
|
||||
Reference in New Issue
Block a user