From c41ca95a43679ec7853e43556b8283a15676602b Mon Sep 17 00:00:00 2001 From: huangzhenpc Date: Tue, 24 Mar 2026 17:01:12 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=AE=A1=E7=90=86=E5=91=98=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E4=BB=B7=E6=A0=BC=E7=9A=84=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E8=87=AA=E5=8A=A8=E6=8C=89=E6=AC=A1=E8=AE=A1?= =?UTF-8?q?=E8=B4=B9=EF=BC=8C=E4=B8=8D=E5=86=8D=E4=B9=98=20OtherRatios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当管理员为任务模型(如 sora_video2)设置了固定价格时, 之前会错误地将价格乘以 seconds/size 等 OtherRatios, 导致设置 $2/次 实际收费 $8($2 × 4秒)。 新增 IsTaskPerCallBilling() 判断逻辑: - 模型在 TASK_PRICE_PATCH 环境变量中 → 按次(兼容旧逻辑) - 模型有管理员配置的价格且不在默认价格表中 → 自动按次 默认价格表中的模型(如 sora-2: $0.3)仍按原逻辑乘 OtherRatios。 Co-Authored-By: Claude Opus 4.6 (1M context) --- controller/relay.go | 3 ++- relay/relay_task.go | 4 +++- service/task_billing.go | 4 +--- setting/ratio_setting/model_ratio.go | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/controller/relay.go b/controller/relay.go index 10dfd502..cba7a594 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -22,6 +22,7 @@ import ( "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/operation_setting" + "github.com/QuantumNous/new-api/setting/ratio_setting" "github.com/QuantumNous/new-api/types" "github.com/bytedance/gopkg/util/gopool" @@ -581,7 +582,7 @@ func RelayTask(c *gin.Context) { ModelRatio: relayInfo.PriceData.ModelRatio, OtherRatios: relayInfo.PriceData.OtherRatios, OriginModelName: relayInfo.OriginModelName, - PerCallBilling: common.StringsContains(constant.TaskPricePatches, relayInfo.OriginModelName), + PerCallBilling: ratio_setting.IsTaskPerCallBilling(relayInfo.OriginModelName), } task.Quota = result.Quota task.Data = result.TaskData diff --git a/relay/relay_task.go b/relay/relay_task.go index 098e2382..0f523616 100644 --- a/relay/relay_task.go +++ b/relay/relay_task.go @@ -19,6 +19,7 @@ import ( relayconstant "github.com/QuantumNous/new-api/relay/constant" "github.com/QuantumNous/new-api/relay/helper" "github.com/QuantumNous/new-api/service" + "github.com/QuantumNous/new-api/setting/ratio_setting" "github.com/gin-gonic/gin" ) @@ -194,7 +195,8 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (*TaskSubmitRe } // 6. 将 OtherRatios 应用到基础额度 - if !common.StringsContains(constant.TaskPricePatches, modelName) { + // 按次计费模型(TaskPricePatches 或管理员自定义价格)跳过 OtherRatios 乘算 + if !ratio_setting.IsTaskPerCallBilling(modelName) { for _, ra := range info.PriceData.OtherRatios { if ra != 1.0 { info.PriceData.Quota = int(float64(info.PriceData.Quota) * ra) diff --git a/service/task_billing.go b/service/task_billing.go index b887f668..a73fc1d4 100644 --- a/service/task_billing.go +++ b/service/task_billing.go @@ -5,8 +5,6 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" @@ -20,7 +18,7 @@ func LogTaskConsumption(c *gin.Context, info *relaycommon.RelayInfo) { tokenName := c.GetString("token_name") logContent := fmt.Sprintf("操作 %s", info.Action) // 支持任务仅按次计费 - if common.StringsContains(constant.TaskPricePatches, info.OriginModelName) { + if ratio_setting.IsTaskPerCallBilling(info.OriginModelName) { logContent = fmt.Sprintf("%s,按次计费", logContent) } else { if len(info.PriceData.OtherRatios) > 0 { diff --git a/setting/ratio_setting/model_ratio.go b/setting/ratio_setting/model_ratio.go index 62fc8b3e..105288d0 100644 --- a/setting/ratio_setting/model_ratio.go +++ b/setting/ratio_setting/model_ratio.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/QuantumNous/new-api/types" ) @@ -732,3 +733,20 @@ func GetModelRatioOrPrice(model string) (float64, bool, bool) { // price or rati } return 37.5, false, false } + +// IsTaskPerCallBilling 判断任务模型是否按次计费(固定价格,不乘 OtherRatios)。 +// 满足以下任一条件即为按次计费: +// 1. 模型在 TaskPricePatches 列表中(环境变量配置) +// 2. 模型有管理员配置的固定价格(在价格表中但不在默认价格表中) +func IsTaskPerCallBilling(modelName string) bool { + if common.StringsContains(constant.TaskPricePatches, modelName) { + return true + } + // 检查模型是否有管理员配置的固定价格(非默认价格) + _, hasPrice := GetModelPrice(modelName, false) + if !hasPrice { + return false + } + _, isDefault := defaultModelPrice[modelName] + return !isDefault +}