137 lines
5.9 KiB
Python

"""
统一 Dashboard API — 从集市表查询所有运营指标 + 性能指标
替代 dashboard_for_sage 中对 llmusage 的直接查询
"""
from sqlor.dbpools import get_sor_context
from datetime import datetime, timedelta
# ── 统计卡片 ──
async def get_today_usage(env, sor):
"""当天调用总数"""
sql = "SELECT COUNT(*) as cnt FROM dm_model_call_fact WHERE call_date = ${today}$"
recs = await sor.sqlExe(sql, {'today': env.curDateString()})
return int(recs[0].cnt) if recs else 0
async def get_today_amount(env, sor):
"""当天总金额"""
sql = "SELECT COALESCE(SUM(amount), 0) as total FROM dm_model_call_fact WHERE call_date = ${today}$"
recs = await sor.sqlExe(sql, {'today': env.curDateString()})
return round(float(recs[0].total), 2) if recs else 0
async def get_success_rate(env, sor):
"""当天成功率"""
sql = """SELECT
COUNT(*) as total,
SUM(CASE WHEN status='SUCCEEDED' THEN 1 ELSE 0 END) as success
FROM dm_model_call_fact WHERE call_date = ${today}$"""
recs = await sor.sqlExe(sql, {'today': env.curDateString()})
if recs and recs[0].total:
return round(recs[0].success * 100.0 / recs[0].total, 1)
return 0
async def get_fail_count(env, sor):
"""当天失败数"""
sql = "SELECT COUNT(*) as cnt FROM dm_model_call_fact WHERE call_date = ${today}$ AND status = 'FAILED'"
recs = await sor.sqlExe(sql, {'today': env.curDateString()})
return int(recs[0].cnt) if recs else 0
async def get_active_users(env, sor):
"""当天活跃用户数"""
sql = "SELECT COUNT(DISTINCT userid) as cnt FROM dm_model_call_fact WHERE call_date = ${today}$"
recs = await sor.sqlExe(sql, {'today': env.curDateString()})
return int(recs[0].cnt) if recs else 0
# ── 排行榜 ──
async def get_top_models(env, sor, limit=5):
"""当天 Top N 模型(按调用次数)"""
sql = """SELECT model, COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total_amount
FROM dm_model_call_fact WHERE call_date = ${today}$
GROUP BY model ORDER BY cnt DESC LIMIT ${limit}$"""
recs = await sor.sqlExe(sql, {'today': env.curDateString(), 'limit': str(limit)})
return [{'model_name': r.model or 'Unknown', 'cnt': int(r.cnt),
'total_amount': round(float(r.total_amount), 2)} for r in recs]
async def get_top_providers(env, sor, limit=5):
"""当天 Top N 供应商(按金额)"""
sql = """SELECT providerid, COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total_amount
FROM dm_model_call_fact WHERE call_date = ${today}$ AND providerid IS NOT NULL
GROUP BY providerid ORDER BY total_amount DESC LIMIT ${limit}$"""
recs = await sor.sqlExe(sql, {'today': env.curDateString(), 'limit': str(limit)})
return [{'provider_name': r.providerid or 'Unknown', 'cnt': int(r.cnt),
'total_amount': round(float(r.total_amount), 2)} for r in recs]
async def get_top_users(env, sor, limit=5):
"""全量 Top N 用户(按金额)"""
sql = """SELECT userid, COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total_amount
FROM dm_model_call_fact GROUP BY userid ORDER BY total_amount DESC LIMIT ${limit}$"""
recs = await sor.sqlExe(sql, {'limit': str(limit)})
return [{'user_name': r.userid, 'cnt': int(r.cnt),
'total_amount': round(float(r.total_amount), 2)} for r in recs]
# ── 客户维度 ──
async def get_customer_daily_models(env, sor, userorgid):
"""客户当天各模型用量"""
sql = """SELECT model, COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total_amount
FROM dm_model_call_fact WHERE call_date = ${today}$ AND userorgid = ${orgid}$
GROUP BY model ORDER BY cnt DESC"""
recs = await sor.sqlExe(sql, {'today': env.curDateString(), 'orgid': userorgid})
return [{'model_name': r.model or 'Unknown', 'cnt': int(r.cnt),
'total_amount': round(float(r.total_amount), 4)} for r in recs]
async def get_customer_monthly_models(env, sor, userorgid):
"""客户当月各模型用量"""
now = datetime.now()
month_start = now.strftime('%Y-%m-01')
sql = """SELECT model, COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total_amount
FROM dm_model_call_fact WHERE call_date >= ${start}$ AND userorgid = ${orgid}$
GROUP BY model ORDER BY cnt DESC"""
recs = await sor.sqlExe(sql, {'start': month_start, 'orgid': userorgid})
return [{'model_name': r.model or 'Unknown', 'cnt': int(r.cnt),
'total_amount': round(float(r.total_amount), 4)} for r in recs]
async def get_customer_user_today(env, sor, userorgid):
"""客户当天各用户用量"""
sql = """SELECT userid, COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total_amount
FROM dm_model_call_fact WHERE call_date = ${today}$ AND userorgid = ${orgid}$
GROUP BY userid ORDER BY cnt DESC"""
recs = await sor.sqlExe(sql, {'today': env.curDateString(), 'orgid': userorgid})
return [{'user_name': r.userid, 'cnt': int(r.cnt),
'total_amount': round(float(r.total_amount), 4)} for r in recs]
# ── 趋势 ──
async def get_daily_trend(env, sor, days=7):
"""最近 N 天调用量和金额趋势"""
sql = """SELECT call_date, COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total_amount
FROM dm_model_call_fact
WHERE call_date >= ${start}$
GROUP BY call_date ORDER BY call_date"""
start = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
recs = await sor.sqlExe(sql, {'start': start})
return [{'date': r.call_date, 'cnt': int(r.cnt),
'amount': round(float(r.total_amount), 2)} for r in recs]
async def get_hourly_concurrency(env, sor):
"""当天每小时并发(活跃用户)"""
sql = """SELECT call_hour, COUNT(DISTINCT userid) as users, COUNT(*) as calls
FROM dm_model_call_fact WHERE call_date = ${today}$
GROUP BY call_hour ORDER BY call_hour"""
recs = await sor.sqlExe(sql, {'today': env.curDateString()})
return [{'hour': r.call_hour, 'users': int(r.users), 'calls': int(r.calls)} for r in recs]