diff --git a/build.sh b/build.sh index 6f79d83..0a28b99 100644 --- a/build.sh +++ b/build.sh @@ -34,8 +34,8 @@ SAGE_MODULE_WWWROOT="$WWWROOT/$MODULE_NAME" echo "Linking wwwroot..." mkdir -p "$SAGE_MODULE_WWWROOT/api" -# Link all .ui and .js files (must be at wwwroot root per bricks convention) -for f in "$MODULE_WWWROOT"/*.ui "$MODULE_WWWROOT"/*.js; do +# Link all .ui, .js, and .css files (must be at wwwroot root per bricks convention) +for f in "$MODULE_WWWROOT"/*.ui "$MODULE_WWWROOT"/*.js "$MODULE_WWWROOT"/*.css; do [ -f "$f" ] && ln -sf "$f" "$SAGE_MODULE_WWWROOT/" done diff --git a/dashboard_for_sage.egg-info/SOURCES.txt b/dashboard_for_sage.egg-info/SOURCES.txt index fb4cf4e..e4c1c5d 100644 --- a/dashboard_for_sage.egg-info/SOURCES.txt +++ b/dashboard_for_sage.egg-info/SOURCES.txt @@ -2,6 +2,7 @@ README.md pyproject.toml dashboard_for_sage/__init__.py dashboard_for_sage/init.py +dashboard_for_sage/load_dashboard.py dashboard_for_sage.egg-info/PKG-INFO dashboard_for_sage.egg-info/SOURCES.txt dashboard_for_sage.egg-info/dependency_links.txt diff --git a/wwwroot/api/top_models.dspy b/wwwroot/api/top_models.dspy new file mode 100644 index 0000000..e2f61d3 --- /dev/null +++ b/wwwroot/api/top_models.dspy @@ -0,0 +1,6 @@ +# coding=utf-8 +"""Top models data API for ChartBar""" +import json + +models = await get_top_models(request) +print(json.dumps(models)) diff --git a/wwwroot/chart_top_models.ui b/wwwroot/chart_top_models.ui new file mode 100644 index 0000000..99148e1 --- /dev/null +++ b/wwwroot/chart_top_models.ui @@ -0,0 +1,10 @@ +{ + "widgettype": "ChartBar", + "options": { + "height": "280px", + "width": "100%", + "data_url": "{{entire_url('api/top_models.dspy')}}", + "nameField": "model_name", + "valueFields": ["cnt", "total_amount"] + } +} diff --git a/wwwroot/global_menu.ui b/wwwroot/global_menu.ui new file mode 100644 index 0000000..8c6eb97 --- /dev/null +++ b/wwwroot/global_menu.ui @@ -0,0 +1,58 @@ +{% set roles = get_user_roles(get_user()) %} +{ + "widgettype": "Menu", + "id": "global_nav_menu", + "options": { + "width": "100%", + "height": "100%", + "bgcolor": "#111827", + "items": [ + { + "name": "dashboard", + "label": "仪表盘", + "icon": "fa fa-dashboard", + "url": "{{entire_url('index.ui')}}", + "target": "app.sage_main_content" + }, +{% if get_user() %} + { + "name": "llmage", + "label": "LLM 模型管理", + "icon": "fa fa-brain", + "submenu": "{{entire_url('/llmage/menu.ui')}}" + }, + { + "name": "rag", + "label": "知识库管理", + "icon": "fa fa-database", + "url": "{{entire_url('/rag/menu.ui')}}", + "target": "app.sage_main_content" + }, +{% endif %} +{% if 'reseller.operator' in roles or 'owner.superuser' in roles %} + { + "name": "platformbiz", + "label": "平台业务", + "icon": "fa fa-building", + "submenu": "{{entire_url('/platformbiz/menu.ui')}}" + }, +{% endif %} +{% if get_user() %} + { + "name": "rbac", + "label": "用户与权限", + "icon": "fa fa-users", + "submenu": "{{entire_url('/rbac/admin_menu.ui')}}" + }, +{% endif %} + { + "name": "hermes_web_cli", + "label": "AI Agent", + "icon": "fa fa-robot", + "url": "{{entire_url('/hermes-web-cli/index.ui')}}", + "target": "app.sage_main_content" + } + ], + "menuitem_css": "menuitem" + } +} diff --git a/wwwroot/index.ui b/wwwroot/index.ui index 0bcd771..b19f2af 100644 --- a/wwwroot/index.ui +++ b/wwwroot/index.ui @@ -2,97 +2,373 @@ "widgettype": "VBox", "options": { "width": "100%", - "height": "100%", - "padding": "20px", - "bgcolor": "#f0f2f5" + "height": "100%" }, "subwidgets": [ { - "widgettype": "Text", + "widgettype": "HBox", "options": { - "text": "Dashboard", - "fontSize": "24px", - "fontWeight": "bold", - "color": "#333", - "marginBottom": "20px" - } + "width": "100%", + "alignItems": "center", + "marginBottom": "24px" + }, + "subwidgets": [ + { + "widgettype": "Title2", + "options": { + "text": "数据概览", + "color": "#F1F5F9", + "fontWeight": "700" + } + }, + { + "widgettype": "Filler" + }, + { + "widgettype": "Text", + "options": { + "text": "最后更新: {{get_today_usage(request) and request._run_ns.curDateString() or ''}}", + "fontSize": "13px", + "color": "#64748B" + } + } + ] }, { "widgettype": "ResponsableBox", "options": { "gap": "16px", - "minWidth": "250px" + "minWidth": "220px", + "marginBottom": "24px" }, "subwidgets": [ { "widgettype": "RefreshWidget", - "id": "refresh_today_usage", + "id": "stat_today_usage", "options": { - "period_seconds": 10, - "url": "{{entire_url('today_usage.ui')}}" + "period_seconds": 30, + "url": "{{entire_url('stat_today_usage.ui')}}" } }, { "widgettype": "RefreshWidget", - "id": "refresh_today_amount", + "id": "stat_today_amount", "options": { - "period_seconds": 10, - "url": "{{entire_url('today_amount.ui')}}" + "period_seconds": 30, + "url": "{{entire_url('stat_today_amount.ui')}}" } }, { "widgettype": "RefreshWidget", - "id": "refresh_total_users", + "id": "stat_total_users", "options": { - "period_seconds": 10, - "url": "{{entire_url('total_users.ui')}}" + "period_seconds": 60, + "url": "{{entire_url('stat_total_users.ui')}}" } }, { "widgettype": "RefreshWidget", - "id": "refresh_concurrent_users", + "id": "stat_concurrent", "options": { - "period_seconds": 10, - "url": "{{entire_url('concurrent_users.ui')}}" + "period_seconds": 15, + "url": "{{entire_url('stat_concurrent.ui')}}" } }, { "widgettype": "RefreshWidget", - "id": "refresh_accounting_errors", + "id": "stat_errors", "options": { - "period_seconds": 10, - "url": "{{entire_url('accounting_errors.ui')}}" + "period_seconds": 30, + "url": "{{entire_url('stat_errors.ui')}}" } } ] }, + { + "widgettype": "HBox", + "options": { + "width": "100%", + "gap": "20px", + "height": "auto" + }, + "subwidgets": [ + { + "widgettype": "VBox", + "options": { + "width": "60%", + "bgcolor": "#1E293B", + "borderRadius": "12px", + "padding": "20px", + "border": "1px solid #334155" + }, + "subwidgets": [ + { + "widgettype": "HBox", + "options": { + "width": "100%", + "alignItems": "center", + "marginBottom": "16px" + }, + "subwidgets": [ + { + "widgettype": "Title4", + "options": { + "text": "Top 3 模型(今日调用)", + "color": "#F1F5F9", + "fontWeight": "600" + } + }, + { + "widgettype": "Filler" + }, + { + "widgettype": "Button", + "options": { + "label": "刷新", + "bgcolor": "#334155", + "color": "#94A3B8", + "border": "none", + "borderRadius": "6px", + "padding": "4px 12px", + "fontSize": "12px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "method", + "target": "-@RefreshWidget", + "method": "render_urldata", + "params": {} + } + ] + } + ] + }, + { + "widgettype": "RefreshWidget", + "id": "chart_top_models", + "options": { + "period_seconds": 30, + "url": "{{entire_url('chart_top_models.ui')}}" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "width": "40%", + "bgcolor": "#1E293B", + "borderRadius": "12px", + "padding": "20px", + "border": "1px solid #334155" + }, + "subwidgets": [ + { + "widgettype": "Title4", + "options": { + "text": "快捷入口", + "color": "#F1F5F9", + "fontWeight": "600", + "marginBottom": "16px" + } + }, + { + "widgettype": "ResponsableBox", + "options": { + "gap": "12px", + "minWidth": "120px" + }, + "subwidgets": [ + { + "widgettype": "VBox", + "options": { + "bgcolor": "#334155", + "padding": "16px", + "borderRadius": "8px", + "cursor": "pointer", + "textAlign": "center" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.sage_main_content", + "options": { + "url": "{{entire_url('/llmage/llm')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Svg", + "options": { + "svg": "" + } + }, + { + "widgettype": "Text", + "options": { + "text": "模型管理", + "color": "#E2E8F0", + "fontSize": "13px", + "marginTop": "8px" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "bgcolor": "#334155", + "padding": "16px", + "borderRadius": "8px", + "cursor": "pointer", + "textAlign": "center" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.sage_main_content", + "options": { + "url": "{{entire_url('/rbac/users')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Svg", + "options": { + "svg": "" + } + }, + { + "widgettype": "Text", + "options": { + "text": "用户管理", + "color": "#E2E8F0", + "fontSize": "13px", + "marginTop": "8px" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "bgcolor": "#334155", + "padding": "16px", + "borderRadius": "8px", + "cursor": "pointer", + "textAlign": "center" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.sage_main_content", + "options": { + "url": "{{entire_url('/rag/kdb')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Svg", + "options": { + "svg": "" + } + }, + { + "widgettype": "Text", + "options": { + "text": "知识库", + "color": "#E2E8F0", + "fontSize": "13px", + "marginTop": "8px" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "bgcolor": "#334155", + "padding": "16px", + "borderRadius": "8px", + "cursor": "pointer", + "textAlign": "center" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.sage_main_content", + "options": { + "url": "{{entire_url('/llmage/failed_accounting.ui')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Svg", + "options": { + "svg": "" + } + }, + { + "widgettype": "Text", + "options": { + "text": "异常记录", + "color": "#E2E8F0", + "fontSize": "13px", + "marginTop": "8px" + } + } + ] + } + ] + } + ] + } + ] + }, { "widgettype": "VBox", "options": { - "bgcolor": "#FFFFFF", - "padding": "24px", - "borderRadius": "8px", - "marginTop": "20px", - "minHeight": "350px", - "boxShadow": "0 2px 8px rgba(0,0,0,0.1)" + "width": "100%", + "bgcolor": "#1E293B", + "borderRadius": "12px", + "padding": "20px", + "border": "1px solid #334155", + "marginTop": "20px" }, "subwidgets": [ { - "widgettype": "Text", + "widgettype": "Title4", "options": { - "text": "Top 3 模型(今日)", - "fontSize": "18px", - "fontWeight": "bold", - "color": "#333", + "text": "用户消费排行(Top 5)", + "color": "#F1F5F9", + "fontWeight": "600", "marginBottom": "16px" } }, { "widgettype": "RefreshWidget", - "id": "refresh_top_models_chart", + "id": "table_top_users", "options": { - "period_seconds": 10, - "url": "{{entire_url('top_models_chart.ui')}}" + "period_seconds": 30, + "url": "{{entire_url('table_top_users.ui')}}" } } ] diff --git a/wwwroot/menu.ui b/wwwroot/menu.ui index f38a7e9..ca0d2cd 100644 --- a/wwwroot/menu.ui +++ b/wwwroot/menu.ui @@ -6,7 +6,7 @@ { "name": "dashboard", "label": "数据看板", - "url": "{{entire_url('index.ui')}}" + "url": "{{entire_url('shell.ui')}}" } ] } diff --git a/wwwroot/shell.ui b/wwwroot/shell.ui new file mode 100644 index 0000000..59aa0d6 --- /dev/null +++ b/wwwroot/shell.ui @@ -0,0 +1,147 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "bgcolor": "#0B1120" + }, + "subwidgets": [ + { + "widgettype": "Html", + "options": { + "html": "