fix(billing): improve quota handling and error reporting for pre-consume operations

This commit is contained in:
CaIon
2026-07-11 13:14:22 +08:00
parent 621927f710
commit d9595831bf
12 changed files with 246 additions and 61 deletions
+26 -24
View File
@@ -32,10 +32,6 @@ func modelPriceNotConfiguredError(modelName string, userId int) error {
)
}
func preConsumeQuotaRangeError(modelName string, clamp *common.QuotaClamp) error {
return fmt.Errorf("model %s pre-consume quota is out of range: operation=%s kind=%s value=%g", modelName, clamp.Op, clamp.Kind, clamp.Original)
}
// https://docs.claude.com/en/docs/build-with-claude/prompt-caching#1-hour-cache-duration
const claudeCacheCreation1hMultiplier = 6 / 3.75
@@ -121,20 +117,15 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
audioRatio = ratio_setting.GetAudioRatio(info.OriginModelName)
audioCompletionRatio = ratio_setting.GetAudioCompletionRatio(info.OriginModelName)
ratio := modelRatio * groupRatioInfo.GroupRatio
var clamp *common.QuotaClamp
preConsumedQuota, clamp = common.QuotaFromFloatChecked(float64(preConsumedTokens) * ratio)
if clamp != nil {
return types.PriceData{}, preConsumeQuotaRangeError(info.OriginModelName, clamp)
quota, err := common.QuotaFromFloatStrict(float64(preConsumedTokens) * ratio)
if err != nil {
return types.PriceData{}, err
}
preConsumedQuota = quota
} else {
if meta.ImagePriceRatio != 0 {
modelPrice = modelPrice * meta.ImagePriceRatio
}
var clamp *common.QuotaClamp
preConsumedQuota, clamp = common.QuotaFromFloatChecked(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
if clamp != nil {
return types.PriceData{}, preConsumeQuotaRangeError(info.OriginModelName, clamp)
}
}
// check if free model pre-consume is disabled
@@ -172,6 +163,17 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
CacheCreation1hRatio: cacheCreationRatio1h,
QuotaToPreConsume: preConsumedQuota,
}
if usePrice {
for name, ratio := range meta.BillingRatios {
priceData.AddOtherRatio(name, ratio)
}
quotaToPreConsume := priceData.ApplyOtherRatiosToFloat(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
quota, err := common.QuotaFromFloatStrict(quotaToPreConsume)
if err != nil {
return types.PriceData{}, err
}
priceData.QuotaToPreConsume = quota
}
if common.DebugEnabled {
logger.LogDebug(c, "model_price_helper result: %s", priceData.ToSetting())
@@ -211,10 +213,10 @@ func ModelPriceHelperPerCall(c *gin.Context, info *relaycommon.RelayInfo) (types
freeModel := false
if usePrice {
var clamp *common.QuotaClamp
quota, clamp = common.QuotaFromFloatChecked(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
if clamp != nil {
return types.PriceData{}, preConsumeQuotaRangeError(info.OriginModelName, clamp)
var err error
quota, err = common.QuotaFromFloatStrict(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
if err != nil {
return types.PriceData{}, err
}
if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume {
if groupRatioInfo.GroupRatio == 0 || modelPrice == 0 {
@@ -224,10 +226,10 @@ func ModelPriceHelperPerCall(c *gin.Context, info *relaycommon.RelayInfo) (types
}
} else {
// 按量计费:以模型倍率的一半作为预扣额度
var clamp *common.QuotaClamp
quota, clamp = common.QuotaFromFloatChecked(modelRatio / 2 * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
if clamp != nil {
return types.PriceData{}, preConsumeQuotaRangeError(info.OriginModelName, clamp)
var err error
quota, err = common.QuotaFromFloatStrict(modelRatio / 2 * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
if err != nil {
return types.PriceData{}, err
}
modelPrice = -1
if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume {
@@ -290,9 +292,9 @@ func modelPriceHelperTiered(c *gin.Context, info *relaycommon.RelayInfo, promptT
// Expression coefficients are $/1M tokens prices; convert to quota the same way per-call billing does.
quotaBeforeGroup := rawCost / 1_000_000 * common.QuotaPerUnit
preConsumedQuota, clamp := billingexpr.QuotaRoundChecked(quotaBeforeGroup * groupRatioInfo.GroupRatio)
if clamp != nil {
return types.PriceData{}, preConsumeQuotaRangeError(info.OriginModelName, clamp)
preConsumedQuota, err := billingexpr.QuotaRoundStrict(quotaBeforeGroup * groupRatioInfo.GroupRatio)
if err != nil {
return types.PriceData{}, err
}
freeModel := false