mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-13 07:40:56 +00:00
refactor(price): improve handling of other ratios in PriceData
This commit is contained in:
+1
-1
@@ -588,7 +588,7 @@ func RelayTask(c *gin.Context) {
|
|||||||
ModelPrice: relayInfo.PriceData.ModelPrice,
|
ModelPrice: relayInfo.PriceData.ModelPrice,
|
||||||
GroupRatio: relayInfo.PriceData.GroupRatioInfo.GroupRatio,
|
GroupRatio: relayInfo.PriceData.GroupRatioInfo.GroupRatio,
|
||||||
ModelRatio: relayInfo.PriceData.ModelRatio,
|
ModelRatio: relayInfo.PriceData.ModelRatio,
|
||||||
OtherRatios: relayInfo.PriceData.OtherRatios,
|
OtherRatios: relayInfo.PriceData.OtherRatios(),
|
||||||
OriginModelName: relayInfo.OriginModelName,
|
OriginModelName: relayInfo.OriginModelName,
|
||||||
PerCallBilling: common.StringsContains(constant.TaskPricePatches, relayInfo.OriginModelName) || relayInfo.PriceData.UsePrice,
|
PerCallBilling: common.StringsContains(constant.TaskPricePatches, relayInfo.OriginModelName) || relayInfo.PriceData.UsePrice,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package relay
|
package relay
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||||
|
"github.com/QuantumNous/new-api/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsResponsesEventStreamContentType(t *testing.T) {
|
func TestIsResponsesEventStreamContentType(t *testing.T) {
|
||||||
@@ -24,3 +28,44 @@ func TestIsResponsesEventStreamContentType(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRecalcQuotaFromRatiosIgnoresInvalidMultipliers(t *testing.T) {
|
||||||
|
info := &relaycommon.RelayInfo{
|
||||||
|
PriceData: types.PriceData{
|
||||||
|
Quota: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
info.PriceData.AddOtherRatio("duration", 2)
|
||||||
|
|
||||||
|
quota, ok := recalcQuotaFromRatios(info, map[string]float64{
|
||||||
|
"duration": 3,
|
||||||
|
"zero": 0,
|
||||||
|
"negative": -1,
|
||||||
|
"nan": math.NaN(),
|
||||||
|
"inf": math.Inf(1),
|
||||||
|
})
|
||||||
|
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, 150, quota)
|
||||||
|
assert.True(t, info.PriceData.HasOtherRatio("duration"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecalcQuotaFromRatiosRejectsAllInvalidAdjustedRatios(t *testing.T) {
|
||||||
|
info := &relaycommon.RelayInfo{
|
||||||
|
PriceData: types.PriceData{
|
||||||
|
Quota: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
info.PriceData.AddOtherRatio("duration", 2)
|
||||||
|
|
||||||
|
quota, ok := recalcQuotaFromRatios(info, map[string]float64{
|
||||||
|
"zero": 0,
|
||||||
|
"negative": -1,
|
||||||
|
"nan": math.NaN(),
|
||||||
|
"inf": math.Inf(1),
|
||||||
|
})
|
||||||
|
|
||||||
|
require.False(t, ok)
|
||||||
|
assert.Equal(t, 0, quota)
|
||||||
|
assert.True(t, info.PriceData.HasOtherRatio("duration"))
|
||||||
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func ImageHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *type
|
|||||||
// Adaptors may have already set a more accurate count from the
|
// Adaptors may have already set a more accurate count from the
|
||||||
// upstream response; only set the default when they haven't.
|
// upstream response; only set the default when they haven't.
|
||||||
if info.PriceData.UsePrice { // only price model use N ratio
|
if info.PriceData.UsePrice { // only price model use N ratio
|
||||||
if _, hasN := info.PriceData.OtherRatios["n"]; !hasN {
|
if !info.PriceData.HasOtherRatio("n") {
|
||||||
info.PriceData.AddOtherRatio("n", float64(imageN))
|
info.PriceData.AddOtherRatio("n", float64(imageN))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-25
@@ -196,12 +196,7 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (*TaskSubmitRe
|
|||||||
|
|
||||||
// 6. 将 OtherRatios 应用到基础额度(饱和转换,防止溢出成负数)
|
// 6. 将 OtherRatios 应用到基础额度(饱和转换,防止溢出成负数)
|
||||||
if !common.StringsContains(constant.TaskPricePatches, modelName) {
|
if !common.StringsContains(constant.TaskPricePatches, modelName) {
|
||||||
quotaWithRatios := float64(info.PriceData.Quota)
|
quotaWithRatios := info.PriceData.ApplyOtherRatiosToFloat(float64(info.PriceData.Quota))
|
||||||
for _, ra := range info.PriceData.OtherRatios {
|
|
||||||
if ra != 1.0 {
|
|
||||||
quotaWithRatios *= ra
|
|
||||||
}
|
|
||||||
}
|
|
||||||
quota, clamp := common.QuotaFromFloatChecked(quotaWithRatios)
|
quota, clamp := common.QuotaFromFloatChecked(quotaWithRatios)
|
||||||
info.PriceData.Quota = quota
|
info.PriceData.Quota = quota
|
||||||
noteTaskQuotaClamp(info, clamp)
|
noteTaskQuotaClamp(info, clamp)
|
||||||
@@ -232,7 +227,7 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (*TaskSubmitRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 10. 返回 OtherRatios 给下游(header 必须在 DoResponse 写 body 之前设置)
|
// 10. 返回 OtherRatios 给下游(header 必须在 DoResponse 写 body 之前设置)
|
||||||
otherRatios := info.PriceData.OtherRatios
|
otherRatios := info.PriceData.OtherRatios()
|
||||||
if otherRatios == nil {
|
if otherRatios == nil {
|
||||||
otherRatios = map[string]float64{}
|
otherRatios = map[string]float64{}
|
||||||
}
|
}
|
||||||
@@ -248,10 +243,12 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (*TaskSubmitRe
|
|||||||
// 11. 提交后计费调整:让适配器根据上游实际返回调整 OtherRatios
|
// 11. 提交后计费调整:让适配器根据上游实际返回调整 OtherRatios
|
||||||
finalQuota := info.PriceData.Quota
|
finalQuota := info.PriceData.Quota
|
||||||
if adjustedRatios := adaptor.AdjustBillingOnSubmit(info, taskData); len(adjustedRatios) > 0 {
|
if adjustedRatios := adaptor.AdjustBillingOnSubmit(info, taskData); len(adjustedRatios) > 0 {
|
||||||
// 基于调整后的 ratios 重新计算 quota
|
if adjustedQuota, ok := recalcQuotaFromRatios(info, adjustedRatios); ok {
|
||||||
finalQuota = recalcQuotaFromRatios(info, adjustedRatios)
|
// 基于调整后的 ratios 重新计算 quota
|
||||||
info.PriceData.OtherRatios = adjustedRatios
|
finalQuota = adjustedQuota
|
||||||
info.PriceData.Quota = finalQuota
|
info.PriceData.ReplaceOtherRatios(adjustedRatios)
|
||||||
|
info.PriceData.Quota = finalQuota
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &TaskSubmitResult{
|
return &TaskSubmitResult{
|
||||||
@@ -264,25 +261,18 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (*TaskSubmitRe
|
|||||||
|
|
||||||
// recalcQuotaFromRatios 根据 adjustedRatios 重新计算 quota。
|
// recalcQuotaFromRatios 根据 adjustedRatios 重新计算 quota。
|
||||||
// 公式: baseQuota × ∏(ratio) — 其中 baseQuota 是不含 OtherRatios 的基础额度。
|
// 公式: baseQuota × ∏(ratio) — 其中 baseQuota 是不含 OtherRatios 的基础额度。
|
||||||
func recalcQuotaFromRatios(info *relaycommon.RelayInfo, ratios map[string]float64) int {
|
func recalcQuotaFromRatios(info *relaycommon.RelayInfo, ratios map[string]float64) (int, bool) {
|
||||||
// 从 PriceData 获取不含 OtherRatios 的基础价格
|
// 从 PriceData 获取不含 OtherRatios 的基础价格
|
||||||
baseQuota := float64(info.PriceData.Quota)
|
baseQuota := info.PriceData.RemoveOtherRatiosFromFloat(float64(info.PriceData.Quota))
|
||||||
// 先除掉原有的 OtherRatios 恢复基础额度
|
priceData := info.PriceData
|
||||||
for _, ra := range info.PriceData.OtherRatios {
|
if !priceData.ReplaceOtherRatios(ratios) {
|
||||||
if ra != 1.0 && ra > 0 {
|
return 0, false
|
||||||
baseQuota /= ra
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 应用新的 ratios
|
// 应用新的 ratios
|
||||||
result := baseQuota
|
result := priceData.ApplyOtherRatiosToFloat(baseQuota)
|
||||||
for _, ra := range ratios {
|
|
||||||
if ra != 1.0 {
|
|
||||||
result *= ra
|
|
||||||
}
|
|
||||||
}
|
|
||||||
quota, clamp := common.QuotaFromFloatChecked(result)
|
quota, clamp := common.QuotaFromFloatChecked(result)
|
||||||
noteTaskQuotaClamp(info, clamp)
|
noteTaskQuotaClamp(info, clamp)
|
||||||
return quota
|
return quota, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// noteTaskQuotaClamp records the first quota saturation event onto the task's
|
// noteTaskQuotaClamp records the first quota saturation event onto the task's
|
||||||
|
|||||||
+18
-10
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/QuantumNous/new-api/model"
|
"github.com/QuantumNous/new-api/model"
|
||||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||||
|
"github.com/QuantumNous/new-api/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,9 +24,9 @@ func LogTaskConsumption(c *gin.Context, info *relaycommon.RelayInfo) {
|
|||||||
if common.StringsContains(constant.TaskPricePatches, info.OriginModelName) {
|
if common.StringsContains(constant.TaskPricePatches, info.OriginModelName) {
|
||||||
logContent = fmt.Sprintf("%s,按次计费", logContent)
|
logContent = fmt.Sprintf("%s,按次计费", logContent)
|
||||||
} else {
|
} else {
|
||||||
if len(info.PriceData.OtherRatios) > 0 {
|
if otherRatios := info.PriceData.OtherRatios(); len(otherRatios) > 0 {
|
||||||
var contents []string
|
var contents []string
|
||||||
for key, ra := range info.PriceData.OtherRatios {
|
for key, ra := range otherRatios {
|
||||||
if 1.0 != ra {
|
if 1.0 != ra {
|
||||||
contents = append(contents, fmt.Sprintf("%s: %.2f", key, ra))
|
contents = append(contents, fmt.Sprintf("%s: %.2f", key, ra))
|
||||||
}
|
}
|
||||||
@@ -126,8 +127,8 @@ func taskBillingOther(task *model.Task) map[string]interface{} {
|
|||||||
other["model_ratio"] = bc.ModelRatio
|
other["model_ratio"] = bc.ModelRatio
|
||||||
}
|
}
|
||||||
other["group_ratio"] = bc.GroupRatio
|
other["group_ratio"] = bc.GroupRatio
|
||||||
if len(bc.OtherRatios) > 0 {
|
if priceData := taskBillingContextPriceData(bc); priceData != nil {
|
||||||
for k, v := range bc.OtherRatios {
|
for k, v := range priceData.OtherRatios() {
|
||||||
other[k] = v
|
other[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,6 +141,17 @@ func taskBillingOther(task *model.Task) map[string]interface{} {
|
|||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func taskBillingContextPriceData(bc *model.TaskBillingContext) *types.PriceData {
|
||||||
|
if bc == nil || len(bc.OtherRatios) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
priceData := &types.PriceData{}
|
||||||
|
if !priceData.ReplaceOtherRatios(bc.OtherRatios) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return priceData
|
||||||
|
}
|
||||||
|
|
||||||
// taskModelName 从 BillingContext 或 Properties 中获取模型名称。
|
// taskModelName 从 BillingContext 或 Properties 中获取模型名称。
|
||||||
func taskModelName(task *model.Task) string {
|
func taskModelName(task *model.Task) string {
|
||||||
if bc := task.PrivateData.BillingContext; bc != nil && bc.OriginModelName != "" {
|
if bc := task.PrivateData.BillingContext; bc != nil && bc.OriginModelName != "" {
|
||||||
@@ -294,12 +306,8 @@ func RecalculateTaskQuotaByTokens(ctx context.Context, task *model.Task, totalTo
|
|||||||
|
|
||||||
// 计算 OtherRatios 乘积(视频折扣、时长等)
|
// 计算 OtherRatios 乘积(视频折扣、时长等)
|
||||||
otherMultiplier := 1.0
|
otherMultiplier := 1.0
|
||||||
if bc := task.PrivateData.BillingContext; bc != nil {
|
if priceData := taskBillingContextPriceData(task.PrivateData.BillingContext); priceData != nil {
|
||||||
for _, r := range bc.OtherRatios {
|
otherMultiplier = priceData.OtherRatioMultiplier()
|
||||||
if r != 1.0 && r > 0 {
|
|
||||||
otherMultiplier *= r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算实际应扣费额度: totalTokens * modelRatio * groupRatio * otherMultiplier(饱和转换,防止溢出成负数)
|
// 计算实际应扣费额度: totalTokens * modelRatio * groupRatio * otherMultiplier(饱和转换,防止溢出成负数)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -11,7 +12,9 @@ import (
|
|||||||
"github.com/QuantumNous/new-api/common"
|
"github.com/QuantumNous/new-api/common"
|
||||||
"github.com/QuantumNous/new-api/model"
|
"github.com/QuantumNous/new-api/model"
|
||||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||||
|
"github.com/QuantumNous/new-api/types"
|
||||||
"github.com/glebarez/sqlite"
|
"github.com/glebarez/sqlite"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -139,6 +142,102 @@ func makeTask(userId, channelId, quota, tokenId int, billingSource string, subsc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPriceDataOtherRatiosFilterAndSnapshot(t *testing.T) {
|
||||||
|
priceData := types.PriceData{}
|
||||||
|
|
||||||
|
priceData.AddOtherRatio("zero", 0)
|
||||||
|
priceData.AddOtherRatio("negative", -0.5)
|
||||||
|
priceData.AddOtherRatio("nan", math.NaN())
|
||||||
|
priceData.AddOtherRatio("inf", math.Inf(1))
|
||||||
|
priceData.AddOtherRatio("one", 1)
|
||||||
|
priceData.AddOtherRatio("positive", 2.5)
|
||||||
|
|
||||||
|
ratios := priceData.OtherRatios()
|
||||||
|
require.Len(t, ratios, 2)
|
||||||
|
assert.Equal(t, 1.0, ratios["one"])
|
||||||
|
assert.Equal(t, 2.5, ratios["positive"])
|
||||||
|
assert.True(t, priceData.HasOtherRatio("one"))
|
||||||
|
assert.False(t, priceData.HasOtherRatio("zero"))
|
||||||
|
|
||||||
|
ratios["positive"] = 99
|
||||||
|
ratios["new"] = 3
|
||||||
|
nextSnapshot := priceData.OtherRatios()
|
||||||
|
assert.Equal(t, 2.5, nextSnapshot["positive"])
|
||||||
|
assert.NotContains(t, nextSnapshot, "new")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPriceDataReplaceAndApplyOtherRatios(t *testing.T) {
|
||||||
|
priceData := types.PriceData{}
|
||||||
|
|
||||||
|
replaced := priceData.ReplaceOtherRatios(map[string]float64{
|
||||||
|
"zero": 0,
|
||||||
|
"negative": -3,
|
||||||
|
"nan": math.NaN(),
|
||||||
|
"inf": math.Inf(1),
|
||||||
|
"one": 1,
|
||||||
|
"duration": 2,
|
||||||
|
"size": 1.5,
|
||||||
|
})
|
||||||
|
|
||||||
|
require.True(t, replaced)
|
||||||
|
assert.Equal(t, 3.0, priceData.OtherRatioMultiplier())
|
||||||
|
assert.Equal(t, 30.0, priceData.ApplyOtherRatiosToFloat(10))
|
||||||
|
assert.Equal(t, 10.0, priceData.RemoveOtherRatiosFromFloat(30))
|
||||||
|
assert.True(t, decimal.NewFromInt(30).Equal(priceData.ApplyOtherRatiosToDecimal(decimal.NewFromInt(10))))
|
||||||
|
|
||||||
|
replaced = priceData.ReplaceOtherRatios(map[string]float64{
|
||||||
|
"zero": 0,
|
||||||
|
"nan": math.NaN(),
|
||||||
|
})
|
||||||
|
|
||||||
|
require.False(t, replaced)
|
||||||
|
assert.Nil(t, priceData.OtherRatios())
|
||||||
|
assert.Equal(t, 1.0, priceData.OtherRatioMultiplier())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskBillingOtherFiltersHistoricalOtherRatios(t *testing.T) {
|
||||||
|
task := makeTask(1, 1, 100, 0, BillingSourceWallet, 0)
|
||||||
|
task.PrivateData.BillingContext.OtherRatios = map[string]float64{
|
||||||
|
"seconds": 2,
|
||||||
|
"identity": 1,
|
||||||
|
"zero": 0,
|
||||||
|
"negative": -1,
|
||||||
|
"nan": math.NaN(),
|
||||||
|
"inf": math.Inf(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
other := taskBillingOther(task)
|
||||||
|
|
||||||
|
assert.Equal(t, 2.0, other["seconds"])
|
||||||
|
assert.Equal(t, 1.0, other["identity"])
|
||||||
|
assert.NotContains(t, other, "zero")
|
||||||
|
assert.NotContains(t, other, "negative")
|
||||||
|
assert.NotContains(t, other, "nan")
|
||||||
|
assert.NotContains(t, other, "inf")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskBillingContextPriceDataFiltersMultiplier(t *testing.T) {
|
||||||
|
priceData := taskBillingContextPriceData(&model.TaskBillingContext{
|
||||||
|
OtherRatios: map[string]float64{
|
||||||
|
"seconds": 2,
|
||||||
|
"size": 3,
|
||||||
|
"identity": 1,
|
||||||
|
"zero": 0,
|
||||||
|
"negative": -1,
|
||||||
|
"nan": math.NaN(),
|
||||||
|
"inf": math.Inf(1),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NotNil(t, priceData)
|
||||||
|
assert.Equal(t, 6.0, priceData.OtherRatioMultiplier())
|
||||||
|
assert.Equal(t, map[string]float64{
|
||||||
|
"seconds": 2,
|
||||||
|
"size": 3,
|
||||||
|
"identity": 1,
|
||||||
|
}, priceData.OtherRatios())
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Read-back helpers
|
// Read-back helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+2
-11
@@ -296,12 +296,7 @@ func calculateTextQuotaSummary(ctx *gin.Context, relayInfo *relaycommon.RelayInf
|
|||||||
quotaCalculateDecimal := promptQuota.Add(completionQuota).Mul(ratio)
|
quotaCalculateDecimal := promptQuota.Add(completionQuota).Mul(ratio)
|
||||||
quotaCalculateDecimal = quotaCalculateDecimal.Add(summary.ToolCallSurchargeQuota)
|
quotaCalculateDecimal = quotaCalculateDecimal.Add(summary.ToolCallSurchargeQuota)
|
||||||
quotaCalculateDecimal = quotaCalculateDecimal.Add(audioInputQuota)
|
quotaCalculateDecimal = quotaCalculateDecimal.Add(audioInputQuota)
|
||||||
|
quotaCalculateDecimal = relayInfo.PriceData.ApplyOtherRatiosToDecimal(quotaCalculateDecimal)
|
||||||
if len(relayInfo.PriceData.OtherRatios) > 0 {
|
|
||||||
for _, otherRatio := range relayInfo.PriceData.OtherRatios {
|
|
||||||
quotaCalculateDecimal = quotaCalculateDecimal.Mul(decimal.NewFromFloat(otherRatio))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ratio.IsZero() && quotaCalculateDecimal.LessThanOrEqual(decimal.Zero) {
|
if !ratio.IsZero() && quotaCalculateDecimal.LessThanOrEqual(decimal.Zero) {
|
||||||
quotaCalculateDecimal = decimal.NewFromInt(1)
|
quotaCalculateDecimal = decimal.NewFromInt(1)
|
||||||
@@ -313,11 +308,7 @@ func calculateTextQuotaSummary(ctx *gin.Context, relayInfo *relaycommon.RelayInf
|
|||||||
quotaCalculateDecimal := dModelPrice.Mul(dQuotaPerUnit).Mul(dGroupRatio)
|
quotaCalculateDecimal := dModelPrice.Mul(dQuotaPerUnit).Mul(dGroupRatio)
|
||||||
quotaCalculateDecimal = quotaCalculateDecimal.Add(summary.ToolCallSurchargeQuota)
|
quotaCalculateDecimal = quotaCalculateDecimal.Add(summary.ToolCallSurchargeQuota)
|
||||||
quotaCalculateDecimal = quotaCalculateDecimal.Add(audioInputQuota)
|
quotaCalculateDecimal = quotaCalculateDecimal.Add(audioInputQuota)
|
||||||
if len(relayInfo.PriceData.OtherRatios) > 0 {
|
quotaCalculateDecimal = relayInfo.PriceData.ApplyOtherRatiosToDecimal(quotaCalculateDecimal)
|
||||||
for _, otherRatio := range relayInfo.PriceData.OtherRatios {
|
|
||||||
quotaCalculateDecimal = quotaCalculateDecimal.Mul(decimal.NewFromFloat(otherRatio))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
quota, clamp := common.QuotaFromDecimalChecked(quotaCalculateDecimal)
|
quota, clamp := common.QuotaFromDecimalChecked(quotaCalculateDecimal)
|
||||||
summary.Quota = quota
|
summary.Quota = quota
|
||||||
noteQuotaClamp(relayInfo, clamp)
|
noteQuotaClamp(relayInfo, clamp)
|
||||||
|
|||||||
+75
-8
@@ -3,6 +3,8 @@ package types
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GroupRatioInfo struct {
|
type GroupRatioInfo struct {
|
||||||
@@ -23,7 +25,7 @@ type PriceData struct {
|
|||||||
ImageRatio float64
|
ImageRatio float64
|
||||||
AudioRatio float64
|
AudioRatio float64
|
||||||
AudioCompletionRatio float64
|
AudioCompletionRatio float64
|
||||||
OtherRatios map[string]float64
|
otherRatios map[string]float64
|
||||||
UsePrice bool
|
UsePrice bool
|
||||||
Quota int // 按次计费的最终额度(MJ / Task)
|
Quota int // 按次计费的最终额度(MJ / Task)
|
||||||
QuotaToPreConsume int // 按量计费的预消耗额度
|
QuotaToPreConsume int // 按量计费的预消耗额度
|
||||||
@@ -31,15 +33,80 @@ type PriceData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PriceData) AddOtherRatio(key string, ratio float64) {
|
func (p *PriceData) AddOtherRatio(key string, ratio float64) {
|
||||||
if p.OtherRatios == nil {
|
if !isValidOtherRatio(ratio) {
|
||||||
p.OtherRatios = make(map[string]float64)
|
|
||||||
}
|
|
||||||
// NaN/Inf would poison every downstream quota multiplication
|
|
||||||
// (int(NaN * quota) wraps to a negative charge).
|
|
||||||
if !(ratio > 0) || math.IsInf(ratio, 1) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.OtherRatios[key] = ratio
|
if p.otherRatios == nil {
|
||||||
|
p.otherRatios = make(map[string]float64)
|
||||||
|
}
|
||||||
|
p.otherRatios[key] = ratio
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PriceData) ReplaceOtherRatios(ratios map[string]float64) bool {
|
||||||
|
p.otherRatios = nil
|
||||||
|
for key, ratio := range ratios {
|
||||||
|
p.AddOtherRatio(key, ratio)
|
||||||
|
}
|
||||||
|
return len(p.otherRatios) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PriceData) HasOtherRatio(key string) bool {
|
||||||
|
ratio, ok := p.otherRatios[key]
|
||||||
|
return ok && isValidOtherRatio(ratio)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PriceData) OtherRatios() map[string]float64 {
|
||||||
|
if len(p.otherRatios) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ratios := make(map[string]float64, len(p.otherRatios))
|
||||||
|
for key, ratio := range p.otherRatios {
|
||||||
|
if isValidOtherRatio(ratio) {
|
||||||
|
ratios[key] = ratio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ratios) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ratios
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PriceData) OtherRatioMultiplier() float64 {
|
||||||
|
multiplier := 1.0
|
||||||
|
for _, ratio := range p.otherRatios {
|
||||||
|
if isValidOtherRatio(ratio) && ratio != 1.0 {
|
||||||
|
multiplier *= ratio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return multiplier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PriceData) ApplyOtherRatiosToFloat(value float64) float64 {
|
||||||
|
return value * p.OtherRatioMultiplier()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PriceData) ApplyOtherRatiosToDecimal(value decimal.Decimal) decimal.Decimal {
|
||||||
|
for _, ratio := range p.otherRatios {
|
||||||
|
if isValidOtherRatio(ratio) && ratio != 1.0 {
|
||||||
|
value = value.Mul(decimal.NewFromFloat(ratio))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PriceData) RemoveOtherRatiosFromFloat(value float64) float64 {
|
||||||
|
for _, ratio := range p.otherRatios {
|
||||||
|
if isValidOtherRatio(ratio) && ratio != 1.0 {
|
||||||
|
value /= ratio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidOtherRatio(ratio float64) bool {
|
||||||
|
// NaN/Inf would poison every downstream quota multiplication
|
||||||
|
// (int(NaN * quota) wraps to a negative charge).
|
||||||
|
return ratio > 0 && !math.IsInf(ratio, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PriceData) ToSetting() string {
|
func (p *PriceData) ToSetting() string {
|
||||||
|
|||||||
Reference in New Issue
Block a user