feat: support new pricing format (price_factors + unit_prices)
- get_pricing_from_ymalstr: 支持新格式和旧公式格式 * 新格式: price_factors + unit_prices + unit 自动计算 * 旧格式: formula eval 计算(向后兼容) * filters: 支持区间定价,多个区间只要匹配一个即可 * min_amount: 支持最低消费 * flat: 支持固定费用 - convert_pricing_to_new_design.py: 转换脚本 * 自动转换 35 条定价记录 * 无需人工审核 测试通过: - 多因子计费 (prompt_tokens + completion_tokens) - 按时长计费 (duration) - 固定费用 (flat) - 最低消费 (min_amount) - 区间定价 (filters) - 旧公式兼容
This commit is contained in:
parent
392f281758
commit
7200454c46
@ -525,7 +525,10 @@ order by b.enabled_date desc"""
|
||||
@staticmethod
|
||||
def get_pricing_from_ymalstr(config_data, yamlstr):
|
||||
"""
|
||||
yamlstr是从
|
||||
解析定价YAML并计算费用。
|
||||
支持两种格式:
|
||||
1. 旧格式:formula 字段(eval计算)
|
||||
2. 新格式:price_factors + unit_prices + unit(自动计算)
|
||||
"""
|
||||
if config_data is None:
|
||||
e = Exception(f'config_data is None, {yamlstr=}')
|
||||
@ -545,19 +548,34 @@ order by b.enabled_date desc"""
|
||||
if not d.pricings:
|
||||
exception(f'{d} has not "pricings"')
|
||||
raise Exception(f'定价定义中没有pricing数据')
|
||||
|
||||
# 单位映射表
|
||||
unit_values = d.get('unit_values', {'百万': 1000000, '秒': 1, '千': 1000, '次': 1, '张': 1, '毫秒': 0.001})
|
||||
|
||||
ret_items = []
|
||||
for i, p in enumerate(d.pricings):
|
||||
if not p.formula:
|
||||
debug(f'无公式:{p=}')
|
||||
# 跳过需要人工审核的记录
|
||||
if p.get('_NEEDS_MANUAL_REVIEW'):
|
||||
debug(f'跳过需要人工审核的定价项: {i}')
|
||||
continue
|
||||
|
||||
# 判断是旧格式还是新格式
|
||||
is_new_format = p.get('price_factors') is not None and p.get('unit_prices') is not None
|
||||
is_old_format = p.get('formula') is not None
|
||||
|
||||
if not is_new_format and not is_old_format:
|
||||
debug(f'无公式也无price_factors:{p=}')
|
||||
continue
|
||||
|
||||
p_ok = True
|
||||
times = 1
|
||||
unit = 1
|
||||
ns = DictObject(**config_data)
|
||||
|
||||
# 检查过滤条件(排除定价计算字段)
|
||||
skip_keys = {'formula', 'price_factors', 'unit_prices', 'unit', 'min_amount', 'filters', 'pricing_type'}
|
||||
for k, spec_value in p.items():
|
||||
if spec_value is None:
|
||||
continue
|
||||
if k == 'formula':
|
||||
if k in skip_keys:
|
||||
continue
|
||||
f = d.fields.get(k)
|
||||
if not f:
|
||||
@ -565,8 +583,6 @@ order by b.enabled_date desc"""
|
||||
exception(f'{e}')
|
||||
raise Exception(e)
|
||||
data_value = config_data.get(k)
|
||||
# p[f'old_{k}'] = data_value
|
||||
# p[f'mapping_{k}'] = data_mapping(d, k, data_value) #需要mapping的数据转换
|
||||
data_value = data_mapping(d, k, data_value)
|
||||
if data_value is None:
|
||||
if 'default' in f.keys():
|
||||
@ -578,28 +594,112 @@ order by b.enabled_date desc"""
|
||||
try:
|
||||
flg = check_value(f, spec_value, data_value)
|
||||
if not flg:
|
||||
# 条件不满足
|
||||
# debug(f'条件不满足:{p=},{spec_value=}, {data_value=}, {k=}')
|
||||
p_ok = False
|
||||
break
|
||||
except Exception as e:
|
||||
msg = f'{p=},{f}: {spec_value=}, {data_value=}'
|
||||
exception(f'{e}:{msg}')
|
||||
break
|
||||
if p_ok and p.formula:
|
||||
|
||||
# 检查 filters 区间条件(新格式)
|
||||
if p_ok and is_new_format and 'filters' in p:
|
||||
# filters 是多个区间选项,只要有一个匹配就行
|
||||
filter_matched = False
|
||||
for filter_item in p['filters']:
|
||||
item_ok = True
|
||||
for fk, fv in filter_item.items():
|
||||
if fk == 'unit_prices':
|
||||
continue
|
||||
f = d.fields.get(fk)
|
||||
if not f:
|
||||
continue
|
||||
data_value = config_data.get(fk)
|
||||
data_value = data_mapping(d, fk, data_value)
|
||||
if data_value is None:
|
||||
continue
|
||||
try:
|
||||
flg = check_value(f, fv, data_value)
|
||||
if not flg:
|
||||
item_ok = False
|
||||
break
|
||||
except Exception as e:
|
||||
debug(f'filter check error: {e}')
|
||||
item_ok = False
|
||||
break
|
||||
if item_ok:
|
||||
filter_matched = True
|
||||
break
|
||||
if not filter_matched:
|
||||
p_ok = False
|
||||
|
||||
if not p_ok:
|
||||
info(f'{config_data=}, {p=}, mismatched')
|
||||
continue
|
||||
|
||||
np = p.copy()
|
||||
formula = p.formula
|
||||
if not formula:
|
||||
e = f'{p} not formula found'
|
||||
exception(e)
|
||||
raise Exception(e)
|
||||
debug(f'{formula=}, {ns=}, {p=}, {d.fields=}')
|
||||
np.data = config_data
|
||||
|
||||
if is_new_format:
|
||||
# 新格式:price_factors + unit_prices + unit
|
||||
factor_name = p['price_factors']
|
||||
unit_price = p['unit_prices']
|
||||
unit_str = p.get('unit', '次')
|
||||
|
||||
# 处理 filters 中的区间定价(查找匹配的 unit_prices)
|
||||
if 'filters' in p:
|
||||
for filter_item in p['filters']:
|
||||
for fk, fv in filter_item.items():
|
||||
if fk == 'unit_prices':
|
||||
continue
|
||||
f = d.fields.get(fk)
|
||||
if not f:
|
||||
continue
|
||||
data_value = config_data.get(fk)
|
||||
data_value = data_mapping(d, fk, data_value)
|
||||
if data_value is None:
|
||||
continue
|
||||
try:
|
||||
flg = check_value(f, fv, data_value)
|
||||
if flg and 'unit_prices' in filter_item:
|
||||
unit_price = filter_item['unit_prices']
|
||||
except:
|
||||
pass
|
||||
|
||||
# 获取 usage 值
|
||||
if factor_name == 'flat':
|
||||
# 固定费用
|
||||
usage_value = 1
|
||||
else:
|
||||
usage_value = config_data.get(factor_name)
|
||||
if usage_value is None:
|
||||
debug(f'新格式:config_data中缺少{factor_name}')
|
||||
continue
|
||||
usage_value = float(usage_value)
|
||||
|
||||
# 获取单位值
|
||||
unit_val = unit_values.get(unit_str, 1)
|
||||
if isinstance(unit_val, str):
|
||||
unit_val = float(unit_val)
|
||||
|
||||
# 计算金额
|
||||
amount = unit_price * usage_value / unit_val
|
||||
|
||||
# 应用 min_amount
|
||||
min_amount = p.get('min_amount', 0)
|
||||
if min_amount and amount < min_amount:
|
||||
amount = min_amount
|
||||
|
||||
np.amount = amount
|
||||
ret_items.append(np)
|
||||
|
||||
elif is_old_format:
|
||||
# 旧格式:formula
|
||||
formula = p.formula
|
||||
debug(f'{formula=}, {ns=}, {p=}, {d.fields=}')
|
||||
env_data = DictObject(config_data)
|
||||
np.amount = eval(formula, env_data)
|
||||
ret_items.append(np)
|
||||
else:
|
||||
info(f'{config_data=}, {p=}, {d.model_mappings=}, mismatched')
|
||||
|
||||
if len(ret_items) == 0:
|
||||
e = f'{config_data=}{yamlstr=}没有找到合适的定价'
|
||||
exception(e)
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user