feat(llmage): 日期变更触发备份(use_date<昨天) + llmusage添加(accounting_status,use_date)组合索引

This commit is contained in:
yumoqing 2026-05-24 17:21:22 +08:00
parent 8755ab5d6d
commit c7acd449f1
5 changed files with 79 additions and 16 deletions

53
dat/qwen3.7-max.txt Normal file
View File

@ -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 uapiOpenAI兼容接口
3. 如果qwen3.7-max有特殊的推理模式如思考/非思考切换),可能需要额外的配置
4. 之前提到的 startReasoning is not defined 前端报错问题需要单独排查可能与harnessed_reasoning的bricks前端代码有关

Binary file not shown.

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
import json import json
import time import time
from datetime import datetime from datetime import datetime, timedelta
from appPublic.log import exception, debug from appPublic.log import exception, debug
from appPublic.uniqueID import getID from appPublic.uniqueID import getID
from appPublic.dictObject import DictObject from appPublic.dictObject import DictObject
@ -239,20 +239,18 @@ async def llm_accoung_failed(luid, reason=None):
await sor.C('llmusage_accounting_failed', failed_rec) await sor.C('llmusage_accounting_failed', failed_rec)
async def backup_accounted_llmusage(): async def backup_accounted_llmusage(cutoff_date):
"""Backup accounted records with use_date before today to history table.""" """Backup accounted records with use_date < cutoff_date to history table."""
env = ServerEnv() env = ServerEnv()
today = datetime.now().strftime('%Y-%m-%d')
ts = env.timestampstr() ts = env.timestampstr()
batched = 0 batched = 0
async with get_sor_context(env, 'llmage') as sor: async with get_sor_context(env, 'llmage') as sor:
# Select records with use_date < today (i.e. yesterday and earlier)
sql = """select * from llmusage sql = """select * from llmusage
where accounting_status='accounted' where accounting_status='accounted'
and use_date < ${today}$""" and use_date < ${cutoff_date}$"""
recs = await sor.sqlExe(sql, {'today': today}) recs = await sor.sqlExe(sql, {'cutoff_date': cutoff_date})
if not recs: if not recs:
debug(f'backup_accounted_llmusage: no records to backup for use_date < {today}') debug(f'backup_accounted_llmusage: no records to backup for use_date < {cutoff_date}')
return 0 return 0
debug(f'backup_accounted_llmusage: {len(recs)} records to backup') debug(f'backup_accounted_llmusage: {len(recs)} records to backup')
for r in recs: for r in recs:
@ -280,7 +278,7 @@ where accounting_status='accounted'
# Delete from main table # Delete from main table
await sor.D('llmusage', {'id': r.id}) await sor.D('llmusage', {'id': r.id})
batched += 1 batched += 1
debug(f'backup_accounted_llmusage: backed up {batched} records') debug(f'backup_accounted_llmusage: backed up {batched} records for use_date < {cutoff_date}')
return batched return batched
@ -335,14 +333,13 @@ order by failed_time desc limit {page_size} offset {offset}"""
async def backend_accounting(): async def backend_accounting():
env = ServerEnv() env = ServerEnv()
debug(f'backend accounting started ...') debug(f'backend accounting started ...')
backup_counter = 0 last_backup_date = None
while True: while True:
try: try:
lus = await get_accounting_llmusages() lus = await get_accounting_llmusages()
except Exception as e: except Exception as e:
exception(f'{e}') exception(f'{e}')
lus = [] lus = []
# debug(f'{len(lus)=} need to accounting........')
for lu in lus: for lu in lus:
try: try:
tpac = await get_user_tpac(lu.userid) tpac = await get_user_tpac(lu.userid)
@ -356,12 +353,14 @@ async def backend_accounting():
exception(f'{e}, {lu.id=}') exception(f'{e}, {lu.id=}')
await llm_accoung_failed(lu.id, reason=str(e)) await llm_accoung_failed(lu.id, reason=str(e))
# Run backup every 30 iterations (~5 minutes) # Check if date changed, trigger backup once per day
backup_counter += 1 today = datetime.now().strftime('%Y-%m-%d')
if backup_counter >= 30: if today != last_backup_date:
backup_counter = 0 yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
last_backup_date = today
try: try:
await backup_accounted_llmusage() debug(f'date changed to {today}, triggering backup for use_date < {yesterday}')
await backup_accounted_llmusage(yesterday)
except Exception as e: except Exception as e:
exception(f'backup_accounted_llmusage failed: {e}') exception(f'backup_accounted_llmusage failed: {e}')

View File

@ -123,6 +123,14 @@
"idxfields": [ "idxfields": [
"userid" "userid"
] ]
},
{
"name": "idx_llmusage_accounting",
"idxtype": "index",
"idxfields": [
"accounting_status",
"use_date"
]
} }
] ]
} }

View File

@ -69,6 +69,9 @@ CREATE INDEX idx_laf_llmid ON llmusage_accounting_failed(llmid);
CREATE INDEX idx_laf_handled ON llmusage_accounting_failed(handled); CREATE INDEX idx_laf_handled ON llmusage_accounting_failed(handled);
CREATE INDEX idx_laf_failed_time ON llmusage_accounting_failed(failed_time); CREATE INDEX idx_laf_failed_time ON llmusage_accounting_failed(failed_time);
-- 3. 为 llmusage 表添加组合索引(优化备份查询: accounting_status + use_date
CREATE INDEX idx_llmusage_accounting ON llmusage(accounting_status, use_date);
-- ============================================================ -- ============================================================
-- 验证步骤(执行后运行): -- 验证步骤(执行后运行):
-- 1. 确认表创建成功: -- 1. 确认表创建成功: