fix(billing): reject saturated pre-consume quota
This commit is contained in:
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
@@ -17,6 +18,23 @@ const (
|
||||
// PreConsumeBilling 根据用户计费偏好创建 BillingSession 并执行预扣费。
|
||||
// 会话存储在 relayInfo.Billing 上,供后续 Settle / Refund 使用。
|
||||
func PreConsumeBilling(c *gin.Context, preConsumedQuota int, relayInfo *relaycommon.RelayInfo) *types.NewAPIError {
|
||||
if relayInfo != nil && relayInfo.QuotaClamp != nil {
|
||||
clamp := relayInfo.QuotaClamp
|
||||
return types.NewErrorWithStatusCode(
|
||||
fmt.Errorf("pre-consume quota is out of range: operation=%s kind=%s value=%g", clamp.Op, clamp.Kind, clamp.Original),
|
||||
types.ErrorCodeModelPriceError,
|
||||
http.StatusBadRequest,
|
||||
types.ErrOptionWithSkipRetry(),
|
||||
)
|
||||
}
|
||||
if preConsumedQuota < 0 {
|
||||
return types.NewErrorWithStatusCode(
|
||||
fmt.Errorf("pre-consume quota cannot be negative: %d", preConsumedQuota),
|
||||
types.ErrorCodeModelPriceError,
|
||||
http.StatusBadRequest,
|
||||
types.ErrOptionWithSkipRetry(),
|
||||
)
|
||||
}
|
||||
session, apiErr := NewBillingSession(c, relayInfo, preConsumedQuota)
|
||||
if apiErr != nil {
|
||||
return apiErr
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -72,3 +74,36 @@ func TestAttachQuotaSaturationNoClampNoMarker(t *testing.T) {
|
||||
_, hasAdmin := other["admin_info"]
|
||||
require.False(t, hasAdmin, "no admin_info should be added when there is no clamp")
|
||||
}
|
||||
|
||||
func TestPreConsumeBillingRejectsSaturatedQuotaBeforeDeduction(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
c, _ := gin.CreateTestContext(nil)
|
||||
info := &relaycommon.RelayInfo{
|
||||
QuotaClamp: &common.QuotaClamp{
|
||||
Op: "QuotaFromFloat",
|
||||
Kind: common.QuotaClampOverflow,
|
||||
Original: 1e30,
|
||||
Clamped: common.MaxQuota,
|
||||
},
|
||||
}
|
||||
|
||||
apiErr := PreConsumeBilling(c, common.MaxQuota, info)
|
||||
|
||||
require.NotNil(t, apiErr)
|
||||
require.Equal(t, types.ErrorCodeModelPriceError, apiErr.GetErrorCode())
|
||||
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode)
|
||||
require.Nil(t, info.Billing)
|
||||
}
|
||||
|
||||
func TestPreConsumeBillingRejectsNegativeQuotaBeforeDeduction(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
c, _ := gin.CreateTestContext(nil)
|
||||
info := &relaycommon.RelayInfo{}
|
||||
|
||||
apiErr := PreConsumeBilling(c, -1, info)
|
||||
|
||||
require.NotNil(t, apiErr)
|
||||
require.Equal(t, types.ErrorCodeModelPriceError, apiErr.GetErrorCode())
|
||||
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode)
|
||||
require.Nil(t, info.Billing)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user