140 lines
4.9 KiB
Python
140 lines
4.9 KiB
Python
from datetime import date, datetime, timedelta
|
|
from decimal import Decimal
|
|
from typing import Optional
|
|
|
|
from sqlalchemy import select, func, cast, Date
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models import UsageLog, ApiKey
|
|
|
|
|
|
class UsageService:
|
|
|
|
@staticmethod
|
|
async def summary(db: AsyncSession, user_id: str) -> dict:
|
|
today_start = datetime.combine(date.today(), datetime.min.time())
|
|
month_start = today_start.replace(day=1)
|
|
|
|
# Today
|
|
today_row = (await db.execute(
|
|
select(
|
|
func.coalesce(func.sum(UsageLog.total_tokens), 0),
|
|
func.coalesce(func.sum(UsageLog.cost), Decimal("0")),
|
|
).where(UsageLog.user_id == user_id, UsageLog.request_time >= today_start)
|
|
)).one()
|
|
|
|
# This month
|
|
month_row = (await db.execute(
|
|
select(
|
|
func.coalesce(func.sum(UsageLog.total_tokens), 0),
|
|
func.coalesce(func.sum(UsageLog.cost), Decimal("0")),
|
|
func.count(),
|
|
).where(UsageLog.user_id == user_id, UsageLog.request_time >= month_start)
|
|
)).one()
|
|
|
|
return {
|
|
"today_tokens": int(today_row[0]),
|
|
"today_cost": today_row[1],
|
|
"month_tokens": int(month_row[0]),
|
|
"month_cost": month_row[1],
|
|
"total_requests": int(month_row[2]),
|
|
}
|
|
|
|
@staticmethod
|
|
async def daily(
|
|
db: AsyncSession, user_id: str,
|
|
start: Optional[date] = None, end: Optional[date] = None,
|
|
) -> list:
|
|
if not start:
|
|
start = date.today() - timedelta(days=29)
|
|
if not end:
|
|
end = date.today()
|
|
|
|
day_col = cast(UsageLog.request_time, Date).label("day")
|
|
result = await db.execute(
|
|
select(
|
|
day_col,
|
|
func.coalesce(func.sum(UsageLog.total_tokens), 0),
|
|
func.coalesce(func.sum(UsageLog.cost), Decimal("0")),
|
|
func.count(),
|
|
)
|
|
.where(
|
|
UsageLog.user_id == user_id,
|
|
cast(UsageLog.request_time, Date) >= start,
|
|
cast(UsageLog.request_time, Date) <= end,
|
|
)
|
|
.group_by(day_col)
|
|
.order_by(day_col)
|
|
)
|
|
return [
|
|
{"date": row[0], "total_tokens": int(row[1]), "cost": row[2], "requests": int(row[3])}
|
|
for row in result.all()
|
|
]
|
|
|
|
@staticmethod
|
|
async def by_model(db: AsyncSession, user_id: str) -> list:
|
|
result = await db.execute(
|
|
select(
|
|
UsageLog.model,
|
|
func.coalesce(func.sum(UsageLog.total_tokens), 0),
|
|
func.coalesce(func.sum(UsageLog.cost), Decimal("0")),
|
|
func.count(),
|
|
)
|
|
.where(UsageLog.user_id == user_id)
|
|
.group_by(UsageLog.model)
|
|
.order_by(func.sum(UsageLog.cost).desc())
|
|
)
|
|
return [
|
|
{"model": row[0], "total_tokens": int(row[1]), "cost": row[2], "requests": int(row[3])}
|
|
for row in result.all()
|
|
]
|
|
|
|
@staticmethod
|
|
async def by_key(db: AsyncSession, user_id: str) -> list:
|
|
result = await db.execute(
|
|
select(
|
|
UsageLog.key_id,
|
|
ApiKey.name,
|
|
ApiKey.key_prefix,
|
|
ApiKey.key_suffix,
|
|
func.coalesce(func.sum(UsageLog.total_tokens), 0),
|
|
func.coalesce(func.sum(UsageLog.cost), Decimal("0")),
|
|
func.count(),
|
|
)
|
|
.join(ApiKey, UsageLog.key_id == ApiKey.id)
|
|
.where(UsageLog.user_id == user_id)
|
|
.group_by(UsageLog.key_id, ApiKey.name, ApiKey.key_prefix, ApiKey.key_suffix)
|
|
.order_by(func.sum(UsageLog.cost).desc())
|
|
)
|
|
return [
|
|
{
|
|
"key_id": row[0], "key_name": row[1] or "",
|
|
"key_prefix": row[2], "key_suffix": row[3],
|
|
"total_tokens": int(row[4]), "cost": row[5], "requests": int(row[6]),
|
|
}
|
|
for row in result.all()
|
|
]
|
|
|
|
@staticmethod
|
|
async def logs(
|
|
db: AsyncSession, user_id: str,
|
|
page: int = 1, size: int = 20,
|
|
model: Optional[str] = None,
|
|
key_id: Optional[str] = None,
|
|
start: Optional[date] = None,
|
|
end: Optional[date] = None,
|
|
) -> list:
|
|
q = select(UsageLog).where(UsageLog.user_id == user_id)
|
|
if model:
|
|
q = q.where(UsageLog.model == model)
|
|
if key_id:
|
|
q = q.where(UsageLog.key_id == key_id)
|
|
if start:
|
|
q = q.where(UsageLog.request_time >= datetime.combine(start, datetime.min.time()))
|
|
if end:
|
|
q = q.where(UsageLog.request_time < datetime.combine(end + timedelta(days=1), datetime.min.time()))
|
|
|
|
q = q.order_by(UsageLog.request_time.desc()).offset((page - 1) * size).limit(size)
|
|
result = await db.execute(q)
|
|
return result.scalars().all()
|