From 69b7ec5cd0452d5347f0e36cf22838251b26ba9d Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sun, 31 May 2026 08:00:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20add=20user-level=20model=20usage=20char?= =?UTF-8?q?t=20(=E6=88=91=E7=9A=84=E4=BB=8A=E6=97=A5=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=BD=BF=E7=94=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add get_user_today_models() function to load_dashboard.py Shows current user's today model call counts and amounts - Create api/user_today_models.dspy endpoint - Create user_today_models_chart.ui ChartBar widget (30s auto-refresh) - Add '我的今日模型使用' card section to index.ui with refresh button - Register new paths in load_path.py (logined permission) --- dashboard_for_sage/load_dashboard.py | 32 +++++++++++++++ scripts/load_path.py | 2 + wwwroot/api/user_today_models.dspy | 6 +++ wwwroot/index.ui | 59 ++++++++++++++++++++++++++++ wwwroot/user_today_models_chart.ui | 11 ++++++ 5 files changed, 110 insertions(+) create mode 100644 wwwroot/api/user_today_models.dspy create mode 100644 wwwroot/user_today_models_chart.ui diff --git a/dashboard_for_sage/load_dashboard.py b/dashboard_for_sage/load_dashboard.py index 8db936c..71ca46e 100644 --- a/dashboard_for_sage/load_dashboard.py +++ b/dashboard_for_sage/load_dashboard.py @@ -238,6 +238,37 @@ async def get_total_orgs(request): return cnt +async def get_user_today_models(request): + """获取当前用户当天各模型调用次数和金额(用户级监控项)""" + env = request._run_ns + userid = await env.get_user() + if not userid: + return [] + today = env.curDateString() + async with get_sor_context(env, 'sage') as sor: + sql = """ + SELECT + COALESCE(b.name, 'Unknown') as model_name, + COUNT(*) as cnt, + COALESCE(SUM(a.amount), 0) as total_amount + FROM llmusage a + LEFT JOIN llm b ON a.llmid = b.id + WHERE a.use_date = ${today}$ + AND a.userid = ${userid}$ + GROUP BY a.llmid, b.name + ORDER BY cnt DESC + """ + recs = await sor.sqlExe(sql, {'today': today, 'userid': userid}) + result = [] + for r in recs: + result.append({ + 'model_name': r.get('model_name', 'Unknown'), + 'cnt': int(r.get('cnt', 0)), + 'total_amount': round(float(r.get('total_amount', 0)), 4) + }) + return result + + def load_dashboard(): """Register dashboard functions on ServerEnv""" g = ServerEnv() @@ -254,3 +285,4 @@ def load_dashboard(): 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 + g.get_user_today_models = get_user_today_models diff --git a/scripts/load_path.py b/scripts/load_path.py index 1d71437..f51e66a 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -67,9 +67,11 @@ paths = [ # Charts ("/dashboard_for_sage/chart_top_models.ui", "logined"), ("/dashboard_for_sage/top_models_chart.ui", "logined"), + ("/dashboard_for_sage/user_today_models_chart.ui", "logined"), # API endpoints ("/dashboard_for_sage/api/top_models.dspy", "logined"), + ("/dashboard_for_sage/api/user_today_models.dspy", "logined"), ] diff --git a/wwwroot/api/user_today_models.dspy b/wwwroot/api/user_today_models.dspy new file mode 100644 index 0000000..30b7310 --- /dev/null +++ b/wwwroot/api/user_today_models.dspy @@ -0,0 +1,6 @@ +# coding=utf-8 +"""User's today model usage data API for ChartBar""" +import json + +models = await get_user_today_models(request) +return json.dumps(models, ensure_ascii=False, default=str) diff --git a/wwwroot/index.ui b/wwwroot/index.ui index 019c10f..9e7ab27 100644 --- a/wwwroot/index.ui +++ b/wwwroot/index.ui @@ -412,6 +412,65 @@ } ] }, + { + "widgettype": "VBox", + "options": { + "css": "card", + "width": "100%", + "borderRadius": "12px", + "padding": "20px", + "marginTop": "20px" + }, + "subwidgets": [ + { + "widgettype": "HBox", + "options": { + "width": "100%", + "alignItems": "center", + "marginBottom": "16px" + }, + "subwidgets": [ + { + "widgettype": "Title4", + "options": { + "fontWeight": "600", + "otext": "我的今日模型使用", + "i18n": true + } + }, + { + "widgettype": "Filler" + }, + { + "widgettype": "Button", + "options": { + "label": "刷新", + "border": "none", + "borderRadius": "6px", + "padding": "4px 12px", + "fontSize": "12px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "method", + "target": "-@ChartBar", + "method": "render_urldata", + "params": {} + } + ] + } + ] + }, + { + "widgettype": "urlwidget", + "options": { + "url": "{{entire_url('user_today_models_chart.ui')}}" + } + } + ] + }, { "widgettype": "VBox", "options": { diff --git a/wwwroot/user_today_models_chart.ui b/wwwroot/user_today_models_chart.ui new file mode 100644 index 0000000..b0b0d7a --- /dev/null +++ b/wwwroot/user_today_models_chart.ui @@ -0,0 +1,11 @@ +{ + "widgettype": "ChartBar", + "options": { + "height": "280px", + "width": "100%", + "data_url": "{{entire_url('api/user_today_models.dspy')}}", + "nameField": "model_name", + "valueFields": ["cnt", "total_amount"], + "refresh_period": 30 + } +}