246 lines
5.5 KiB
Python
246 lines
5.5 KiB
Python
import json
|
||
import yaml
|
||
from ahserver.serverenv import ServerEnv
|
||
from sqlor.dbpools import DBPools, get_sor_context
|
||
from appPublic.log import debug, exception
|
||
from appPublic.dictObject import DictObject
|
||
|
||
import yaml
|
||
|
||
"""
|
||
采用yaml描述定价策略,
|
||
一下是一个例子
|
||
pricing_unit: 1000
|
||
- id: input_token_pricing_1
|
||
field: prompt_tokens
|
||
between: 0 ~= 1000
|
||
- id: input_token_pricing_2
|
||
- cnt_field: prompt_tokens
|
||
between: 1000 ~= 100000
|
||
- id: input_token_pricing_2
|
||
- cnt_field: prompt_tokens
|
||
between: 100000 ~=
|
||
- id: output_pricing
|
||
- cnt_field: completion_tokens
|
||
|
||
视频定价
|
||
fields:
|
||
resolution:
|
||
type: str
|
||
label: 分辨率
|
||
value_mode:
|
||
- between
|
||
- in
|
||
- =
|
||
- >
|
||
- >=
|
||
- <
|
||
- <=
|
||
|
||
duration:
|
||
type: int
|
||
label: 时长
|
||
audio:
|
||
type: boolean
|
||
label: 音频
|
||
|
||
pricings:
|
||
- resolution: 480p
|
||
duration: 4
|
||
audio: false
|
||
- resolution: 480p
|
||
duration: 8
|
||
audio: false
|
||
- resolution: 480p
|
||
duration: 12
|
||
audio: false
|
||
- resolution: 480p
|
||
duration: 4
|
||
audio: true
|
||
- resolution: 480p
|
||
duration: 8
|
||
audio: true
|
||
- resolution: 480p
|
||
duration: 12
|
||
audio: true
|
||
- resolution: 720p
|
||
duration: 4
|
||
audio: false
|
||
- resolution: 720p
|
||
duration: 8
|
||
audio: false
|
||
- resolution: 720p
|
||
duration: 12
|
||
audio: false
|
||
- resolution: 720p
|
||
duration: 4
|
||
audio: true
|
||
- resolution: 720p
|
||
duration: 8
|
||
audio: true
|
||
- resolution: 720p
|
||
duration: 12
|
||
audio: true
|
||
|
||
|
||
"""
|
||
|
||
"""
|
||
typefuncs = {
|
||
'int': int,
|
||
'float': float
|
||
}
|
||
def typevalue(v, t):
|
||
if not v:
|
||
return None
|
||
f = typefuncs.get(t)
|
||
if not f:
|
||
return v
|
||
return f(v)
|
||
|
||
def check_value(field, spec_value, data_value):
|
||
if field.value_mode == 'between':
|
||
arr = spec_value.split(' ')
|
||
if len(arr) < 2 or len(arr) > 3:
|
||
e = f'{spec_value=} error'
|
||
exception(e)
|
||
raise Exception(e)
|
||
|
||
if (arr[0] is None or arr[-1] is None) and field.type == 'str':
|
||
e = f'字符串类型between方法的两个值任何一个都不能为空')
|
||
exception(e)
|
||
raise e
|
||
else:
|
||
if arr[0] is None:
|
||
arr[0] = -float('inf')
|
||
if arr[-1] is None:
|
||
arr[-1] = float('inf')
|
||
|
||
if len(arr) == 2 or arr[1] == '=~' :
|
||
return arr[0] <= data_value and data_value < arr[-1]
|
||
if arr[1] == '~':
|
||
return arr[0] < data_value and data_value < arr[-1]
|
||
if arr[1] == '~=':
|
||
return arr[0] < data_value and data_value <= arr[-1]
|
||
e = f'{arr[1]}不认识的期间逻辑,只支持:~ =~ ~='
|
||
exception(e)
|
||
raise Exception(e)
|
||
|
||
if field.value_mode == 'in':
|
||
arr = spec_value.split(' ')
|
||
arr = [ typevalue(a, field.type) for a in arr ]
|
||
return data_value in arr
|
||
|
||
mode = field.value_mode
|
||
if not mode:
|
||
mode = '='
|
||
script = f'{data_value} {mode} {typevalue(spec_value, field.type)}'
|
||
x = eval(scrpt)
|
||
return x
|
||
|
||
class PricingProgram:
|
||
def parse_pricing_spec(self, yamlstr):
|
||
d = yaml.safe_load(yamlstr)
|
||
assert isinstance(d, list)
|
||
return d
|
||
async def add_pricing_program(self, sor,
|
||
name, ownerid,
|
||
description, pricing_spec, id=None):
|
||
env = ServerEnv()
|
||
if not id:
|
||
id = env.uuid()
|
||
yamlstr = yaml.dump(pricing_spec)
|
||
await sor.C('pricing_program', {
|
||
'id': id,
|
||
'name': name,
|
||
'ownerid': ownerid,
|
||
'description': description,
|
||
'pricing_spec': ymalstr
|
||
})
|
||
|
||
async def pp_db2app(self, pp):
|
||
try:
|
||
pp.pricing_spec = yaml.safe_load(pp.pricing_spec)
|
||
except Exception as e:
|
||
e = f'{pp.pricing_spec}:yaml数据格式错误'
|
||
exception(e)
|
||
raise Exception(e)
|
||
|
||
async def pp_app2db(self, pp):
|
||
try:
|
||
pp.pricing_spec = yaml.dump(pp.pricing_spec)
|
||
except Exception as e:
|
||
e = f'{pp.pricing_spec}:导出到yaml失败'
|
||
exception(e)
|
||
raise Exception(e)
|
||
async def ppt_db2app(self, ppt):
|
||
try:
|
||
ppt.pricing_data = yaml.safe_load(ppt.pricing_data)
|
||
except Exception as e:
|
||
e = f'{ppt.pricing_data}:yaml数据格式错误'
|
||
exception(e)
|
||
raise Exception(e)
|
||
|
||
async def ppt_app2db(self, ppt):
|
||
try:
|
||
ppt.pricing_data = yaml.dump(ppt.pricing_data)
|
||
except Exception as e:
|
||
e = f'{ppt.pricing_data}:yaml数据格式错误'
|
||
exception(e)
|
||
raise Exception(e)
|
||
|
||
def pricing(self, config_data, yamlstr):
|
||
"""
|
||
yamlstr是从
|
||
d = None
|
||
try:
|
||
d = yaml.safe_load(yamlstr)
|
||
except Exception as e:
|
||
exception(f'yaml.sage_load({yamlstr}) error: {e}')
|
||
raise e
|
||
if not d.fields:
|
||
exception(f'{d} has not "fields"')
|
||
raise Exception(f'定价定义中没有fields数据')
|
||
if not d.pricing:
|
||
exception(f'{d} has not "pricing"')
|
||
raise Exception(f'定价定义中没有pricing数据')
|
||
ret_items = []
|
||
for i, p in enumerate(d.pricings):
|
||
p_ok = True
|
||
times = 1
|
||
unit = 1
|
||
for k,spec_value in p.items():
|
||
f = d.fields.get(k)
|
||
if not f:
|
||
e = f'定价项({i})中的{k}在fields中没有定义'
|
||
exception(f'{e}')
|
||
raise Exception(e)
|
||
data_value = config_data.get(k)
|
||
if not value:
|
||
e = f'数据({config_data})没有({k})数据'
|
||
exception(e)
|
||
raise Exeption(e)
|
||
if f.type == 'unit':
|
||
unit = spec_value
|
||
elif f.type == 'times':
|
||
times = data_value
|
||
else:
|
||
f = check_value(f, spec_value, data_value)
|
||
if not f:
|
||
# 条件不满足
|
||
p_ok = False
|
||
break
|
||
if p_ok:
|
||
if not p.price:
|
||
e = f'{p} 没有价格属性')
|
||
exception(e)
|
||
raise Exception(e)
|
||
np = p.copy()
|
||
np.amount = p.price * float(times) / float(unit)
|
||
ret_items.append(np)
|
||
if len(ret_items) == 0:
|
||
e = f'{config_data=}{yamlstr=}没有找到合适的定价‘
|
||
exception(e)
|
||
raise Exception(e)
|
||
return ret_items
|