docs(pricing): 枚举设计 §4.2/§5.2/§8 同步 mediaPrices v0.4 + 设计文档 §5.2 语法修正

This commit is contained in:
2026-09-01 15:00:32 +08:00
parent 1825ea5581
commit 7ff9743acc
2 changed files with 13 additions and 15 deletions
@@ -104,10 +104,9 @@ rule_snapshot TEXT NOT NULL, -- workflow: 选中模式的规则片段
"unit": "per_1M",
"tiered": false,
"rules": [
{ "name": "规则名", "match": {...}, "price": {...} }
{ "name": "规则名", "match": {...}, "price": {...}, "mediaPrices": {"audio": {...}} }
],
"currency": "CNY",
"discount": null
"currency": "CNY"
}
```
@@ -127,7 +126,6 @@ rule_snapshot TEXT NOT NULL, -- workflow: 选中模式的规则片段
| 字段 | 值 | 说明 | 用于 |
|---|---|---|---|
| `mediaType` | text/audio/video/image | 输入媒体类型 | 视频「输入含/不含视频」、推理「输入含/不含音频」 |
| `thinking` | true/false | 思考/非思考模式 | 推理 |
| `outputAudio` | true/false | 输出有声/无声 | 视频 |
| `outputResolution` | 自由字符串 | 输出分辨率(视频 480p/720p/1080p/2k/4k;图片 512x512/1024x1024 | 视频、图片 |
@@ -136,10 +134,11 @@ rule_snapshot TEXT NOT NULL, -- workflow: 选中模式的规则片段
**price 计价项**
- token 单位(per_1K/per_1M):`input`(输入单价)、`output`(输出单价)、`cacheHit`(缓存命中输入单价,缺省 0=无优惠)
- 非 token 单位(per_1/per_second/per_minute/per_hour/per_char):`unitPrice`(每单位单价)
- **mediaPrices 媒体类型价**(可选):`媒体类型 → price` 的 map,键 ∈ {text,audio,video,image}。输入含该媒体时用对应价;否则用默认 `price`(**默认价 = 输入不含以下媒体**)。一个规则可挂多套媒体价;缺 key 即走默认价。
**tiered 阶梯(分档不累加)**:仅 token 单位;`tiered:true` 表示按输入 token 分档——档位区间相邻不重叠(下档 max+1=上档 min),结算按 promptTokens 命中哪档就整单按哪档 input/output/cacheHit 价;`tiered:false` 规则间靠其他维度区分。
> 与 model-gateway `PriceConfig` 的关系:不复用其字段结构(`inputAudio/cacheHitAudio/cacheStorageHour` 删除,音频输入差价改由 `mediaType:"audio"` 命中独立规则表达)。规则级/模型级 `discount` 字段保留(本期模型结算未接,仅建模)
> 与 model-gateway `PriceConfig` 的关系:不复用其字段结构(`inputAudio/cacheHitAudio/cacheStorageHour` 删除,音频输入差价改由 `mediaPrices.audio` 表达)。`discount` 废弃(代码无实现)。v0.4 修订全文见 `2026-09-01-pricing-media-price-design.md`
**示例**
@@ -148,8 +147,8 @@ rule_snapshot TEXT NOT NULL, -- workflow: 选中模式的规则片段
{
"unit": "per_second", "tiered": false,
"rules": [
{ "name": "无声-720p-输入不含视频", "match": {"outputAudio": false, "outputResolution": "720p", "mediaType": "text"}, "price": {"unitPrice": 0.8} },
{ "name": "有声-1080p-输入含视频", "match": {"outputAudio": true, "outputResolution": "1080p", "mediaType": "video"}, "price": {"unitPrice": 1.5} }
{ "name": "无声-720p", "match": {"outputAudio": false, "outputResolution": "720p"}, "price": {"unitPrice": 0.8}, "mediaPrices": {"video": {"unitPrice": 1.0}} },
{ "name": "有声-1080p", "match": {"outputAudio": true, "outputResolution": "1080p"}, "price": {"unitPrice": 1.5}, "mediaPrices": {"video": {"unitPrice": 2.0}} }
]
}
```
@@ -159,10 +158,9 @@ rule_snapshot TEXT NOT NULL, -- workflow: 选中模式的规则片段
{
"unit": "per_1M", "tiered": true,
"rules": [
{ "name": "思考-输入≤32000-文本", "match": {"thinking": true, "inputLengthMax": 32000, "mediaType": "text"}, "price": {"input": 0.6, "output": 3.6, "cacheHit": 0.12} },
{ "name": "思考-输入32001~128000-文本", "match": {"thinking": true, "inputLengthMin": 32001, "inputLengthMax": 128000, "mediaType": "text"}, "price": {"input": 0.9, "output": 5.4, "cacheHit": 0.18} },
{ "name": "思考-输入含音频", "match": {"thinking": true, "mediaType": "audio"}, "price": {"input": 9, "output": 3.6, "cacheHit": 1.8} },
{ "name": "非思考-统一价", "match": {"thinking": false}, "price": {"input": 0.3, "output": 1.2, "cacheHit": 0.06} }
{ "name": "思考-输入≤32000", "match": {"thinking": true, "inputLengthMax": 32000}, "price": {"input": 0.6, "output": 3.6, "cacheHit": 0.12}, "mediaPrices": {"audio": {"input": 9, "output": 3.6, "cacheHit": 1.8}} },
{ "name": "思考-输入32001~128000", "match": {"thinking": true, "inputLengthMin": 32001, "inputLengthMax": 128000}, "price": {"input": 0.9, "output": 5.4, "cacheHit": 0.18}, "mediaPrices": {"audio": {"input": 13.5, "output": 5.4, "cacheHit": 2.7}} },
{ "name": "思考-统一价", "match": {"thinking": false}, "price": {"input": 0.3, "output": 1.2, "cacheHit": 0.06} }
]
}
```
@@ -178,7 +176,7 @@ rule_snapshot TEXT NOT NULL, -- workflow: 选中模式的规则片段
] }
```
- Save 校验:`unit ∈ {per_1K, per_1M, per_1, per_second, per_minute, per_hour, per_char}``tiered:true` 仅 token 单位且 inputLength 档相邻不重叠match 字段取值合法;price 非负。
- Save 校验:`unit ∈ {per_1K, per_1M, per_1, per_second, per_minute, per_hour, per_char}``tiered:true` 仅 token 单位且 inputLength 档按下界排序相邻不重叠(上不封顶档须最后);mediaPrices 键 ∈ {text,audio,video,image} 且值非空match 字段取值合法;price 非负。
### 4.3 business —— 周期订阅(per_period
@@ -207,7 +205,7 @@ per_item | per_second | per_token | per_1K | per_1M | per_1 | per_minute | per_h
- **单位计算器**(注册 per_1/per_second/per_minute/per_hour/per_char 五键,base=1/1/60/3600/1,用量源=张数/秒/秒/秒/字数):`cost = 用量/base×unitPrice`
- **阶梯=分档不累加**tiered 只影响 match 语义(按输入 token 档)与校验(档相邻不重叠),无分段累加逻辑。
- **无规则命中 → 报错「无匹配计费规则」**(配置缺失建单即暴露,不静默收 0)。
- 折扣:规则级/模型级 `discount` 字段保留(本期模型结算未接,仅建模)。
- 折扣:废弃(v0.4 修订为规则级 `mediaPrices`,见 `2026-09-01-pricing-media-price-design.md`)。
### 5.3 ChargeUsage 扩展(模型计价入参,本期备用)
@@ -293,7 +291,7 @@ type ChargeUsage struct {
| 周期计费用 per_period 通用建模 | 扩月/季只扩 period 允许集合,不改代码结构 |
| 阶梯=分档不累加 | 用户确认;匹配统一 rules 首条命中,tiered 仅约束 inputLength 分档校验 |
| match 维度全可选 + 首条命中 | 与 model-gateway 移植逻辑一致,规则即优先级 |
| 价格项收敛 input/output/cacheHit + unitPrice | 四类模型一套结构;音频输入差价用 mediaType:"audio" 规则表达(删 inputAudio/cacheHitAudio/cacheStorageHour |
| 价格项收敛 input/output/cacheHit + unitPrice | 四类模型一套结构;音频输入差价用 `mediaPrices.audio` 表达(删 inputAudio/cacheHitAudio/cacheStorageHour 与 match.mediaType |
| 输出分辨率自由字符串 | 视频/图片枚举值管理端 UI 下拉提供,DB 不预设 |
---
@@ -156,7 +156,7 @@ func validateTieredBands(rules []modelRule) error {
```go
if len(r.Rules[i].MediaPrices) > 0 {
for mt, mp := range r.Rules[i].MediaPrices {
if !pricingConsts.ModelMediaSet[mt] {
if _, ok := pricingConsts.ModelMediaSet[mt]; !ok {
return nil, errors.New("model 费率 mediaPrices 键须为 text/audio/video/image")
}
if mp == nil {