fix(billing): extend quantity validation and saturating conversions to remaining paths
Bound max-tokens fields across all relay format validators, saturate tiered-expression rounding and audio/tool/task token conversions, and route legacy remix ratios through the guarded setter.
This commit is contained in:
+2
-2
@@ -54,7 +54,7 @@ func calculateAudioQuota(info QuotaInfo) int {
|
||||
groupRatio := decimal.NewFromFloat(info.GroupRatio)
|
||||
|
||||
quota := modelPrice.Mul(quotaPerUnit).Mul(groupRatio)
|
||||
return int(quota.IntPart())
|
||||
return decimalToQuota(quota)
|
||||
}
|
||||
|
||||
completionRatio := decimal.NewFromFloat(ratio_setting.GetCompletionRatio(info.ModelName))
|
||||
@@ -83,7 +83,7 @@ func calculateAudioQuota(info QuotaInfo) int {
|
||||
quota = decimal.NewFromInt(1)
|
||||
}
|
||||
|
||||
return int(quota.Round(0).IntPart())
|
||||
return decimalToQuota(quota)
|
||||
}
|
||||
|
||||
func PreWssConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.RealtimeUsage) error {
|
||||
|
||||
@@ -145,15 +145,13 @@ func composeTieredTextQuota(relayInfo *relaycommon.RelayInfo, summary textQuotaS
|
||||
|
||||
if tieredResult != nil {
|
||||
if snap := relayInfo.TieredBillingSnapshot; snap != nil {
|
||||
return int(decimal.NewFromFloat(tieredResult.ActualQuotaBeforeGroup).
|
||||
return decimalToQuota(decimal.NewFromFloat(tieredResult.ActualQuotaBeforeGroup).
|
||||
Mul(decimal.NewFromFloat(snap.GroupRatio)).
|
||||
Add(summary.ToolCallSurchargeQuota).
|
||||
Round(0).
|
||||
IntPart())
|
||||
Add(summary.ToolCallSurchargeQuota))
|
||||
}
|
||||
}
|
||||
|
||||
return tieredQuota + int(summary.ToolCallSurchargeQuota.Round(0).IntPart())
|
||||
return tieredQuota + decimalToQuota(summary.ToolCallSurchargeQuota)
|
||||
}
|
||||
|
||||
func calculateTextQuotaSummary(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.Usage) textQuotaSummary {
|
||||
|
||||
@@ -208,8 +208,14 @@ func EstimateRequestToken(c *gin.Context, meta *types.TokenCountMeta, info *rela
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error getting audio duration: %v", err)
|
||||
}
|
||||
// 一分钟 1000 token,与 $price / minute 对齐
|
||||
totalAudioToken += int(math.Round(math.Ceil(duration) / 60.0 * 1000))
|
||||
// 一分钟 1000 token,与 $price / minute 对齐。
|
||||
// duration 来自用户上传文件的元数据,可被伪造成天文数字,
|
||||
// 必须饱和转换防止 int 回绕成负数 token。
|
||||
audioTokens := common.QuotaFromFloat(math.Round(math.Ceil(duration) / 60.0 * 1000))
|
||||
if audioTokens < 0 {
|
||||
audioTokens = 0
|
||||
}
|
||||
totalAudioToken += audioTokens
|
||||
}
|
||||
return totalAudioToken, nil
|
||||
}
|
||||
@@ -377,7 +383,8 @@ func CountAudioTokenInput(audioBase64 string, audioFormat string) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(duration / 60 * 100 / 0.06), nil
|
||||
// duration 来自用户提供的音频元数据,饱和转换防止 int 回绕
|
||||
return common.QuotaFromFloat(duration / 60 * 100 / 0.06), nil
|
||||
}
|
||||
|
||||
func CountAudioTokenOutput(audioBase64 string, audioFormat string) (int, error) {
|
||||
@@ -388,7 +395,8 @@ func CountAudioTokenOutput(audioBase64 string, audioFormat string) (int, error)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(duration / 60 * 200 / 0.24), nil
|
||||
// duration 来自上游返回的音频元数据,饱和转换防止 int 回绕
|
||||
return common.QuotaFromFloat(duration / 60 * 200 / 0.24), nil
|
||||
}
|
||||
|
||||
// CountTextToken 统计文本的token数量,仅OpenAI模型使用tokenizer,其余模型使用估算
|
||||
|
||||
@@ -49,7 +49,7 @@ func ComputeToolCallQuota(usage ToolCallUsage, groupRatio float64) ToolCallResul
|
||||
return
|
||||
}
|
||||
totalPrice := pricePer1K * float64(count) / 1000
|
||||
quota := int(math.Round(totalPrice * common.QuotaPerUnit * groupRatio))
|
||||
quota := common.QuotaFromFloat(math.Round(totalPrice * common.QuotaPerUnit * groupRatio))
|
||||
items = append(items, ToolCallItem{
|
||||
Name: toolName,
|
||||
CallCount: count,
|
||||
@@ -70,7 +70,7 @@ func ComputeToolCallQuota(usage ToolCallUsage, groupRatio float64) ToolCallResul
|
||||
|
||||
if usage.ImageGenerationCall {
|
||||
price := operation_setting.GetGPTImage1PriceOnceCall(usage.ImageGenerationQuality, usage.ImageGenerationSize)
|
||||
quota := int(math.Round(price * common.QuotaPerUnit * groupRatio))
|
||||
quota := common.QuotaFromFloat(math.Round(price * common.QuotaPerUnit * groupRatio))
|
||||
items = append(items, ToolCallItem{
|
||||
Name: "image_generation",
|
||||
CallCount: 1,
|
||||
|
||||
Reference in New Issue
Block a user