mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-14 00:01:53 +00:00
fix(billing): surface quota saturation events for admin auditing
Thread int32 saturation clamps from tiered settlement and video task recompute into the consume/task logs under admin_info, so oversized or malformed billing inputs stay auditable. Clamp negative audio duration before token conversion and gate the saturation UI markers on admin.
This commit is contained in:
+26
-12
@@ -138,6 +138,18 @@ func calculateTextToolCallSurcharge(ctx *gin.Context, relayInfo *relaycommon.Rel
|
||||
return surcharge
|
||||
}
|
||||
|
||||
// noteQuotaClamp records the first quota saturation event onto relayInfo so it
|
||||
// can later be attached to the consume/task log for admin auditing. First
|
||||
// non-nil clamp wins (a single request may hit multiple conversions).
|
||||
func noteQuotaClamp(relayInfo *relaycommon.RelayInfo, clamp *common.QuotaClamp) {
|
||||
if clamp == nil || relayInfo == nil {
|
||||
return
|
||||
}
|
||||
if relayInfo.QuotaClamp == nil {
|
||||
relayInfo.QuotaClamp = clamp
|
||||
}
|
||||
}
|
||||
|
||||
func composeTieredTextQuota(relayInfo *relaycommon.RelayInfo, summary textQuotaSummary, tieredQuota int, tieredResult *billingexpr.TieredResult) int {
|
||||
if summary.ToolCallSurchargeQuota.IsZero() {
|
||||
return tieredQuota
|
||||
@@ -145,13 +157,17 @@ func composeTieredTextQuota(relayInfo *relaycommon.RelayInfo, summary textQuotaS
|
||||
|
||||
if tieredResult != nil {
|
||||
if snap := relayInfo.TieredBillingSnapshot; snap != nil {
|
||||
return decimalToQuota(decimal.NewFromFloat(tieredResult.ActualQuotaBeforeGroup).
|
||||
quota, clamp := common.QuotaFromDecimalChecked(decimal.NewFromFloat(tieredResult.ActualQuotaBeforeGroup).
|
||||
Mul(decimal.NewFromFloat(snap.GroupRatio)).
|
||||
Add(summary.ToolCallSurchargeQuota))
|
||||
noteQuotaClamp(relayInfo, clamp)
|
||||
return quota
|
||||
}
|
||||
}
|
||||
|
||||
return tieredQuota + decimalToQuota(summary.ToolCallSurchargeQuota)
|
||||
surcharge, clamp := common.QuotaFromDecimalChecked(summary.ToolCallSurchargeQuota)
|
||||
noteQuotaClamp(relayInfo, clamp)
|
||||
return tieredQuota + surcharge
|
||||
}
|
||||
|
||||
func calculateTextQuotaSummary(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.Usage) textQuotaSummary {
|
||||
@@ -285,7 +301,9 @@ func calculateTextQuotaSummary(ctx *gin.Context, relayInfo *relaycommon.RelayInf
|
||||
if !ratio.IsZero() && quotaCalculateDecimal.LessThanOrEqual(decimal.Zero) {
|
||||
quotaCalculateDecimal = decimal.NewFromInt(1)
|
||||
}
|
||||
summary.Quota = decimalToQuota(quotaCalculateDecimal)
|
||||
quota, clamp := common.QuotaFromDecimalChecked(quotaCalculateDecimal)
|
||||
summary.Quota = quota
|
||||
noteQuotaClamp(relayInfo, clamp)
|
||||
} else {
|
||||
quotaCalculateDecimal := dModelPrice.Mul(dQuotaPerUnit).Mul(dGroupRatio)
|
||||
quotaCalculateDecimal = quotaCalculateDecimal.Add(summary.ToolCallSurchargeQuota)
|
||||
@@ -295,7 +313,9 @@ func calculateTextQuotaSummary(ctx *gin.Context, relayInfo *relaycommon.RelayInf
|
||||
quotaCalculateDecimal = quotaCalculateDecimal.Mul(decimal.NewFromFloat(otherRatio))
|
||||
}
|
||||
}
|
||||
summary.Quota = decimalToQuota(quotaCalculateDecimal)
|
||||
quota, clamp := common.QuotaFromDecimalChecked(quotaCalculateDecimal)
|
||||
summary.Quota = quota
|
||||
noteQuotaClamp(relayInfo, clamp)
|
||||
}
|
||||
|
||||
if summary.TotalTokens == 0 {
|
||||
@@ -307,14 +327,6 @@ func calculateTextQuotaSummary(ctx *gin.Context, relayInfo *relaycommon.RelayInf
|
||||
return summary
|
||||
}
|
||||
|
||||
// decimalToQuota converts a computed quota decimal to int with saturation
|
||||
// (see common.QuotaFromFloat). Oversized multipliers (e.g. an absurd image
|
||||
// generation count) must never wrap around and turn a charge into a credit.
|
||||
func decimalToQuota(d decimal.Decimal) int {
|
||||
f, _ := d.Round(0).Float64()
|
||||
return common.QuotaFromFloat(f)
|
||||
}
|
||||
|
||||
func usageSemanticFromUsage(relayInfo *relaycommon.RelayInfo, usage *dto.Usage) string {
|
||||
if usage != nil && usage.UsageSemantic != "" {
|
||||
return usage.UsageSemantic
|
||||
@@ -465,6 +477,8 @@ func PostTextConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, us
|
||||
InjectTieredBillingInfo(other, relayInfo, tieredResult)
|
||||
}
|
||||
|
||||
attachQuotaSaturation(ctx, relayInfo, other)
|
||||
|
||||
model.RecordConsumeLog(ctx, relayInfo.UserId, model.RecordConsumeLogParams{
|
||||
ChannelId: relayInfo.ChannelId,
|
||||
PromptTokens: summary.PromptTokens,
|
||||
|
||||
Reference in New Issue
Block a user