mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat(usage): 统计表格新增输入/输出与缓存创建token细分展示
- 后端接口(admin/user_me/query)新增 output_tokens、cache_creation_tokens、total_input_context 字段返回 - 前端统计表格(模型/提供商/API格式)合并 Tokens 列为"输入/输出"+"缓存读取/创建"两行紧凑布局 - 合并"缓存Token"和"缓存命中率"为单一"命中率"列,减少表格宽度 - 修正 UsageRecordsTable 缩进及模板格式
This commit is contained in:
@@ -546,6 +546,8 @@ class AdminUsageByModelAdapter(AdminApiAdapter):
|
||||
func.sum(Usage.actual_total_cost_usd).label("actual_cost"),
|
||||
func.sum(Usage.cache_read_input_tokens).label("cache_read_tokens"),
|
||||
func.sum(input_context_expr()).label("total_input_context"),
|
||||
func.sum(Usage.output_tokens).label("output_tokens"),
|
||||
func.sum(Usage.cache_creation_input_tokens).label("cache_creation_tokens"),
|
||||
)
|
||||
# 过滤掉 pending/streaming 状态的请求(尚未完成的请求不应计入统计)
|
||||
query = query.filter(Usage.status.notin_(["pending", "streaming"]))
|
||||
@@ -573,15 +575,28 @@ class AdminUsageByModelAdapter(AdminApiAdapter):
|
||||
"model": model,
|
||||
"request_count": count,
|
||||
"total_tokens": int(tokens or 0),
|
||||
"total_input_context": int(total_input_context or 0),
|
||||
"output_tokens": int(output_tokens or 0),
|
||||
"total_cost": float(cost or 0),
|
||||
"actual_cost": float(actual_cost or 0),
|
||||
"cache_read_tokens": int(cache_read_tokens or 0),
|
||||
"cache_creation_tokens": int(cache_creation_tokens or 0),
|
||||
"cache_hit_rate": _calculate_token_cache_hit_rate(
|
||||
total_input_context=total_input_context,
|
||||
cache_read_tokens=cache_read_tokens,
|
||||
),
|
||||
}
|
||||
for model, count, tokens, cost, actual_cost, cache_read_tokens, total_input_context in stats
|
||||
for (
|
||||
model,
|
||||
count,
|
||||
tokens,
|
||||
cost,
|
||||
actual_cost,
|
||||
cache_read_tokens,
|
||||
total_input_context,
|
||||
output_tokens,
|
||||
cache_creation_tokens,
|
||||
) in stats
|
||||
]
|
||||
|
||||
|
||||
@@ -707,6 +722,8 @@ class AdminUsageByProviderAdapter(AdminApiAdapter):
|
||||
func.avg(Usage.response_time_ms).label("avg_response_time_ms"),
|
||||
func.sum(Usage.cache_read_input_tokens).label("cache_read_tokens"),
|
||||
func.sum(input_context_expr()).label("total_input_context"),
|
||||
func.sum(Usage.output_tokens).label("output_tokens"),
|
||||
func.sum(Usage.cache_creation_input_tokens).label("cache_creation_tokens"),
|
||||
).filter(
|
||||
Usage.provider_id.isnot(None),
|
||||
# 过滤掉 pending/streaming 状态的请求
|
||||
@@ -766,6 +783,10 @@ class AdminUsageByProviderAdapter(AdminApiAdapter):
|
||||
"provider": provider_map.get(provider_id_str, "Unknown"),
|
||||
"request_count": attempt_count, # 尝试次数
|
||||
"total_tokens": int(usage_stat.total_tokens or 0) if usage_stat else 0,
|
||||
"total_input_context": (
|
||||
int(usage_stat.total_input_context or 0) if usage_stat else 0
|
||||
),
|
||||
"output_tokens": int(usage_stat.output_tokens or 0) if usage_stat else 0,
|
||||
"total_cost": float(usage_stat.total_cost or 0) if usage_stat else 0,
|
||||
"actual_cost": float(usage_stat.actual_cost or 0) if usage_stat else 0,
|
||||
"avg_response_time_ms": float(stat.avg_latency_ms or 0),
|
||||
@@ -774,6 +795,9 @@ class AdminUsageByProviderAdapter(AdminApiAdapter):
|
||||
"cache_read_tokens": (
|
||||
int(usage_stat.cache_read_tokens or 0) if usage_stat else 0
|
||||
),
|
||||
"cache_creation_tokens": (
|
||||
int(usage_stat.cache_creation_tokens or 0) if usage_stat else 0
|
||||
),
|
||||
"cache_hit_rate": _calculate_token_cache_hit_rate(
|
||||
total_input_context=(usage_stat.total_input_context if usage_stat else 0),
|
||||
cache_read_tokens=(usage_stat.cache_read_tokens if usage_stat else 0),
|
||||
@@ -811,6 +835,8 @@ class AdminUsageByApiFormatAdapter(AdminApiAdapter):
|
||||
func.avg(Usage.response_time_ms).label("avg_response_time_ms"),
|
||||
func.sum(Usage.cache_read_input_tokens).label("cache_read_tokens"),
|
||||
func.sum(input_context_expr()).label("total_input_context"),
|
||||
func.sum(Usage.output_tokens).label("output_tokens"),
|
||||
func.sum(Usage.cache_creation_input_tokens).label("cache_creation_tokens"),
|
||||
)
|
||||
# 过滤掉 pending/streaming 状态的请求
|
||||
query = query.filter(Usage.status.notin_(["pending", "streaming"]))
|
||||
@@ -843,10 +869,13 @@ class AdminUsageByApiFormatAdapter(AdminApiAdapter):
|
||||
"api_format": api_format or "unknown",
|
||||
"request_count": count,
|
||||
"total_tokens": int(tokens or 0),
|
||||
"total_input_context": int(total_input_context or 0),
|
||||
"output_tokens": int(output_tokens or 0),
|
||||
"total_cost": float(cost or 0),
|
||||
"actual_cost": float(actual_cost or 0),
|
||||
"avg_response_time_ms": float(avg_response_time or 0),
|
||||
"cache_read_tokens": int(cache_read_tokens or 0),
|
||||
"cache_creation_tokens": int(cache_creation_tokens or 0),
|
||||
"cache_hit_rate": _calculate_token_cache_hit_rate(
|
||||
total_input_context=total_input_context,
|
||||
cache_read_tokens=cache_read_tokens,
|
||||
@@ -861,6 +890,8 @@ class AdminUsageByApiFormatAdapter(AdminApiAdapter):
|
||||
avg_response_time,
|
||||
cache_read_tokens,
|
||||
total_input_context,
|
||||
output_tokens,
|
||||
cache_creation_tokens,
|
||||
) in stats
|
||||
]
|
||||
|
||||
|
||||
@@ -1061,6 +1061,7 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
"output_tokens": 0,
|
||||
"total_tokens": 0,
|
||||
"cache_read_tokens": 0,
|
||||
"cache_creation_tokens": 0,
|
||||
"total_input_context": 0,
|
||||
"cache_hit_rate": 0.0,
|
||||
"total_cost_usd": 0.0,
|
||||
@@ -1075,6 +1076,7 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
stats["output_tokens"] += item["output_tokens"]
|
||||
stats["total_tokens"] += item["total_tokens"]
|
||||
stats["cache_read_tokens"] += int(item.get("cache_read_tokens", 0) or 0)
|
||||
stats["cache_creation_tokens"] += int(item.get("cache_creation_tokens", 0) or 0)
|
||||
stats["total_input_context"] += int(item.get("total_input_context", 0) or 0)
|
||||
stats["total_cost_usd"] += item["total_cost_usd"]
|
||||
# 管理员可以看到真实成本
|
||||
@@ -1086,7 +1088,9 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
"provider": provider_name,
|
||||
"requests": 0,
|
||||
"total_tokens": 0,
|
||||
"output_tokens": 0,
|
||||
"cache_read_tokens": 0,
|
||||
"cache_creation_tokens": 0,
|
||||
"total_input_context": 0,
|
||||
"total_cost_usd": 0.0,
|
||||
"success_count": 0,
|
||||
@@ -1096,7 +1100,11 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
provider_stats = provider_summary.setdefault(provider_name, provider_base_stats)
|
||||
provider_stats["requests"] += item["requests"]
|
||||
provider_stats["total_tokens"] += item["total_tokens"]
|
||||
provider_stats["output_tokens"] += item.get("output_tokens", 0) or 0
|
||||
provider_stats["cache_read_tokens"] += int(item.get("cache_read_tokens", 0) or 0)
|
||||
provider_stats["cache_creation_tokens"] += int(
|
||||
item.get("cache_creation_tokens", 0) or 0
|
||||
)
|
||||
provider_stats["total_input_context"] += int(item.get("total_input_context", 0) or 0)
|
||||
provider_stats["total_cost_usd"] += item["total_cost_usd"]
|
||||
provider_stats["success_count"] += int(item.get("success_count", 0) or 0)
|
||||
@@ -1112,7 +1120,6 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
total_input_context=int(model_stats.get("total_input_context", 0) or 0),
|
||||
cache_read_tokens=int(model_stats.get("cache_read_tokens", 0) or 0),
|
||||
)
|
||||
model_stats.pop("total_input_context", None)
|
||||
|
||||
summary_by_model = sorted(model_summary.values(), key=lambda x: x["requests"], reverse=True)
|
||||
summary_by_provider = []
|
||||
@@ -1132,7 +1139,10 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
"provider": provider_stats["provider"],
|
||||
"requests": provider_stats["requests"],
|
||||
"total_tokens": provider_stats["total_tokens"],
|
||||
"total_input_context": provider_stats["total_input_context"],
|
||||
"output_tokens": provider_stats["output_tokens"],
|
||||
"cache_read_tokens": provider_stats["cache_read_tokens"],
|
||||
"cache_creation_tokens": provider_stats["cache_creation_tokens"],
|
||||
"cache_hit_rate": _calculate_token_cache_hit_rate(
|
||||
total_input_context=int(provider_stats.get("total_input_context", 0) or 0),
|
||||
cache_read_tokens=int(provider_stats.get("cache_read_tokens", 0) or 0),
|
||||
@@ -1151,6 +1161,8 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
func.sum(Usage.total_tokens).label("total_tokens"),
|
||||
func.sum(Usage.cache_read_input_tokens).label("cache_read_tokens"),
|
||||
func.sum(input_context_expr()).label("total_input_context"),
|
||||
func.sum(Usage.output_tokens).label("output_tokens"),
|
||||
func.sum(Usage.cache_creation_input_tokens).label("cache_creation_tokens"),
|
||||
func.sum(Usage.total_cost_usd).label("total_cost_usd"),
|
||||
func.avg(Usage.response_time_ms).label("avg_response_time_ms"),
|
||||
).filter(
|
||||
@@ -1171,7 +1183,10 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
"api_format": api_format or "unknown",
|
||||
"request_count": count,
|
||||
"total_tokens": int(total_tokens or 0),
|
||||
"total_input_context": int(total_input_context or 0),
|
||||
"output_tokens": int(output_tokens or 0),
|
||||
"cache_read_tokens": int(cache_read_tokens or 0),
|
||||
"cache_creation_tokens": int(cache_creation_tokens or 0),
|
||||
"cache_hit_rate": _calculate_token_cache_hit_rate(
|
||||
total_input_context=total_input_context,
|
||||
cache_read_tokens=cache_read_tokens,
|
||||
@@ -1185,6 +1200,8 @@ class GetUsageAdapter(AuthenticatedApiAdapter):
|
||||
total_tokens,
|
||||
cache_read_tokens,
|
||||
total_input_context,
|
||||
output_tokens,
|
||||
cache_creation_tokens,
|
||||
total_cost_usd,
|
||||
avg_response_time_ms,
|
||||
) in api_format_stats
|
||||
|
||||
@@ -261,6 +261,7 @@ class UsageQueryMixin:
|
||||
func.sum(Usage.output_tokens).label("output_tokens"),
|
||||
func.sum(Usage.total_tokens).label("total_tokens"),
|
||||
func.sum(Usage.cache_read_input_tokens).label("cache_read_tokens"),
|
||||
func.sum(Usage.cache_creation_input_tokens).label("cache_creation_tokens"),
|
||||
func.sum(input_context_expr()).label("total_input_context"),
|
||||
func.sum(Usage.total_cost_usd).label("total_cost_usd"),
|
||||
func.sum(Usage.actual_total_cost_usd).label("actual_total_cost_usd"),
|
||||
@@ -307,6 +308,7 @@ class UsageQueryMixin:
|
||||
"output_tokens": row.output_tokens,
|
||||
"total_tokens": row.total_tokens,
|
||||
"cache_read_tokens": int(row.cache_read_tokens or 0),
|
||||
"cache_creation_tokens": int(row.cache_creation_tokens or 0),
|
||||
"total_input_context": int(row.total_input_context or 0),
|
||||
"total_cost_usd": float(row.total_cost_usd or 0.0),
|
||||
"actual_total_cost_usd": float(row.actual_total_cost_usd or 0.0),
|
||||
|
||||
Reference in New Issue
Block a user