diff --git a/dashboard_for_sage/load_dashboard.py b/dashboard_for_sage/load_dashboard.py index 790b905..bef6f17 100644 --- a/dashboard_for_sage/load_dashboard.py +++ b/dashboard_for_sage/load_dashboard.py @@ -70,7 +70,7 @@ async def get_top_models(request): WHERE a.use_date = ${today}$ GROUP BY a.llmid, b.name ORDER BY cnt DESC - LIMIT 3 + LIMIT 5 """ recs = await sor.sqlExe(sql, {'today': today}) result = [] @@ -200,6 +200,59 @@ async def get_top_providers_by_count(request): return result +async def get_top_users_combined(request): + """Top 5 users by amount with count - for combined ChartBar""" + env = request._run_ns + async with get_sor_context(env, 'sage') as sor: + sql = """ + SELECT + COALESCE(b.nick_name, b.username) as user_name, + COUNT(*) as cnt, + COALESCE(SUM(a.amount), 0) as total_amount + FROM llmusage a + LEFT JOIN users b ON a.userid = b.id + GROUP BY a.userid, b.nick_name, b.username + ORDER BY total_amount DESC + LIMIT 5 + """ + recs = await sor.sqlExe(sql, {}) + result = [] + for r in recs: + result.append({ + 'user_name': r.get('user_name', 'Unknown'), + 'cnt': int(r.get('cnt', 0)), + 'total_amount': round(float(r.get('total_amount', 0)), 2) + }) + return result + + +async def get_top_providers_combined(request): + """Top 5 providers by amount with count - for combined ChartBar""" + env = request._run_ns + async with get_sor_context(env, 'sage') as sor: + sql = """ + SELECT + COALESCE(c.orgname, 'Unknown') as provider_name, + COUNT(*) as cnt, + COALESCE(SUM(a.amount), 0) as total_amount + FROM llmusage a + LEFT JOIN llm b ON a.llmid = b.id + LEFT JOIN organization c ON b.providerid = c.id + GROUP BY b.providerid, c.orgname + ORDER BY total_amount DESC + LIMIT 5 + """ + recs = await sor.sqlExe(sql, {}) + result = [] + for r in recs: + result.append({ + 'provider_name': r.get('provider_name', 'Unknown'), + 'cnt': int(r.get('cnt', 0)), + 'total_amount': round(float(r.get('total_amount', 0)), 2) + }) + return result + + async def get_active_users_today(request): """获取今日活跃用户数(今日有llmusage记录的去重用户)""" env = request._run_ns @@ -481,6 +534,8 @@ def load_dashboard(): g.get_top_users_by_count = get_top_users_by_count g.get_top_providers_by_amount = get_top_providers_by_amount g.get_top_providers_by_count = get_top_providers_by_count + g.get_top_users_combined = get_top_users_combined + g.get_top_providers_combined = get_top_providers_combined g.get_active_users_today = get_active_users_today g.get_new_users_month = get_new_users_month g.get_total_orgs = get_total_orgs diff --git a/wwwroot/api/top_providers.dspy b/wwwroot/api/top_providers.dspy new file mode 100644 index 0000000..a63c1fd --- /dev/null +++ b/wwwroot/api/top_providers.dspy @@ -0,0 +1,6 @@ +# coding=utf-8 +"""Top providers data API for ChartBar""" +import json + +providers = await get_top_providers_combined(request) +return json.dumps(providers, ensure_ascii=False, default=str) diff --git a/wwwroot/api/top_users.dspy b/wwwroot/api/top_users.dspy new file mode 100644 index 0000000..2b926ee --- /dev/null +++ b/wwwroot/api/top_users.dspy @@ -0,0 +1,6 @@ +# coding=utf-8 +"""Top users data API for ChartBar""" +import json + +users = await get_top_users_combined(request) +return json.dumps(users, ensure_ascii=False, default=str) diff --git a/wwwroot/chart_top_providers.ui b/wwwroot/chart_top_providers.ui new file mode 100644 index 0000000..1776cf2 --- /dev/null +++ b/wwwroot/chart_top_providers.ui @@ -0,0 +1,9 @@ +{ + "widgettype": "ChartBar", + "options": { + "height": "300px", + "data_url": "api/top_providers.dspy", + "nameField": "provider_name", + "valueFields": ["total_amount", "cnt"] + } +} diff --git a/wwwroot/chart_top_users.ui b/wwwroot/chart_top_users.ui new file mode 100644 index 0000000..6432c84 --- /dev/null +++ b/wwwroot/chart_top_users.ui @@ -0,0 +1,9 @@ +{ + "widgettype": "ChartBar", + "options": { + "height": "300px", + "data_url": "api/top_users.dspy", + "nameField": "user_name", + "valueFields": ["total_amount", "cnt"] + } +} diff --git a/wwwroot/index.ui b/wwwroot/index.ui index afd4c03..2658b8c 100644 --- a/wwwroot/index.ui +++ b/wwwroot/index.ui @@ -175,7 +175,7 @@ "widgettype": "Title4", "options": { "fontWeight": "600", - "otext": "Top 3 模型(今日调用)", + "otext": "热门模型", "i18n": true } }, @@ -372,16 +372,16 @@ "options": { "fontWeight": "600", "marginBottom": "16px", - "otext": "用户消费排行(Top 5)", + "otext": "用户排行", "i18n": true } }, { "widgettype": "RefreshWidget", - "id": "table_top_users", + "id": "chart_top_users", "options": { - "period_seconds": 30, - "url": "{{entire_url('table_top_users.ui')}}" + "period_seconds": 60, + "url": "{{entire_url('chart_top_users.ui')}}" } } ] @@ -401,85 +401,19 @@ "options": { "fontWeight": "600", "marginBottom": "16px", - "otext": "用户调用排行(Top 5)", + "otext": "供应商排行", "i18n": true } }, { "widgettype": "RefreshWidget", - "id": "table_top_users_count", + "id": "chart_top_providers", "options": { - "period_seconds": 30, - "url": "{{entire_url('table_top_users_count.ui')}}" + "period_seconds": 60, + "url": "{{entire_url('chart_top_providers.ui')}}" } } ] - }, - { - "widgettype": "HBox", - "options": { - "width": "100%", - "gap": "20px", - "marginTop": "20px" - }, - "subwidgets": [ - { - "widgettype": "VBox", - "options": { - "css": "card", - "width": "50%", - "borderRadius": "12px", - "padding": "20px" - }, - "subwidgets": [ - { - "widgettype": "Title4", - "options": { - "fontWeight": "600", - "marginBottom": "16px", - "otext": "供应商交易排行(金额 Top 5)", - "i18n": true - } - }, - { - "widgettype": "RefreshWidget", - "id": "table_top_providers_amount", - "options": { - "period_seconds": 30, - "url": "{{entire_url('table_top_providers_amount.ui')}}" - } - } - ] - }, - { - "widgettype": "VBox", - "options": { - "css": "card", - "width": "50%", - "borderRadius": "12px", - "padding": "20px" - }, - "subwidgets": [ - { - "widgettype": "Title4", - "options": { - "fontWeight": "600", - "marginBottom": "16px", - "otext": "供应商调用排行(数量 Top 5)", - "i18n": true - } - }, - { - "widgettype": "RefreshWidget", - "id": "table_top_providers_count", - "options": { - "period_seconds": 30, - "url": "{{entire_url('table_top_providers_count.ui')}}" - } - } - ] - } - ] } {% endif %} ]