refactor(llm_model): 删除价格/成本四字段(price/cost input/output)——定价统一走ppid定价平台,记账改路后为死库存;结算cost恒0,展示/列表/迁移同步清理

This commit is contained in:
yumoqing 2026-09-04 21:02:58 +08:00
parent 6feacaf51c
commit f965e4b4d6
7 changed files with 13 additions and 55 deletions

View File

@ -76,38 +76,6 @@
"type": "str", "type": "str",
"length": 32 "length": 32
}, },
{
"name": "price_input",
"title": "输入单价(元/千token)",
"type": "float",
"length": 12,
"dec": 6,
"default": "0"
},
{
"name": "price_output",
"title": "输出单价(元/千token)",
"type": "float",
"length": 12,
"dec": 6,
"default": "0"
},
{
"name": "cost_input",
"title": "输入成本(元/千token)",
"type": "float",
"length": 12,
"dec": 6,
"default": "0"
},
{
"name": "cost_output",
"title": "输出成本(元/千token)",
"type": "float",
"length": 12,
"dec": 6,
"default": "0"
},
{ {
"name": "default_params", "name": "default_params",
"title": "默认参数(JSON)", "title": "默认参数(JSON)",

View File

@ -43,10 +43,6 @@ CREATE TABLE IF NOT EXISTS llm_model (
`profile_id` varchar(32) comment '适配模板ID', `profile_id` varchar(32) comment '适配模板ID',
`query_profile_ids` text comment '后续步骤模板链(JSON数组,异步模型提交后顺序执行:查询/下载)', `query_profile_ids` text comment '后续步骤模板链(JSON数组,异步模型提交后顺序执行:查询/下载)',
`ppid` varchar(32) comment '定价项目ID(pricing_program)', `ppid` varchar(32) comment '定价项目ID(pricing_program)',
`price_input` decimal(12,6) NOT NULL DEFAULT 0 comment '输入单价(元/千token)',
`price_output` decimal(12,6) NOT NULL DEFAULT 0 comment '输出单价(元/千token)',
`cost_input` decimal(12,6) NOT NULL DEFAULT 0 comment '输入成本(元/千token)',
`cost_output` decimal(12,6) NOT NULL DEFAULT 0 comment '输出成本(元/千token)',
`default_params` text comment '默认参数(JSON)', `default_params` text comment '默认参数(JSON)',
`status` varchar(20) NOT NULL DEFAULT 'active' comment '状态', `status` varchar(20) NOT NULL DEFAULT 'active' comment '状态',
`description` text comment '描述', `description` text comment '描述',

View File

@ -199,7 +199,7 @@ async def _model_chain(sor, org_id: str, model_name: str, purpose: str = ''):
for mid in chain_ids: for mid in chain_ids:
recs = await sor.sqlExe( recs = await sor.sqlExe(
"SELECT id, name, vendor_id, vendor_model_id, capability, default_params, " "SELECT id, name, vendor_id, vendor_model_id, capability, default_params, "
"price_input, price_output, cost_input, cost_output, ppid, org_id " "ppid, org_id "
"FROM llm_model WHERE id=${i}$ AND status='active' LIMIT 1", {"i": mid}) "FROM llm_model WHERE id=${i}$ AND status='active' LIMIT 1", {"i": mid})
await sor.sqlExe("COMMIT", {}) await sor.sqlExe("COMMIT", {})
if recs: if recs:
@ -496,9 +496,10 @@ async def govern_settle(ctx: dict, ok_call: bool, req_tokens: int = 0, resp_toke
db, dbname = _get_db() db, dbname = _get_db()
try: try:
model = ctx.get('model_row') or {} model = ctx.get('model_row') or {}
cost_in = _fnum(model.get('cost_input')) # 2026-09-04:模型表不再存单价/成本四字段——成本与售价统一走 ppid 定价引擎,
cost_out = _fnum(model.get('cost_output')) # 出账由异步记账循环(accounting.py)经产品层计算;此处 cost 恒 0,
cost = req_tokens / 1000.0 * cost_in + resp_tokens / 1000.0 * cost_out # 供应商账号余额不再按调用扣减(真实成本以定价引擎结算为准)。
cost = 0.0
policy_org = ctx.get('policy_org_id') or '' policy_org = ctx.get('policy_org_id') or ''
caller_org = ctx.get('org_id') or '' caller_org = ctx.get('org_id') or ''
# owner 模型 = 生效策略是平台('0') 且调用方不是平台自己 # owner 模型 = 生效策略是平台('0') 且调用方不是平台自己

View File

@ -371,7 +371,7 @@ async def list_models(org_id, catelogid='', limit=200):
"""按能力分类列模型(对齐 llmage v1/models 的分类方式)。 """按能力分类列模型(对齐 llmage v1/models 的分类方式)。
catelogid 空 = 全部能力。返回 [{'id','name','model_id','capability', catelogid 空 = 全部能力。返回 [{'id','name','model_id','capability',
'vendor','status','price_input','price_output'}]。 'vendor','status'}]。
机构未配策略(无可用模型)返回空列表,由调用方决定是否提示。 机构未配策略(无可用模型)返回空列表,由调用方决定是否提示。
""" """
from .gateway import _active_policy_org, _get_db from .gateway import _active_policy_org, _get_db
@ -381,7 +381,7 @@ async def list_models(org_id, catelogid='', limit=200):
if not await _active_policy_org(sor, org_id or ''): if not await _active_policy_org(sor, org_id or ''):
return rows return rows
sql = ("SELECT m.id, m.name, m.vendor_model_id, m.capability, m.status, " sql = ("SELECT m.id, m.name, m.vendor_model_id, m.capability, m.status, "
"m.price_input, m.price_output, v.name AS vendor_name " "v.name AS vendor_name "
"FROM llm_model m LEFT JOIN llm_vendor v ON v.id=m.vendor_id " "FROM llm_model m LEFT JOIN llm_vendor v ON v.id=m.vendor_id "
"WHERE m.status='active'") "WHERE m.status='active'")
params = {} params = {}
@ -400,8 +400,6 @@ async def list_models(org_id, catelogid='', limit=200):
'capability': getattr(r, 'capability', '') or 't2t', 'capability': getattr(r, 'capability', '') or 't2t',
'vendor': getattr(r, 'vendor_name', '') or '', 'vendor': getattr(r, 'vendor_name', '') or '',
'status': getattr(r, 'status', ''), 'status': getattr(r, 'status', ''),
'price_input': float(_fnum(getattr(r, 'price_input', 0))),
'price_output': float(_fnum(getattr(r, 'price_output', 0))),
}) })
return rows return rows

View File

@ -26,7 +26,7 @@ async def _resolve_model(resource_ref_id):
async with get_sor_context(env, _MODULE_NAME) as sor: async with get_sor_context(env, _MODULE_NAME) as sor:
recs = await sor.sqlExe( recs = await sor.sqlExe(
"SELECT id, name, vendor_model_id, capability, status, ppid, org_id, " "SELECT id, name, vendor_model_id, capability, status, ppid, org_id, "
"price_input, price_output, cost_input, cost_output, description " "description "
"FROM llm_model WHERE id=${i}$ LIMIT 1", {"i": resource_ref_id}) "FROM llm_model WHERE id=${i}$ LIMIT 1", {"i": resource_ref_id})
await sor.sqlExe("COMMIT", {}) await sor.sqlExe("COMMIT", {})
if not recs: if not recs:
@ -35,18 +35,17 @@ async def _resolve_model(resource_ref_id):
async def get_product_display(resource_ref_id): async def get_product_display(resource_ref_id):
"""展示定价信息。""" """展示定价信息。2026-09-04 起模型表不存单价,定价统一走 ppid 定价引擎。"""
m, err = await _resolve_model(resource_ref_id) m, err = await _resolve_model(resource_ref_id)
if err: if err:
return {'success': False, 'message': err} return {'success': False, 'message': err}
pi = float(m.get('price_input') or 0) ppid = m.get('ppid') or ''
po = float(m.get('price_output') or 0) pricing_text = '按定价方案计费(ppid=%s)' % ppid if ppid else '未挂定价方案'
pricing_text = '输入 %.4f 元/千token,输出 %.4f 元/千token' % (pi, po) if (pi or po) else '未定价'
return { return {
'success': True, 'success': True,
'pricing_text': pricing_text, 'pricing_text': pricing_text,
'pricing_detail': { 'pricing_detail': {
'price_input': pi, 'price_output': po, 'ppid': ppid,
'capability': m.get('capability') or 't2t', 'capability': m.get('capability') or 't2t',
'model_id': m.get('vendor_model_id') or m.get('name', ''), 'model_id': m.get('vendor_model_id') or m.get('name', ''),
}, },
@ -156,7 +155,7 @@ async def load_product_category_product(parent_category_id):
async with get_sor_context(env, _MODULE_NAME) as sor: async with get_sor_context(env, _MODULE_NAME) as sor:
models = await sor.sqlExe( models = await sor.sqlExe(
"SELECT id, name, vendor_model_id, capability, status, org_id, description, " "SELECT id, name, vendor_model_id, capability, status, org_id, description, "
"ppid, price_input, price_output FROM llm_model " "ppid FROM llm_model "
"WHERE status='active' ORDER BY capability, name", {}) "WHERE status='active' ORDER BY capability, name", {})
await sor.sqlExe("COMMIT", {}) await sor.sqlExe("COMMIT", {})
caps = await sor.sqlExe( caps = await sor.sqlExe(

View File

@ -160,8 +160,6 @@ async def main(dry=False):
'capability': cap, 'sync_mode': 'sync', 'capability': cap, 'sync_mode': 'sync',
'profile_id': '', 'query_profile_ids': '', 'profile_id': '', 'query_profile_ids': '',
'ppid': '', 'ppid': '',
'price_input': 0, 'price_output': 0,
'cost_input': 0, 'cost_output': 0,
'default_params': '', 'status': 'active', 'default_params': '', 'status': 'active',
'description': '由旧 llm 表迁移生成', 'org_id': '0', 'description': '由旧 llm 表迁移生成', 'org_id': '0',
}) })

View File

@ -39,7 +39,5 @@ for r in rows:
"name": r.get('name', ''), "name": r.get('name', ''),
"catelogid": r.get('capability', ''), "catelogid": r.get('capability', ''),
"vendor": r.get('vendor', ''), "vendor": r.get('vendor', ''),
"price_input": r.get('price_input', 0),
"price_output": r.get('price_output', 0),
}) })
return json.dumps(out, ensure_ascii=False) return json.dumps(out, ensure_ascii=False)