diff --git a/sage_datamart/init.py b/sage_datamart/init.py index b55f344..4323394 100644 --- a/sage_datamart/init.py +++ b/sage_datamart/init.py @@ -398,6 +398,62 @@ async def j2_avg_ttft(request): return 0 +# ── 监控组件 j2 函数,每段独立 .ui 通过 RefreshWidget 加载 ── + +async def j2_model_perf_data(request): + try: + env = request._run_ns + async with get_sor_context(env, MODULE_NAME) as sor: + return await api_model_perf(sor) + except Exception: + return [] + + +async def j2_currency_data(request): + try: + env = request._run_ns + async with get_sor_context(env, MODULE_NAME) as sor: + return await get_currency_breakdown(env, sor) + except Exception: + return [] + + +async def j2_provider_roi_data(request): + try: + env = request._run_ns + async with get_sor_context(env, MODULE_NAME) as sor: + return await api_provider_roi(sor) + except Exception: + return [] + + +async def j2_top_models_data(request): + try: + env = request._run_ns + async with get_sor_context(env, MODULE_NAME) as sor: + return await get_top_models(env, sor) + except Exception: + return [] + + +async def j2_top_users_data(request): + try: + env = request._run_ns + async with get_sor_context(env, MODULE_NAME) as sor: + return await get_top_users(env, sor) + except Exception: + return [] + + +async def j2_top_providers_data(request): + try: + env = request._run_ns + async with get_sor_context(env, MODULE_NAME) as sor: + return await get_top_providers(env, sor) + except Exception: + return [] + + # ── Cron ETL ── async def cron_etl_sync(request): @@ -457,6 +513,12 @@ def load_sage_datamart(): env.j2_online_users = j2_online_users env.j2_scoped_success_rate = j2_scoped_success_rate env.j2_scoped_fail_count = j2_scoped_fail_count + env.j2_model_perf_data = j2_model_perf_data + env.j2_currency_data = j2_currency_data + env.j2_provider_roi_data = j2_provider_roi_data + env.j2_top_models_data = j2_top_models_data + env.j2_top_users_data = j2_top_users_data + env.j2_top_providers_data = j2_top_providers_data env.cron_etl_sync = cron_etl_sync env.cron_etl_aggregate = cron_etl_aggregate env.cron_etl_provider_cost = cron_etl_provider_cost diff --git a/scripts/load_path.py b/scripts/load_path.py index 61c5dcb..75b6d7a 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -35,6 +35,12 @@ PATHS_LOGINED = [ f"/{MOD}/rt_card_online.ui", f"/{MOD}/rt_card_success.ui", f"/{MOD}/rt_card_fail.ui", + f"/{MOD}/sect_model_perf.ui", + f"/{MOD}/sect_currency.ui", + f"/{MOD}/sect_provider_roi.ui", + f"/{MOD}/sect_top_models.ui", + f"/{MOD}/sect_top_users.ui", + f"/{MOD}/sect_top_providers.ui", ] def run(role, path): diff --git a/wwwroot/index.ui b/wwwroot/index.ui index 10a686e..e1c69a2 100644 --- a/wwwroot/index.ui +++ b/wwwroot/index.ui @@ -20,13 +20,13 @@ "widgettype": "HBox", "options": {"width": "100%", "gap": "12px", "marginBottom": "16px", "flexWrap": "wrap"}, "subwidgets": [ - {"widgettype": "RefreshWidget", "id": "rt_calls", "options": {"period_seconds": 15, "url": "{{entire_url('rt_card_calls.ui')}}", "width": "13%"}}, + {"widgettype": "RefreshWidget", "id": "rt_calls", "options": {"period_seconds": 15, "url": "{{entire_url('rt_card_calls.ui')}}", "width": "13%"}}, {"widgettype": "RefreshWidget", "id": "rt_amount", "options": {"period_seconds": 15, "url": "{{entire_url('rt_card_amount.ui')}}", "width": "13%"}}, - {"widgettype": "RefreshWidget", "id": "rt_users", "options": {"period_seconds": 30, "url": "{{entire_url('rt_card_users.ui')}}", "width": "13%"}}, + {"widgettype": "RefreshWidget", "id": "rt_users", "options": {"period_seconds": 30, "url": "{{entire_url('rt_card_users.ui')}}", "width": "13%"}}, {"widgettype": "RefreshWidget", "id": "rt_active", "options": {"period_seconds": 30, "url": "{{entire_url('rt_card_active.ui')}}", "width": "13%"}}, {"widgettype": "RefreshWidget", "id": "rt_online", "options": {"period_seconds": 30, "url": "{{entire_url('rt_card_online.ui')}}", "width": "13%"}}, - {"widgettype": "RefreshWidget", "id": "rt_success", "options": {"period_seconds": 30, "url": "{{entire_url('rt_card_success.ui')}}", "width": "13%"}}, - {"widgettype": "RefreshWidget", "id": "rt_fail", "options": {"period_seconds": 30, "url": "{{entire_url('rt_card_fail.ui')}}", "width": "13%"}} + {"widgettype": "RefreshWidget", "id": "rt_success","options": {"period_seconds": 30, "url": "{{entire_url('rt_card_success.ui')}}", "width": "13%"}}, + {"widgettype": "RefreshWidget", "id": "rt_fail", "options": {"period_seconds": 30, "url": "{{entire_url('rt_card_fail.ui')}}", "width": "13%"}} ] }, { @@ -38,25 +38,7 @@ "options": {"css": "card", "width": "60%", "padding": "16px"}, "subwidgets": [ {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "模型性能排行", "i18n": true, "marginBottom": "8px"}}, - { - "widgettype": "Cols", - "options": { - "col_cwidth": 24, - "data_url": "{{entire_url('api/model_perf.dspy')}}", - "data_params": {}, - "record_view": { - "widgettype": "HBox", - "options": {"cheight": 3, "width": "100%"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "${model}", "width": "35%"}}, - {"widgettype": "Text", "options": {"text": "${avg_ttft_ms}ms", "width": "15%"}}, - {"widgettype": "Text", "options": {"text": "${success_rate}%", "width": "15%"}}, - {"widgettype": "Text", "options": {"text": "${total_calls}", "width": "15%"}}, - {"widgettype": "Text", "options": {"text": "${total_amount_cny}", "width": "20%"}} - ] - } - } - } + {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_model_perf.ui')}}", "width": "100%"}} ] }, { @@ -64,24 +46,7 @@ "options": {"css": "card", "width": "40%", "padding": "16px"}, "subwidgets": [ {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "币种分布", "i18n": true, "marginBottom": "8px"}}, - { - "widgettype": "Cols", - "options": { - "col_cwidth": 24, - "data_url": "{{entire_url('api/currency_stats.dspy')}}", - "data_params": {}, - "record_view": { - "widgettype": "HBox", - "options": {"cheight": 3, "width": "100%"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "${currency}", "width": "20%"}}, - {"widgettype": "Text", "options": {"text": "${call_count}次", "width": "25%"}}, - {"widgettype": "Text", "options": {"text": "${amount}", "width": "25%"}}, - {"widgettype": "Text", "options": {"text": "${amount_cny}", "width": "30%"}} - ] - } - } - } + {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_currency.ui')}}", "width": "100%"}} ] } ] @@ -95,25 +60,7 @@ "options": {"css": "card", "width": "50%", "padding": "16px"}, "subwidgets": [ {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "供应商性价比", "i18n": true, "marginBottom": "8px"}}, - { - "widgettype": "Cols", - "options": { - "col_cwidth": 24, - "data_url": "{{entire_url('api/provider_roi.dspy')}}", - "data_params": {}, - "record_view": { - "widgettype": "HBox", - "options": {"cheight": 3, "width": "100%"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "${model}", "width": "35%"}}, - {"widgettype": "Text", "options": {"text": "${unit_price}/t", "width": "20%"}}, - {"widgettype": "Text", "options": {"text": "${avg_ttft_ms}ms", "width": "15%"}}, - {"widgettype": "Text", "options": {"text": "${total_calls}次", "width": "15%"}}, - {"widgettype": "Text", "options": {"text": "${total_amount_cny}", "width": "15%"}} - ] - } - } - } + {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_provider_roi.ui')}}", "width": "100%"}} ] } ] @@ -127,23 +74,7 @@ "options": {"css": "card", "width": "33%", "padding": "16px"}, "subwidgets": [ {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "热门模型", "i18n": true, "marginBottom": "8px"}}, - { - "widgettype": "Cols", - "options": { - "col_cwidth": 20, - "data_url": "{{entire_url('api/top_models.dspy')}}", - "data_params": {}, - "record_view": { - "widgettype": "HBox", - "options": {"cheight": 3, "width": "100%"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "${model_name}", "width": "50%"}}, - {"widgettype": "Text", "options": {"text": "${cnt}次", "width": "25%"}}, - {"widgettype": "Text", "options": {"text": "${total_amount}", "width": "25%"}} - ] - } - } - } + {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_top_models.ui')}}", "width": "100%"}} ] }, { @@ -151,23 +82,7 @@ "options": {"css": "card", "width": "33%", "padding": "16px"}, "subwidgets": [ {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "用户排行", "i18n": true, "marginBottom": "8px"}}, - { - "widgettype": "Cols", - "options": { - "col_cwidth": 20, - "data_url": "{{entire_url('api/top_users.dspy')}}", - "data_params": {}, - "record_view": { - "widgettype": "HBox", - "options": {"cheight": 3, "width": "100%"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "${user_name}", "width": "50%"}}, - {"widgettype": "Text", "options": {"text": "${cnt}次", "width": "25%"}}, - {"widgettype": "Text", "options": {"text": "${total_amount}", "width": "25%"}} - ] - } - } - } + {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_top_users.ui')}}", "width": "100%"}} ] }, { @@ -175,23 +90,7 @@ "options": {"css": "card", "width": "33%", "padding": "16px"}, "subwidgets": [ {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "供应商排行", "i18n": true, "marginBottom": "8px"}}, - { - "widgettype": "Cols", - "options": { - "col_cwidth": 20, - "data_url": "{{entire_url('api/top_providers.dspy')}}", - "data_params": {}, - "record_view": { - "widgettype": "HBox", - "options": {"cheight": 3, "width": "100%"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "${provider_name}", "width": "50%"}}, - {"widgettype": "Text", "options": {"text": "${cnt}次", "width": "25%"}}, - {"widgettype": "Text", "options": {"text": "${total_amount}", "width": "25%"}} - ] - } - } - } + {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_top_providers.ui')}}", "width": "100%"}} ] } ] diff --git a/wwwroot/sect_currency.ui b/wwwroot/sect_currency.ui new file mode 100644 index 0000000..6f64177 --- /dev/null +++ b/wwwroot/sect_currency.ui @@ -0,0 +1,10 @@ +{% set data = j2_currency_data(request) %} +{"widgettype":"VBox","options":{"width":"100%"},"subwidgets":[ +{% for r in data %} +{"widgettype":"HBox","options":{"cheight":3,"width":"100%"},"subwidgets":[ +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('currency',''),ensure_ascii=False)}},"width":"20%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('call_count',0)|string)+'次',ensure_ascii=False)}},"width":"25%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('amount',0)|string,ensure_ascii=False)}},"width":"25%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('amount_cny',0)|string,ensure_ascii=False)}},"width":"30%"}} +]}{% if not loop.last %},{% endif %}{% endfor %} +]} diff --git a/wwwroot/sect_model_perf.ui b/wwwroot/sect_model_perf.ui new file mode 100644 index 0000000..2c26961 --- /dev/null +++ b/wwwroot/sect_model_perf.ui @@ -0,0 +1,11 @@ +{% set data = j2_model_perf_data(request) %} +{"widgettype":"VBox","options":{"width":"100%"},"subwidgets":[ +{% for r in data %} +{"widgettype":"HBox","options":{"cheight":3,"width":"100%"},"subwidgets":[ +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('model',''),ensure_ascii=False)}},"width":"35%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('avg_ttft_ms','')|string)+'ms',ensure_ascii=False)}},"width":"15%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('success_rate','')|string)+'%',ensure_ascii=False)}},"width":"15%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('total_calls','')|string,ensure_ascii=False)}},"width":"15%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('total_amount_cny','')|string,ensure_ascii=False)}},"width":"20%"}} +]}{% if not loop.last %},{% endif %}{% endfor %} +]} diff --git a/wwwroot/sect_provider_roi.ui b/wwwroot/sect_provider_roi.ui new file mode 100644 index 0000000..4d3a5bd --- /dev/null +++ b/wwwroot/sect_provider_roi.ui @@ -0,0 +1,11 @@ +{% set data = j2_provider_roi_data(request) %} +{"widgettype":"VBox","options":{"width":"100%"},"subwidgets":[ +{% for r in data %} +{"widgettype":"HBox","options":{"cheight":3,"width":"100%"},"subwidgets":[ +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('model',''),ensure_ascii=False)}},"width":"35%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('unit_price','')|string)+'/t',ensure_ascii=False)}},"width":"20%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('avg_ttft_ms','')|string)+'ms',ensure_ascii=False)}},"width":"15%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('total_calls',0)|string)+'次',ensure_ascii=False)}},"width":"15%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('total_amount_cny',0)|string,ensure_ascii=False)}},"width":"15%"}} +]}{% if not loop.last %},{% endif %}{% endfor %} +]} diff --git a/wwwroot/sect_top_models.ui b/wwwroot/sect_top_models.ui new file mode 100644 index 0000000..75b7859 --- /dev/null +++ b/wwwroot/sect_top_models.ui @@ -0,0 +1,9 @@ +{% set data = j2_top_models_data(request) %} +{"widgettype":"VBox","options":{"width":"100%"},"subwidgets":[ +{% for r in data %} +{"widgettype":"HBox","options":{"cheight":3,"width":"100%"},"subwidgets":[ +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('model_name',''),ensure_ascii=False)}},"width":"50%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('cnt',0)|string)+'次',ensure_ascii=False)}},"width":"25%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('total_amount',0)|string,ensure_ascii=False)}},"width":"25%"}} +]}{% if not loop.last %},{% endif %}{% endfor %} +]} diff --git a/wwwroot/sect_top_providers.ui b/wwwroot/sect_top_providers.ui new file mode 100644 index 0000000..05612ae --- /dev/null +++ b/wwwroot/sect_top_providers.ui @@ -0,0 +1,9 @@ +{% set data = j2_top_providers_data(request) %} +{"widgettype":"VBox","options":{"width":"100%"},"subwidgets":[ +{% for r in data %} +{"widgettype":"HBox","options":{"cheight":3,"width":"100%"},"subwidgets":[ +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('provider_name',''),ensure_ascii=False)}},"width":"50%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('cnt',0)|string)+'次',ensure_ascii=False)}},"width":"25%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('total_amount',0)|string,ensure_ascii=False)}},"width":"25%"}} +]}{% if not loop.last %},{% endif %}{% endfor %} +]} diff --git a/wwwroot/sect_top_users.ui b/wwwroot/sect_top_users.ui new file mode 100644 index 0000000..4532614 --- /dev/null +++ b/wwwroot/sect_top_users.ui @@ -0,0 +1,9 @@ +{% set data = j2_top_users_data(request) %} +{"widgettype":"VBox","options":{"width":"100%"},"subwidgets":[ +{% for r in data %} +{"widgettype":"HBox","options":{"cheight":3,"width":"100%"},"subwidgets":[ +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('user_name',''),ensure_ascii=False)}},"width":"50%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps((r.get('cnt',0)|string)+'次',ensure_ascii=False)}},"width":"25%"}}, +{"widgettype":"Text","options":{"text":{{json.dumps(r.get('total_amount',0)|string,ensure_ascii=False)}},"width":"25%"}} +]}{% if not loop.last %},{% endif %}{% endfor %} +]}