refactor(pricing): modelRule 加 mediaPrices + match 移除 mediaType
This commit is contained in:
@@ -22,6 +22,11 @@ var ModelUnitSet = map[ChargeMode]bool{
|
||||
ChargeModePerSecond: true, ChargeModePerMinute: true, ChargeModePerHour: true, ChargeModePerChar: true,
|
||||
}
|
||||
|
||||
// ModelMediaSet 模型费率 mediaPrices 允许的媒体类型键(输入媒体白名单,前端下拉同源)
|
||||
var ModelMediaSet = map[string]struct{}{
|
||||
"text": {}, "audio": {}, "video": {}, "image": {},
|
||||
}
|
||||
|
||||
// PeriodSet 周期允许集合(per_period,扩月/季只加值)
|
||||
var PeriodSet = map[string]bool{"year": true}
|
||||
|
||||
|
||||
@@ -12,11 +12,10 @@ import (
|
||||
// ====================== 用量结构 ======================
|
||||
|
||||
// ChargeUsage 实际用量(结算入参,工作流/调用方上报)。
|
||||
// DurationSec 秒、ItemCount 条数、TokensByModel 各模型token数(per_token 用)。
|
||||
// DurationSec 秒、TokensByModel 各模型token数(per_token 用)。
|
||||
// 模型计价字段见各计算器注释(spec §5.3)。
|
||||
type ChargeUsage struct {
|
||||
DurationSec float64 `json:"durationSec"` // 时长(秒)
|
||||
ItemCount int64 `json:"itemCount"` // 条数(按条计费可选)
|
||||
TokensByModel map[string]int64 `json:"tokensByModel"` // 各模型消耗token
|
||||
// —— 模型计价扩展(spec §5.3)——
|
||||
PromptTokens int64 `json:"promptTokens"` // token 计费用:输入
|
||||
@@ -55,7 +54,6 @@ type perSecondRules struct {
|
||||
// calculator 计费方式计算器(新增方式:实现该接口并按 subject_type 注册进对应分表)。
|
||||
// rules 为 rulesJSON 解析后的强类型费率结构;所有金额单位均为元(保留2位小数)。
|
||||
type calculator interface {
|
||||
mode() pricingConsts.ChargeMode
|
||||
// validate 解析并校验 rulesJSON(SaveConfig 与结算时调用)
|
||||
validate(rulesJSON string) (interface{}, error)
|
||||
// charge 结算实际金额(元),不足1分向上取整
|
||||
@@ -130,8 +128,6 @@ func calcCharge(subjectType pricingConsts.SubjectType, mode pricingConsts.Charge
|
||||
|
||||
type perItemCalculator struct{}
|
||||
|
||||
func (perItemCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerItem }
|
||||
|
||||
func (perItemCalculator) validate(rulesJSON string) (interface{}, error) {
|
||||
var rules perItemRules
|
||||
if err := json.Unmarshal([]byte(rulesJSON), &rules); err != nil {
|
||||
@@ -160,8 +156,6 @@ func (perItemCalculator) charge(rules interface{}, usage *ChargeUsage) (float64,
|
||||
|
||||
type perSecondCalculator struct{}
|
||||
|
||||
func (perSecondCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerSecond }
|
||||
|
||||
func (perSecondCalculator) validate(rulesJSON string) (interface{}, error) {
|
||||
var rules perSecondRules
|
||||
if err := json.Unmarshal([]byte(rulesJSON), &rules); err != nil {
|
||||
@@ -183,8 +177,6 @@ func (perSecondCalculator) charge(rules interface{}, usage *ChargeUsage) (float6
|
||||
|
||||
type perTokenCalculator struct{}
|
||||
|
||||
func (perTokenCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerToken }
|
||||
|
||||
func (perTokenCalculator) validate(rulesJSON string) (interface{}, error) {
|
||||
// per_token 为 {} 仅启用标记;价格取自各模型 subject 实时配置(spec §5.4)
|
||||
var container map[string]interface{}
|
||||
@@ -210,14 +202,14 @@ type modelRules struct {
|
||||
}
|
||||
|
||||
type modelRule struct {
|
||||
Name string `json:"name"`
|
||||
Match *modelMatch `json:"match,omitempty"` // 空=任意调用
|
||||
Price *modelPrice `json:"price"`
|
||||
Name string `json:"name"`
|
||||
Match *modelMatch `json:"match,omitempty"` // 空=任意调用
|
||||
Price *modelPrice `json:"price"` // 默认价:输入不含以下媒体
|
||||
MediaPrices map[string]*modelPrice `json:"mediaPrices,omitempty"` // 媒体类型→该媒体价(输入含该媒体时)
|
||||
}
|
||||
|
||||
// modelMatch 命中条件(全可选,命中=全部满足)
|
||||
type modelMatch struct {
|
||||
MediaType string `json:"mediaType,omitempty"` // text/audio/video/image
|
||||
Thinking *bool `json:"thinking,omitempty"` // 思考/非思考
|
||||
OutputAudio *bool `json:"outputAudio,omitempty"` // 输出有声/无声
|
||||
OutputResolution string `json:"outputResolution,omitempty"` // 输出分辨率
|
||||
@@ -244,9 +236,6 @@ func modelRuleMatch(m *modelMatch, u *ChargeUsage) bool {
|
||||
if m == nil {
|
||||
return true
|
||||
}
|
||||
if m.MediaType != "" && m.MediaType != u.MediaType {
|
||||
return false
|
||||
}
|
||||
if m.Thinking != nil && (u.Thinking == nil || *m.Thinking != *u.Thinking) {
|
||||
return false
|
||||
}
|
||||
@@ -281,8 +270,6 @@ func matchModelRule(r *modelRules, u *ChargeUsage) *modelRule {
|
||||
// cost = (prompt-cached)/base×input + cached/base×cacheHit + completion/base×output
|
||||
type modelTokenCalculator struct{ base float64 }
|
||||
|
||||
func (modelTokenCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePer1M }
|
||||
|
||||
func (c modelTokenCalculator) validate(rulesJSON string) (interface{}, error) {
|
||||
var r modelRules
|
||||
if err := json.Unmarshal([]byte(rulesJSON), &r); err != nil {
|
||||
@@ -355,8 +342,6 @@ type modelUnitCalculator struct {
|
||||
usage func(*ChargeUsage) float64
|
||||
}
|
||||
|
||||
func (modelUnitCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerSecond }
|
||||
|
||||
func (c modelUnitCalculator) validate(rulesJSON string) (interface{}, error) {
|
||||
var r modelRules
|
||||
if err := json.Unmarshal([]byte(rulesJSON), &r); err != nil {
|
||||
@@ -394,8 +379,6 @@ func usageImageCount(u *ChargeUsage) float64 { return float64(u.ImageCount) }
|
||||
|
||||
type perPeriodCalculator struct{}
|
||||
|
||||
func (perPeriodCalculator) mode() pricingConsts.ChargeMode { return pricingConsts.ChargeModePerPeriod }
|
||||
|
||||
func (perPeriodCalculator) validate(rulesJSON string) (interface{}, error) {
|
||||
var r perPeriodRules
|
||||
if err := json.Unmarshal([]byte(rulesJSON), &r); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user