feat(usage): 修复缓存命中率计算并新增用户端 API 格式统计

- 新增 input_context_expr() 按 api_format 区分 input_tokens 语义
  (OpenAI/Gemini input_tokens 已含 cache_read,Claude 需额外加上)
- 缓存命中率统一改为基于归一化后的 total_input_context 计算
- 用户 /me/usage 接口新增 summary_by_api_format 后端聚合字段
- 前端 API 格式统计改用后端聚合数据,移除前端逐条记录手动统计
- 提取 formatHitRate 到 utils/format.ts 消除三处重复定义
- 移除 PoolManager 中未使用的 select_key 方法

Co-Authored-By: AAEE86 <ppk0227@hotmail.com>
This commit is contained in:
fawney19
2026-03-14 00:28:28 +08:00
parent e9678ea899
commit bdfe4adc98
14 changed files with 281 additions and 247 deletions

View File

@@ -19,6 +19,7 @@ async def test_get_usage_adapter_uses_coarse_summary_grouping(
count_query.scalar.return_value = 0
query.outerjoin.return_value = query
query.filter.return_value = query
query.group_by.return_value = query
query.with_entities.return_value = count_query
query.options.return_value = query
query.order_by.return_value = query
@@ -36,6 +37,8 @@ async def test_get_usage_adapter_uses_coarse_summary_grouping(
"input_tokens": 10,
"output_tokens": 5,
"total_tokens": 15,
"cache_read_tokens": 5,
"total_input_context": 15,
"total_cost_usd": 1.5,
"actual_total_cost_usd": 1.2,
"success_count": 2,
@@ -49,6 +52,8 @@ async def test_get_usage_adapter_uses_coarse_summary_grouping(
"input_tokens": 999,
"output_tokens": 999,
"total_tokens": 1998,
"cache_read_tokens": 50,
"total_input_context": 1049,
"total_cost_usd": 9.9,
"actual_total_cost_usd": 9.9,
"success_count": 0,
@@ -82,15 +87,20 @@ async def test_get_usage_adapter_uses_coarse_summary_grouping(
"input_tokens": 10,
"output_tokens": 5,
"total_tokens": 15,
"cache_read_tokens": 5,
"cache_hit_rate": 33.33,
"total_cost_usd": 1.5,
}
]
assert result["summary_by_api_format"] == []
assert "total_actual_cost" not in result
assert result["summary_by_provider"] == [
{
"provider": "provider-a",
"requests": 2,
"total_tokens": 15,
"cache_read_tokens": 5,
"cache_hit_rate": 33.33,
"total_cost_usd": 1.5,
"success_rate": 100.0,
"avg_response_time_ms": 500.0,
@@ -109,6 +119,7 @@ async def test_get_usage_adapter_provider_success_rate_uses_success_count(
count_query.scalar.return_value = 0
query.outerjoin.return_value = query
query.filter.return_value = query
query.group_by.return_value = query
query.with_entities.return_value = count_query
query.options.return_value = query
query.order_by.return_value = query
@@ -126,6 +137,8 @@ async def test_get_usage_adapter_provider_success_rate_uses_success_count(
"input_tokens": 30,
"output_tokens": 15,
"total_tokens": 45,
"cache_read_tokens": 15,
"total_input_context": 45,
"total_cost_usd": 4.5,
"actual_total_cost_usd": 4.5,
"success_count": 2,
@@ -139,6 +152,8 @@ async def test_get_usage_adapter_provider_success_rate_uses_success_count(
"input_tokens": 10,
"output_tokens": 5,
"total_tokens": 15,
"cache_read_tokens": 5,
"total_input_context": 15,
"total_cost_usd": 1.5,
"actual_total_cost_usd": 1.5,
"success_count": 0,
@@ -168,6 +183,8 @@ async def test_get_usage_adapter_provider_success_rate_uses_success_count(
"provider": "provider-a",
"requests": 4,
"total_tokens": 60,
"cache_read_tokens": 20,
"cache_hit_rate": 33.33,
"total_cost_usd": 6.0,
"success_rate": 50.0,
"avg_response_time_ms": 300.0,