""" 统一 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]