feat: 完善模型用量趋势与全局手账卡片并补齐阶段验收
This commit is contained in:
@@ -6,7 +6,7 @@ import logging
|
||||
import math
|
||||
from contextlib import closing
|
||||
from contextvars import ContextVar
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
from app.database.db import connect
|
||||
@@ -107,8 +107,8 @@ class UsageAttempt:
|
||||
logger.warning("Usage persistence failed; model response remains available")
|
||||
|
||||
|
||||
def aggregate(start, end, provider_id=None, model=None, source=None):
|
||||
query = "SELECT counters_json,completed,capability FROM model_usage WHERE started_at>=? AND started_at<?"
|
||||
def aggregate(start, end, provider_id=None, model=None, source=None, timezone_offset=0):
|
||||
query = "SELECT counters_json,completed,capability,started_at,source FROM model_usage WHERE started_at>=? AND started_at<?"
|
||||
args = [start.astimezone(timezone.utc).isoformat(), end.astimezone(timezone.utc).isoformat()]
|
||||
for column, value in (("provider_id", provider_id), ("model", model), ("source", source)):
|
||||
if value:
|
||||
@@ -117,6 +117,18 @@ def aggregate(start, end, provider_id=None, model=None, source=None):
|
||||
with closing(connection()) as conn:
|
||||
rows = conn.execute(query, args).fetchall()
|
||||
options = conn.execute("SELECT DISTINCT provider_id,model,source FROM model_usage ORDER BY provider_id,model").fetchall()
|
||||
# Calendar buckets use the caller's UTC offset; absent counters remain null.
|
||||
zone = timezone(timedelta(minutes=timezone_offset))
|
||||
first = start.astimezone(zone).date()
|
||||
last = (end - timedelta(microseconds=1)).astimezone(zone).date()
|
||||
days = (last - first).days + 1
|
||||
step = max(1, (days + 89) // 90)
|
||||
series = []
|
||||
for offset in range(0, days, step):
|
||||
date = first + timedelta(days=offset)
|
||||
series.append({"date": date.isoformat(), "end_date": (first + timedelta(days=min(days-1, offset+step-1))).isoformat(),
|
||||
"local": {"requests": 0, "totals": {key: None for key in METRICS}, "coverage": {key: 0 for key in METRICS}},
|
||||
"api": {"requests": 0, "totals": {key: None for key in METRICS}, "coverage": {key: 0 for key in METRICS}}})
|
||||
totals = {key: None for key in METRICS}
|
||||
coverage = {key: 0 for key in METRICS}
|
||||
hits, eligible_input, cache_requests = 0, 0, 0
|
||||
@@ -125,6 +137,13 @@ def aggregate(start, end, provider_id=None, model=None, source=None):
|
||||
if row[2] in {"transcription", "speaker_matching"}:
|
||||
audio_requests += 1
|
||||
counts = json.loads(row[0])
|
||||
date = datetime.fromisoformat(row[3]).astimezone(zone).date()
|
||||
bucket = series[(date - first).days // step][row[4]]
|
||||
bucket['requests'] += 1
|
||||
for key in METRICS:
|
||||
if counts.get(key) is not None:
|
||||
bucket['totals'][key] = (bucket['totals'][key] or 0) + counts[key]
|
||||
bucket['coverage'][key] += 1
|
||||
if counts.get("audio_seconds") is not None:
|
||||
audio_covered += 1
|
||||
audio_seconds = (audio_seconds or 0) + counts["audio_seconds"]
|
||||
@@ -140,4 +159,4 @@ def aggregate(start, end, provider_id=None, model=None, source=None):
|
||||
"complete_requests": sum(row[1] for row in rows), "cache_covered_requests": cache_requests,
|
||||
"cache_hit_rate": hits / eligible_input if eligible_input else None,
|
||||
"options": [dict(row) for row in options], "start": start, "end": end,
|
||||
"scope": "application_observed_usage"}
|
||||
"scope": "application_observed_usage", "series": series, "timezone_offset": timezone_offset}
|
||||
|
||||
Reference in New Issue
Block a user