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" + } + } + ] +}