feat(usage): 改进请求追踪与缓存预热

- 区分请求重试与故障转移:新增 has_retry 标识亲和缓存重试
- 修正候选 TTFB 计算:记录候选自身的首字节时间而非全局时间
- Streaming 状态同步 rate_multiplier,支持按 API 格式配置
- 新增缓存预热服务:启动时预热仪表盘统计、热力图、每日统计
- 优化缓存 TTL 配置:仪表盘统计 2 分钟、热力图和每日统计 10 分钟
- 自动刷新间隔从 10 秒调整为 5 秒
This commit is contained in:
fawney19
2026-01-15 11:45:46 +08:00
parent 24b65d76b5
commit 514bf7e3ed
15 changed files with 386 additions and 39 deletions

View File

@@ -1,5 +1,6 @@
"""管理员使用情况统计路由。"""
from collections import defaultdict
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
@@ -717,19 +718,42 @@ class AdminUsageRecordsAdapter(AdminApiAdapter):
request_ids = [usage.request_id for usage, _, _, _, _ in records if usage.request_id]
fallback_map = {}
retry_map = {}
if request_ids:
# 查询每个请求的候选执行情况
# 只统计实际执行的候选success 或 failed不包括 skipped/pending/available
executed_counts = (
db.query(RequestCandidate.request_id, func.count(RequestCandidate.id))
executed_candidates = (
db.query(
RequestCandidate.request_id,
RequestCandidate.candidate_index,
RequestCandidate.retry_index,
)
.filter(
RequestCandidate.request_id.in_(request_ids),
RequestCandidate.status.in_(["success", "failed"]),
)
.group_by(RequestCandidate.request_id)
.all()
)
# 如果实际执行的候选数 > 1说明发生了 Provider 切换
fallback_map = {req_id: count > 1 for req_id, count in executed_counts}
# 按 request_id 分组分析
request_candidates: dict[str, list[tuple[int, int]]] = defaultdict(list)
for req_id, candidate_idx, retry_idx in executed_candidates:
request_candidates[req_id].append((candidate_idx, retry_idx))
for req_id, candidates in request_candidates.items():
# 提取所有不同的 candidate_index
unique_candidates = set(c[0] for c in candidates)
# 如果有多个不同的 candidate_index说明发生了 FallbackProvider 切换)
fallback_map[req_id] = len(unique_candidates) > 1
# 检查是否有重试:同一个 candidate_index 有多个 retry_index
has_retry = False
for candidate_idx in unique_candidates:
retry_indices = [c[1] for c in candidates if c[0] == candidate_idx]
if len(retry_indices) > 1 or (retry_indices and max(retry_indices) > 0):
has_retry = True
break
retry_map[req_id] = has_retry
context.add_audit_metadata(
action="usage_records",
@@ -809,6 +833,7 @@ class AdminUsageRecordsAdapter(AdminApiAdapter):
"error_message": usage.error_message,
"status": usage.status, # 请求状态: pending, streaming, completed, failed
"has_fallback": fallback_map.get(usage.request_id, False),
"has_retry": retry_map.get(usage.request_id, False),
"api_format": usage.api_format
or (endpoint.api_format if endpoint and endpoint.api_format else None),
"api_key_name": provider_api_key.name if provider_api_key else None,