Files
projectAIpopular/router_system/v2stats.py
T

81 lines
3.2 KiB
Python

"""v2 统计聚合器(V2Stats)—— token 计量与账单(纯标准库)。
T9:每请求 API token 记账;聚合快路径命中率、回合数分布、熔断次数、累计 token/成本。
配合 /metrics 对外透出(论文 E1 token 经济学数据来源之一)。
"""
from __future__ import annotations
import threading
from typing import Any, Dict, List, Optional
class V2Stats:
"""线程安全的 v2 运行统计。"""
def __init__(self):
self._lock = threading.Lock()
self._total = 0
self._fast_path = 0
self._breach = 0
self._by_status: Dict[str, int] = {}
self._rounds: List[int] = []
self._api_input_tokens = 0
self._api_output_tokens = 0
self._api_cost_usd = 0.0
self._recent: List[Dict[str, Any]] = []
def record(self, result) -> None:
"""记录一次 PipelineResult。"""
with self._lock:
self._total += 1
if getattr(result, "fast_path", False):
self._fast_path += 1
status = getattr(result, "status", "?")
self._by_status[status] = self._by_status.get(status, 0) + 1
self._rounds.append(getattr(result, "rounds_used", 0))
if "breach" in " ".join(getattr(result, "route", [])):
self._breach += 1
self._api_input_tokens += getattr(result, "api_input_tokens", 0)
self._api_output_tokens += getattr(result, "api_output_tokens", 0)
self._api_cost_usd += getattr(result, "cost_est", 0.0)
self._recent.append({
"request_id": getattr(result, "request_id", ""),
"status": status,
"fast_path": getattr(result, "fast_path", False),
"api_input_tokens": getattr(result, "api_input_tokens", 0),
"api_output_tokens": getattr(result, "api_output_tokens", 0),
"rounds_used": getattr(result, "rounds_used", 0),
})
if len(self._recent) > 200:
self._recent = self._recent[-200:]
def summary(self) -> Dict[str, Any]:
with self._lock:
total = self._total
rounds = self._rounds
return {
"total_requests": total,
"fast_path_rate": round(self._fast_path / total, 4) if total else 0.0,
"status_distribution": dict(self._by_status),
"breach_count": self._breach,
"rounds_used": {
"avg": round(sum(rounds) / len(rounds), 2) if rounds else 0.0,
"max": max(rounds) if rounds else 0,
"distribution": _histogram(rounds),
},
"api_tokens": {
"input": self._api_input_tokens,
"output": self._api_output_tokens,
"total": self._api_input_tokens + self._api_output_tokens,
"cost_est_usd": round(self._api_cost_usd, 6),
},
}
def _histogram(values: List[int], max_bucket: int = 10) -> Dict[str, int]:
out: Dict[str, int] = {}
for v in values:
key = str(v) if v <= max_bucket else f">{max_bucket}"
out[key] = out.get(key, 0) + 1
return out