From 9cb6b5abdf3e81291e261180b4859387dbea8bcb Mon Sep 17 00:00:00 2001 From: yumoqing Date: Mon, 7 Sep 2026 17:57:49 +0800 Subject: [PATCH] =?UTF-8?q?chore(scripts):=20=E5=AD=98=E6=A1=A3MiniMax?= =?UTF-8?q?=E5=AE=9A=E4=BB=B7=E8=90=BD=E5=BA=93(M3=E5=88=86=E6=A1=A3?= =?UTF-8?q?=E4=BA=94=E6=8A=98/M2.7/M2.7-highspeed,=E4=B8=8D=E9=85=8D?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=86=99=E5=85=A5=E5=9B=A0=E5=AD=90-?= =?UTF-8?q?=E6=97=A0usage=E8=AF=81=E6=8D=AE)=E4=B8=8E4=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=B8=8B=E7=BA=BF=E8=84=9A=E6=9C=AC(=E4=B8=8A=E6=B8=B8?= =?UTF-8?q?=E4=B8=8B=E6=9E=B6,disabled+=E4=BA=A7=E5=93=81=E4=B8=8B?= =?UTF-8?q?=E6=9E=B6,=E5=B9=82=E7=AD=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/apply_pricing_minimax_20260907.py | 170 ++++++++++++++++++++++ scripts/retire_models_20260907.py | 84 +++++++++++ 2 files changed, 254 insertions(+) create mode 100644 scripts/apply_pricing_minimax_20260907.py create mode 100644 scripts/retire_models_20260907.py diff --git a/scripts/apply_pricing_minimax_20260907.py b/scripts/apply_pricing_minimax_20260907.py new file mode 100644 index 0000000..168158c --- /dev/null +++ b/scripts/apply_pricing_minimax_20260907.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 -*- +"""MiniMax 定价落库(2026-09-07 用户提供价目表)。 + +覆盖:M3(≤512k/​>512k 分档,永久五折价)、M2.7、M2.7-highspeed。 +不配:M2/M2.1/M2.1-highspeed/M2.5/M2.5-highspeed(价目表没有,铁律不编造); + 「缓存写入2.625」因子(无运行时usage证据,历史流水为空,配了永远命中不了)。 +所有 YAML 已过引擎试算(tmp_mm.py,8组用例含分档边界全过)。 +幂等:方案按名复用;timing 每 ppid 单条有效行(同内容跳过/同日原地/历史拉链)。 +用法:py3/bin/python tmp_mm_apply.py [--dry] +""" +import asyncio +import json +import sys +import time + +sys.path.insert(0, '/d/pipeline/pipeline-app') + +import yaml # noqa: E402 +from sqlor.dbpools import DBPools # noqa: E402 +from appPublic.uniqueID import getID # noqa: E402 +from appPublic.jsonConfig import getConfig # noqa: E402 + +DRY = '--dry' in sys.argv +DOC_QUOTE = '用户提供的MiniMax按量计费价目表(2026-09-07粘贴正文)' + +TOKEN_FIELDS = { + 'price_factors': {'type': 'string', 'role': 'factor', 'label': '计价因子'}, + 'unit_prices': {'type': 'float', 'role': 'factor', 'label': '单位定价'}, + 'unit': {'type': 'string', 'role': 'factor', 'label': '计价单位'}, + 'uncache_tokens': {'type': 'int', 'role': 'factor', 'label': '非缓存输入Token', + 'derived': 'prompt_tokens - prompt_tokens_details.cached_tokens'}, + 'cached_tokens': {'type': 'int', 'role': 'factor', 'label': '缓存Token', + 'derived': 'prompt_tokens_details.cached_tokens'}, + 'completion_tokens': {'type': 'float', 'role': 'factor', 'label': '输出tokens'}, +} + + +def token_yaml(u, c, ca): + return yaml.dump({'unit_values': {'百万': 1000000}, 'fields': TOKEN_FIELDS, + 'pricings': [ + {'price_factors': 'uncache_tokens', 'unit_prices': u, 'unit': '百万'}, + {'price_factors': 'completion_tokens', 'unit_prices': c, 'unit': '百万'}, + {'price_factors': 'cached_tokens', 'unit_prices': ca, 'unit': '百万'}]}, + allow_unicode=True, sort_keys=False) + + +def tiered_token_yaml(tiers): + fields = dict(TOKEN_FIELDS) + fields['prompt_tokens'] = {'type': 'int', 'role': 'filter', 'label': 'prompt_tokens', + 'value_mode': 'between'} + pricings = [] + for fi, factor in enumerate(('uncache_tokens', 'completion_tokens', 'cached_tokens')): + filters = [] + for lo, hi, u, c, ca in tiers: + op = ('0 =~= %d' % hi) if lo == 0 else ('%d ~= %d' % (lo, hi)) + filters.append({'prompt_tokens': op, 'value_mode': 'between', + 'unit_prices': (u, c, ca)[fi]}) + pricings.append({'price_factors': factor, 'unit_prices': tiers[-1][2 + fi], + 'unit': '百万', 'filters': filters}) + return yaml.dump({'unit_values': {'百万': 1000000}, 'fields': fields, 'pricings': pricings}, + allow_unicode=True, sort_keys=False) + + +PLANS = [ + ('MiniMax-M3 定价', tiered_token_yaml([ + (0, 512000, 2.10, 8.40, 0.42), + (512000, 1000000, 4.20, 16.80, 0.84)]), + 'MiniMax-M3 ≤512k输入tokens永久五折 输入2.10/输出8.40/缓存读取0.42; ' + '>512k永久五折 4.20/16.80/0.84 元/百万tokens', ['MiniMax-M3']), + ('MiniMax-M2.7 定价', token_yaml(2.1, 8.4, 0.42), + 'MiniMax-M2.7 输入2.1/输出8.4/缓存读取0.42 元/百万tokens', ['MiniMax-M2.7']), + ('MiniMax-M2.7-highspeed 定价', token_yaml(4.2, 16.8, 0.42), + 'MiniMax-M2.7-highspeed 输入4.2/输出16.8/缓存读取0.42 元/百万tokens', + ['MiniMax-M2.7-highspeed']), +] + + +async def main(): + cfg = getConfig() + DBPools(cfg.databases) + db = DBPools() + log = [] + now = time.strftime('%Y-%m-%d') + async with db.sqlorContext('pipeline') as sor: + for pname, yml, quote, model_names in PLANS: + desc = '定价出处:%s | 文档原文:%s' % (DOC_QUOTE, quote) + recs = await sor.sqlExe( + "SELECT id FROM pricing_program WHERE name=${n}$ LIMIT 1", {"n": pname}) + await sor.sqlExe("COMMIT", {}) + if recs: + ppid = recs[0].id + action = '复用方案' + recs2 = await sor.sqlExe( + "SELECT id, pricing_data, enabled_date FROM pricing_program_timing " + "WHERE ppid=${p}$ AND expired_date='9999-12-31' " + "ORDER BY enabled_date DESC LIMIT 1", {"p": ppid}) + await sor.sqlExe("COMMIT", {}) + cur = recs2[0] if recs2 else None + same = False + if cur is not None: + try: + same = (yaml.safe_load(cur.pricing_data or '{}') == yaml.safe_load(yml)) + except Exception: + same = (cur.pricing_data or '') == yml + if same: + action = '复用方案+内容无变化(幂等跳过)' + elif cur is not None and str(cur.enabled_date)[:10] == now: + if not DRY: + await sor.sqlExe( + "UPDATE pricing_program_timing SET pricing_data=${y}$ WHERE id=${i}$", + {"y": yml, "i": cur.id}) + await sor.sqlExe("COMMIT", {}) + action = '复用方案+原地更新(同日)' + elif cur is not None: + if not DRY: + await sor.sqlExe( + "UPDATE pricing_program_timing SET expired_date=${d}$ " + "WHERE ppid=${p}$ AND expired_date='9999-12-31'", + {"d": now, "p": ppid}) + await sor.C('pricing_program_timing', { + 'id': getID(), 'ppid': ppid, 'name': pname, 'pricing_data': yml, + 'enabled_date': now, 'expired_date': '9999-12-31'}) + await sor.sqlExe("COMMIT", {}) + action = '复用方案+拉链更新' + else: + if not DRY: + await sor.C('pricing_program_timing', { + 'id': getID(), 'ppid': ppid, 'name': pname, 'pricing_data': yml, + 'enabled_date': now, 'expired_date': '9999-12-31'}) + await sor.sqlExe("COMMIT", {}) + action = '复用方案+新建时序' + if not DRY: + await sor.sqlExe( + "UPDATE pricing_program SET description=${d}$ WHERE id=${i}$", + {"d": desc[:1000], "i": ppid}) + await sor.sqlExe("COMMIT", {}) + else: + ppid = getID() + if not DRY: + await sor.C('pricing_program', { + 'id': ppid, 'name': pname, 'ownerid': '0', 'providerid': '', + 'pricing_belong': '', 'description': desc[:1000], 'currency': 'CNY'}) + await sor.C('pricing_program_timing', { + 'id': getID(), 'ppid': ppid, 'name': pname, 'pricing_data': yml, + 'enabled_date': now, 'expired_date': '9999-12-31'}) + await sor.sqlExe("COMMIT", {}) + action = '新建方案+时序' + for mn in model_names: + recs3 = await sor.sqlExe( + "SELECT id, COALESCE(ppid,'') AS ppid FROM llm_model " + "WHERE name=${n}$ AND status='active'", {"n": mn}) + await sor.sqlExe("COMMIT", {}) + if not recs3: + log.append(' ⚠ 模型 %s 未找到(active)——跳过挂载' % mn) + continue + mid = recs3[0].id + old_ppid = recs3[0].ppid or '' + if old_ppid == ppid: + log.append(' %s ppid 已正确' % mn) + continue + if not DRY: + await sor.sqlExe( + "UPDATE llm_model SET ppid=${p}$, updated_at=NOW() WHERE id=${i}$", + {"p": ppid, "i": mid}) + await sor.sqlExe("COMMIT", {}) + log.append(' %s: ppid %s→%s [%s]' % (mn, old_ppid or '空', ppid, action)) + print(json.dumps({'dry': DRY, 'log': log}, ensure_ascii=False, indent=1)) + + +asyncio.run(main()) diff --git a/scripts/retire_models_20260907.py b/scripts/retire_models_20260907.py new file mode 100644 index 0000000..a24e378 --- /dev/null +++ b/scripts/retire_models_20260907.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +"""模型下线(2026-09-07 用户确认:4个模型上游已下架)。 + +目标模型:ali-deepseek-v4-flash / qwen3-vl-embedding / qwen3-vl-rerank / + tongyi-embedding-vision-flash + +下线动作(两层): + 1. llm_model.status='disabled'(停用,字典合法值 active/disabled/suspended/exhausted) + 2. product.status='0'(产品映射同步下架,商城不再展示) + +安全性依据(代码级核对): + - 治理链 gateway._model_chain 加载主/备/辅助模型时都带 status='active' 过滤 + (gateway.py L183/L203)——备链里的停用模型会被自动顺延跳过,不会断链, + 无需改 6 个组织的 backup_model_ids JSON(引擎天然过滤,改 JSON 反而有风险) + - 模型下拉(get_llm_model_options/get_llm_online_check_models/selection.model_options) + 全部 WHERE status='active'——停用后自动从所有选择器消失 + - 出账不受影响:停用只拦新调用,历史流水照常出账 + +幂等:重复执行结果一致。用法:py3/bin/python tmp_retire_models.py [--dry] +""" +import asyncio +import json +import sys + +sys.path.insert(0, '/d/pipeline/pipeline-app') + +from sqlor.dbpools import DBPools # noqa: E402 +from appPublic.jsonConfig import getConfig # noqa: E402 + +DRY = '--dry' in sys.argv +TARGETS = ['ali-deepseek-v4-flash', 'qwen3-vl-embedding', 'qwen3-vl-rerank', + 'tongyi-embedding-vision-flash'] +NOTE_SUFFIX = '[下线:上游已下架2026-09-07]' + + +async def main(): + cfg = getConfig() + DBPools(cfg.databases) + db = DBPools() + log = [] + async with db.sqlorContext('pipeline') as sor: + for name in TARGETS: + recs = await sor.sqlExe( + "SELECT id, status, COALESCE(description,'') AS descr FROM llm_model " + "WHERE name=${n}$ LIMIT 1", {"n": name}) + await sor.sqlExe("COMMIT", {}) + if not recs: + log.append('%s: 未找到(跳过)' % name) + continue + mid = recs[0].id + old_status = recs[0].status + if old_status == 'disabled': + log.append('%s: 已是 disabled(幂等跳过)' % name) + else: + descr = recs[0].descr or '' + new_descr = (descr + ' ' + NOTE_SUFFIX).strip() \ + if NOTE_SUFFIX not in descr else descr + if not DRY: + await sor.sqlExe( + "UPDATE llm_model SET status='disabled', description=${d}$, " + "updated_at=NOW() WHERE id=${i}$", {"d": new_descr[:1000], "i": mid}) + await sor.sqlExe("COMMIT", {}) + log.append('%s: status %s→disabled(描述追加下线标注)' % (name, old_status)) + # 产品同步下架 + prec = await sor.sqlExe( + "SELECT id, status FROM product WHERE resource_ref_id=${r}$", {"r": mid}) + await sor.sqlExe("COMMIT", {}) + if prec: + p_old = prec[0].status + if p_old == '0': + log.append(' 产品: 已下架(幂等跳过)') + else: + if not DRY: + await sor.sqlExe( + "UPDATE product SET status='0', updated_at=NOW() WHERE id=${i}$", + {"i": prec[0].id}) + await sor.sqlExe("COMMIT", {}) + log.append(' 产品: status %s→0(商城下架)' % p_old) + else: + log.append(' 产品: 无映射行(跳过)') + print(json.dumps({'dry': DRY, 'log': log}, ensure_ascii=False, indent=1)) + + +asyncio.run(main())