feat(region_pricing): 扩展非 token 统一计费字段,支持语音按字符/秒计费
Some checks failed
CI / go-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / docker-build (push) Has been cancelled

- 新增 region_pricing.pricing_mode / price_unit / flat_price 字段
- 新增 migration 016_region_pricing_non_token_units.sql
- officialPricingRecord 新增 PricingMode/PriceUnit/FlatPrice 字段
- detectModality 新增 audio 模态检测(voice/audio/speech)
- providerMetadata 新增 BAAI/ByteDance/China Mobile 元数据
- import_mobile_cloud_pricing.go: 解析语音计费表(CosyVoice/SenseVoice)
  - CosyVoice: 2元/万字符 → pricingMode=flat, priceUnit=10k_characters
  - SenseVoice: 0.0007元/秒 → pricingMode=flat, priceUnit=second
- mobileCloudProviderName 新增 cosyvoice/sensevoice → Alibaba 映射
- cmd/server: modelResponse 新增 pricingMode/priceUnit/flatPrice,API 字段说明同步更新
- 新增 TestModelsHandlerReturnsFlatPricingFields 测试
This commit is contained in:
phamnazage-jpg
2026-05-22 14:51:38 +08:00
parent 1db813cb6b
commit 5c5578a19b
7 changed files with 776 additions and 2 deletions

View File

@@ -34,6 +34,9 @@ type officialPricingRecord struct {
OperatorType string
Region string
Currency string
PricingMode string
PriceUnit string
FlatPrice float64
InputPrice float64
OutputPrice float64
ContextLength int
@@ -81,16 +84,21 @@ func upsertOfficialPricingRecords(db *sql.DB, records []officialPricingRecord, b
_, err = db.Exec(
`INSERT INTO region_pricing (
model_id, operator_id, region, currency,
pricing_mode, price_unit, flat_price,
input_price_per_mtok, output_price_per_mtok,
is_free, effective_date, source_url, source_type,
free_quota, free_limitations, rate_limit
) VALUES (
$1, $2, $3, $4,
$5, $6, $7, CURRENT_DATE, $8, $9,
$10, $11, $12
$5, $6, $7,
$8, $9, $10, CURRENT_DATE, $11, $12,
$13, $14, $15
)
ON CONFLICT (model_id, operator_id, region, currency, effective_date)
DO UPDATE SET
pricing_mode = EXCLUDED.pricing_mode,
price_unit = EXCLUDED.price_unit,
flat_price = EXCLUDED.flat_price,
input_price_per_mtok = EXCLUDED.input_price_per_mtok,
output_price_per_mtok = EXCLUDED.output_price_per_mtok,
is_free = EXCLUDED.is_free,
@@ -101,6 +109,7 @@ func upsertOfficialPricingRecords(db *sql.DB, records []officialPricingRecord, b
rate_limit = EXCLUDED.rate_limit,
updated_at = CURRENT_TIMESTAMP`,
modelID, operatorID, record.Region, record.Currency,
fallbackPricingMode(record.PricingMode), fallbackPriceUnit(record.PriceUnit), nullIfZeroFloat(record.FlatPrice),
record.InputPrice, record.OutputPrice, record.IsFree, record.SourceURL, sourceType,
nullIfBlank(freeQuota), freeLimitations, rateLimit,
)
@@ -260,6 +269,29 @@ func fallbackModality(raw string) string {
return value
}
func fallbackPricingMode(raw string) string {
value := strings.TrimSpace(raw)
if value == "" {
return "input_output"
}
return value
}
func fallbackPriceUnit(raw string) string {
value := strings.TrimSpace(raw)
if value == "" {
return "million_tokens"
}
return value
}
func nullIfZeroFloat(value float64) any {
if value == 0 {
return nil
}
return value
}
func fetchRawPricingPage(url string, fixture string, client *http.Client) (string, error) {
return fetchRawPricingPageWithOptions(url, fixture, client, officialPricingFetchOptions{
AcceptLanguage: "zh-CN,zh;q=0.9,en;q=0.8",
@@ -399,6 +431,8 @@ func detectModality(modelName string) string {
switch {
case strings.Contains(lower, "coder"), strings.Contains(lower, "code"):
return "code"
case strings.Contains(lower, "voice"), strings.Contains(lower, "audio"), strings.Contains(lower, "speech"):
return "audio"
case strings.Contains(lower, "vision"), strings.Contains(lower, "vl"), strings.Contains(lower, "omni"), strings.Contains(lower, "multi"), strings.Contains(lower, "live"):
return "multimodal"
default:
@@ -414,8 +448,14 @@ func providerMetadata(providerName string) (string, string, string) {
return "亚马逊", "US", "https://aws.amazon.com"
case "Anthropic":
return "Anthropic", "US", "https://www.anthropic.com"
case "BAAI":
return "智源", "CN", "https://www.baai.ac.cn"
case "Baidu":
return "百度", "CN", "https://cloud.baidu.com"
case "ByteDance":
return "字节跳动", "CN", "https://www.volcengine.com"
case "China Mobile":
return "中国移动", "CN", "https://ecloud.10086.cn"
case "Cloudflare":
return "Cloudflare", "US", "https://www.cloudflare.com"
case "Cohere":