diff --git a/dat/qwen3.7-max.txt b/dat/qwen3.7-max.txt
new file mode 100644
index 0000000..f07f135
--- /dev/null
+++ b/dat/qwen3.7-max.txt
@@ -0,0 +1,53 @@
+ 已生成完整的 qwen3.7-max 配置SQL。以下是配置方案:
+
+ 模型摘要
+ - 模型名称: 千问3.7-Max
+ - API model: qwen3.7-max (严格按API文档填写)
+ - 分类: text2text (文生文)
+ - 供应商: 阿里百炼 (ali-qwen)
+ - 接口: OpenAI兼容 /chat/completions,同步流式
+
+ 复用vs新建
+ - upapp: 复用 ali-qwen (阿里百炼)
+ - upappkey: 复用 gCMl-BnrTrfoqWAEPPoH8 (API Key已配置)
+ - uapi: 复用 t2t (BY2cA4RD_axNxTG4m9Coa)
+ - uapiio: 复用 Is8l4TGkcZcqFSjbbeIK2 (文本会话)
+ - llm: 新建
+ - llm_api_map: 新建
+ - pricing_program: 新建
+ - pricing_program_timing: 新建
+
+ 生成的SQL (4条)
+
+ sql
+ -- 1. llm表
+ INSERT INTO llm VALUES ('Jt26VKlUFsJxABuVLauBc','千问3.7-Max','qwen3.7-max','通义千问3.7系列旗舰模型,相较3.6版本在推理能力、代码能力、多模态理解等方面全面升级。适用于复杂任务处理、智能体开发、代码生成等高级应用场景。','text2text','qwen','ali-qwen','t2t','6fadgewjraOyvxC_EkHou','0','2026-05-23','9999-12-31','',NULL,10.00);
+
+ -- 2. llm_api_map表
+ INSERT INTO llm_api_map VALUES ('rFS0Ag-epBUxE5LCF6glW','Jt26VKlUFsJxABuVLauBc','text2text','t2t',NULL,NULL,'0B-JNSUEelsY5TQ_Sawa7','1');
+
+ -- 3. pricing_program表
+ INSERT INTO pricing_program VALUES ('0B-JNSUEelsY5TQ_Sawa7','qwen3.7-max定价','fields:\r\n formula:\r\n label: 计算公式\r\n type: str\r\n model:\r\n label: 模型\r\n type: str\r\n prompt_tokens:\r\n label: 输入词元数\r\n type: int\r\n value_mode: between\r\nformula: null\r\npricings:\r\n- formula: 2.5 prompt_tokens / 1000000 + completion_tokens 10 /1000000\r\n model: qwen3.7-max\r\n prompt_tokens: -0.1 ~= 32000\r\n- formula: 4 prompt_tokens / 1000000 + completion_tokens 16 /1000000\r\n model: qwen3.7-max\r\n prompt_tokens: 32000 ~= 128000\r\n- formula: 7 prompt_tokens / 1000000 + completion_tokens 28 /1000000\r\n model: qwen3.7-max\r\n prompt_tokens: 128000 ~= 256000\r\n- formula: 10 prompt_tokens / 1000000 + completion_tokens 40 /1000000\r\n model: qwen3.7-max\r\n prompt_tokens: 256000 ~= 1000000','2026-05-23','9999-12-31');
+
+ -- 4. pricing_program_timing表
+ INSERT INTO pricing_program_timing VALUES ('C-IIEG_9_O9beN8S2vdUI','0B-JNSUEelsY5TQ_Sawa7','qwen3.7-max计费','fields:\r\n formula:\r\n label: 计算公式\r\n type: str\r\n model:\r\n label: 模型\r\n type: str\r\n prompt_tokens:\r\n label: 输入词元数\r\n type: int\r\n value_mode: between\r\nformula: null\r\npricings:\r\n- formula: 2.5 prompt_tokens / 1000000 + completion_tokens 10 /1000000\r\n model: qwen3.7-max\r\n prompt_tokens: -0.1 ~= 32000\r\n- formula: 4 prompt_tokens / 1000000 + completion_tokens 16 /1000000\r\n model: qwen3.7-max\r\n prompt_tokens: 32000 ~= 128000\r\n- formula: 7 prompt_tokens / 1000000 + completion_tokens 28 /1000000\r\n model: qwen3.7-max\r\n prompt_tokens: 128000 ~= 256000\r\n- formula: 10 prompt_tokens / 1000000 + completion_tokens 40 /1000000\r\n model: qwen3.7-max\r\n prompt_tokens: 256000 ~= 1000000','2026-05-23','9999-12-31');
+
+
+ 验证查询
+ sql
+ SELECT id, name, model, upappid, apiname FROM llm WHERE model = 'qwen3.7-max';
+ SELECT id, llmid, llmcatelogid, apiname, ppid FROM llm_api_map WHERE llmid = 'Jt26VKlUFsJxABuVLauBc';
+ SELECT id, name FROM pricing_program WHERE id = '0B-JNSUEelsY5TQ_Sawa7';
+
+
+ ppid三处一致
+ - pricing_program.id = 0B-JNSUEelsY5TQ_Sawa7
+ - pricing_program_timing.ppid = 0B-JNSUEelsY5TQ_Sawa7
+ - llm_api_map.ppid = 0B-JNSUEelsY5TQ_Sawa7
+
+ 注意事项
+ 1. 定价参考qwen3.6-plus的阶梯式计费模式,具体价格需根据百炼官方定价调整
+ 2. qwen3.7-max使用已有的t2t uapi,OpenAI兼容接口
+ 3. 如果qwen3.7-max有特殊的推理模式(如思考/非思考切换),可能需要额外的配置
+ 4. 之前提到的 startReasoning is not defined 前端报错问题需要单独排查,可能与harnessed_reasoning的bricks前端代码有关
+
diff --git a/llmage/__pycache__/accounting.cpython-310.pyc b/llmage/__pycache__/accounting.cpython-310.pyc
new file mode 100644
index 0000000..3958bc8
Binary files /dev/null and b/llmage/__pycache__/accounting.cpython-310.pyc differ
diff --git a/llmage/init.py b/llmage/init.py
index e0f3080..7dd86f3 100644
--- a/llmage/init.py
+++ b/llmage/init.py
@@ -32,6 +32,7 @@ from .accounting import (
get_failed_accounting_records,
llm_accoung_failed
)
+from .stats import get_llmage_stats
from .asyncinference import (
get_asynctask_status,
@@ -63,6 +64,7 @@ def load_llmage():
env.get_llms_by_catelog_to_customer = get_llms_by_catelog_to_customer
env.backup_accounted_llmusage = backup_accounted_llmusage
env.get_failed_accounting_records = get_failed_accounting_records
+ env.get_llmage_stats = get_llmage_stats
rf = RegisterFunction()
rf.register('jimeng_auth_headers', jimeng_auth_headers)
diff --git a/llmage/stats.py b/llmage/stats.py
new file mode 100644
index 0000000..8afe0f2
--- /dev/null
+++ b/llmage/stats.py
@@ -0,0 +1,69 @@
+from sqlor.dbpools import get_sor_context
+from appPublic.timeUtils import curDateString, timestampstr
+from datetime import datetime, timedelta
+from appPublic.log import debug, exception
+
+async def get_llmage_stats(request):
+ """Get llmage module statistics"""
+ env = request._run_ns
+ userorgid = await env.get_userorgid()
+ today = curDateString()
+ tomorrow = (datetime.now() + timedelta(days=1)).strftime('%Y-%m-%d')
+
+ stats = {
+ 'total_models': 0,
+ 'today_usage_count': 0,
+ 'today_amount': 0,
+ 'catelog_count': 0
+ }
+
+ async with get_sor_context(env, 'llmage') as sor:
+ # Total enabled models
+ sql_models = """
+ SELECT COUNT(DISTINCT id) as cnt FROM llm
+ WHERE enabled_date <= ${today}$
+ AND expired_date > ${today}$
+ """
+ recs = await sor.sqlExe(sql_models, {'today': today})
+ if recs:
+ stats['total_models'] = int(recs[0].cnt or 0)
+
+ # Today's usage count
+ sql_usage = """
+ SELECT COUNT(*) as cnt FROM llmusage
+ WHERE userorgid = ${userorgid}$
+ AND use_date >= ${today}$
+ AND use_date < ${tomorrow}$
+ """
+ recs = await sor.sqlExe(sql_usage, {
+ 'userorgid': userorgid,
+ 'today': today,
+ 'tomorrow': tomorrow
+ })
+ if recs:
+ stats['today_usage_count'] = int(recs[0].cnt or 0)
+
+ # Today's total amount
+ sql_amount = """
+ SELECT COALESCE(SUM(amount), 0) as total FROM llmusage
+ WHERE userorgid = ${userorgid}$
+ AND use_date >= ${today}$
+ AND use_date < ${tomorrow}$
+ """
+ recs = await sor.sqlExe(sql_amount, {
+ 'userorgid': userorgid,
+ 'today': today,
+ 'tomorrow': tomorrow
+ })
+ if recs:
+ stats['today_amount'] = float(recs[0].total or 0)
+
+ # Catalog count
+ sql_catelog = """
+ SELECT COUNT(*) as cnt FROM llmcatelog
+ """
+ recs = await sor.sqlExe(sql_catelog, {})
+ if recs:
+ stats['catelog_count'] = int(recs[0].cnt or 0)
+
+ return stats
diff --git a/wwwroot/index.ui b/wwwroot/index.ui
index 86137d7..c9dcf6e 100644
--- a/wwwroot/index.ui
+++ b/wwwroot/index.ui
@@ -35,6 +35,40 @@
}
]
},
+ {
+ "widgettype": "ResponsableBox",
+ "options": {
+ "gap": "16px",
+ "minWidth": "200px",
+ "marginBottom": "24px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "urlwidget",
+ "options": {
+ "url": "{{entire_url('/llmage/stat_total_models.ui')}}"
+ }
+ },
+ {
+ "widgettype": "urlwidget",
+ "options": {
+ "url": "{{entire_url('/llmage/stat_today_calls.ui')}}"
+ }
+ },
+ {
+ "widgettype": "urlwidget",
+ "options": {
+ "url": "{{entire_url('/llmage/stat_today_amount.ui')}}"
+ }
+ },
+ {
+ "widgettype": "urlwidget",
+ "options": {
+ "url": "{{entire_url('/llmage/stat_catelog_count.ui')}}"
+ }
+ }
+ ]
+ },
{
"widgettype": "ResponsableBox",
"options": {
diff --git a/wwwroot/stat_catelog_count.ui b/wwwroot/stat_catelog_count.ui
new file mode 100644
index 0000000..3bd121f
--- /dev/null
+++ b/wwwroot/stat_catelog_count.ui
@@ -0,0 +1,53 @@
+{% set stats = get_llmage_stats(request) %}
+{
+ "widgettype": "VBox",
+ "options": {
+ "bgcolor": "#1E293B",
+ "padding": "20px",
+ "borderRadius": "12px",
+ "border": "1px solid #334155",
+ "flex": "1",
+ "minHeight": "110px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "HBox",
+ "options": {
+ "alignItems": "center",
+ "marginBottom": "12px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "Svg",
+ "options": {
+ "svg": "",
+ "width": "24px",
+ "height": "24px"
+ }
+ },
+ {
+ "widgettype": "Filler"
+ }
+ ]
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "{{stats.catelog_count}}",
+ "fontSize": "32px",
+ "fontWeight": "700",
+ "color": "#F1F5F9",
+ "lineHeight": "1.1"
+ }
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "模型分类",
+ "fontSize": "14px",
+ "color": "#94A3B8",
+ "marginTop": "4px"
+ }
+ }
+ ]
+}
diff --git a/wwwroot/stat_today_amount.ui b/wwwroot/stat_today_amount.ui
new file mode 100644
index 0000000..2ac8469
--- /dev/null
+++ b/wwwroot/stat_today_amount.ui
@@ -0,0 +1,53 @@
+{% set stats = get_llmage_stats(request) %}
+{
+ "widgettype": "VBox",
+ "options": {
+ "bgcolor": "#1E293B",
+ "padding": "20px",
+ "borderRadius": "12px",
+ "border": "1px solid #334155",
+ "flex": "1",
+ "minHeight": "110px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "HBox",
+ "options": {
+ "alignItems": "center",
+ "marginBottom": "12px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "Svg",
+ "options": {
+ "svg": "",
+ "width": "24px",
+ "height": "24px"
+ }
+ },
+ {
+ "widgettype": "Filler"
+ }
+ ]
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "¥{{'%.2f' % stats.today_amount}}",
+ "fontSize": "32px",
+ "fontWeight": "700",
+ "color": "#F1F5F9",
+ "lineHeight": "1.1"
+ }
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "今日消费",
+ "fontSize": "14px",
+ "color": "#94A3B8",
+ "marginTop": "4px"
+ }
+ }
+ ]
+}
diff --git a/wwwroot/stat_today_calls.ui b/wwwroot/stat_today_calls.ui
new file mode 100644
index 0000000..7edd862
--- /dev/null
+++ b/wwwroot/stat_today_calls.ui
@@ -0,0 +1,53 @@
+{% set stats = get_llmage_stats(request) %}
+{
+ "widgettype": "VBox",
+ "options": {
+ "bgcolor": "#1E293B",
+ "padding": "20px",
+ "borderRadius": "12px",
+ "border": "1px solid #334155",
+ "flex": "1",
+ "minHeight": "110px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "HBox",
+ "options": {
+ "alignItems": "center",
+ "marginBottom": "12px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "Svg",
+ "options": {
+ "svg": "",
+ "width": "24px",
+ "height": "24px"
+ }
+ },
+ {
+ "widgettype": "Filler"
+ }
+ ]
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "{{stats.today_usage_count}}",
+ "fontSize": "32px",
+ "fontWeight": "700",
+ "color": "#F1F5F9",
+ "lineHeight": "1.1"
+ }
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "今日调用",
+ "fontSize": "14px",
+ "color": "#94A3B8",
+ "marginTop": "4px"
+ }
+ }
+ ]
+}
diff --git a/wwwroot/stat_total_models.ui b/wwwroot/stat_total_models.ui
new file mode 100644
index 0000000..3861e52
--- /dev/null
+++ b/wwwroot/stat_total_models.ui
@@ -0,0 +1,53 @@
+{% set stats = get_llmage_stats(request) %}
+{
+ "widgettype": "VBox",
+ "options": {
+ "bgcolor": "#1E293B",
+ "padding": "20px",
+ "borderRadius": "12px",
+ "border": "1px solid #334155",
+ "flex": "1",
+ "minHeight": "110px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "HBox",
+ "options": {
+ "alignItems": "center",
+ "marginBottom": "12px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "Svg",
+ "options": {
+ "svg": "",
+ "width": "24px",
+ "height": "24px"
+ }
+ },
+ {
+ "widgettype": "Filler"
+ }
+ ]
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "{{stats.total_models}}",
+ "fontSize": "32px",
+ "fontWeight": "700",
+ "color": "#F1F5F9",
+ "lineHeight": "1.1"
+ }
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "可用模型数",
+ "fontSize": "14px",
+ "color": "#94A3B8",
+ "marginTop": "4px"
+ }
+ }
+ ]
+}