From 9591795b10f3ddb840dfeaeed010e583fa02786c Mon Sep 17 00:00:00 2001 From: npc0-hue Date: Thu, 12 Mar 2026 11:11:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8D=83=E9=97=AE3.5-plus=E8=AE=A1?= =?UTF-8?q?=E8=B4=B9=E4=B8=8D=E6=AD=A3=E7=A1=AE=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gaia/model_provider_constants_extend.go | 31 ++++++++++ admin/server/service/gaia/model_provider.go | 57 ++++++++++++++----- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/admin/server/model/gaia/model_provider_constants_extend.go b/admin/server/model/gaia/model_provider_constants_extend.go index ab1d08cdf..7decc2d6d 100644 --- a/admin/server/model/gaia/model_provider_constants_extend.go +++ b/admin/server/model/gaia/model_provider_constants_extend.go @@ -49,3 +49,34 @@ var DefaultAPIBase = map[string]string{ // CredentialKeyFallback 未知提供商时依次尝试的配置 key var CredentialKeyFallback = []string{ConfigKeyOpenaiAPIKey, ConfigKeyAPIKey, ConfigKeyDashScopeAPIKey} + +// BuiltinModelPricing 内置兜底定价表(当 Dify Console API 未返回该模型定价时使用)。 +// 价格单位:每千 token(与 ModelPricing.Unit=0.001 对应),货币为各模型实际结算货币。 +// 通义/百炼模型官方定价(人民币,参考 https://help.aliyun.com/document_detail/2586379.html): +// - 输入/输出价格均为「每百万 token」,换算为每千 token 时除以 1000。 +var BuiltinModelPricing = map[string]ModelPricing{ + // ──── 通义千问 Qwen3 系列(RMB / 百万 token,128K 档) ──── + "qwen3-235b-a22b": {Input: 0.4 / 1000, Output: 1.6 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen3-30b-a3b": {Input: 0.11 / 1000, Output: 0.44 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen3-32b": {Input: 0.8 / 1000, Output: 3.2 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen3-14b": {Input: 0.3 / 1000, Output: 1.2 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen3-8b": {Input: 0.1 / 1000, Output: 0.4 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen3-4b": {Input: 0.04 / 1000, Output: 0.16 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen3-1.7b": {Input: 0.02 / 1000, Output: 0.08 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen3-0.6b": {Input: 0.01 / 1000, Output: 0.04 / 1000, Unit: 0.001, Currency: "RMB"}, + + // ──── 通义千问 Qwen3.5 系列(RMB / 百万 token,128K 档) ──── + "qwen3.5-plus": {Input: 0.8 / 1000, Output: 4.8 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen3.5-turbo": {Input: 0.3 / 1000, Output: 1.2 / 1000, Unit: 0.001, Currency: "RMB"}, + + // ──── 通义千问 Qwen2.5 系列(RMB / 百万 token) ──── + "qwen2.5-72b-instruct": {Input: 4.0 / 1000, Output: 12.0 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen2.5-32b-instruct": {Input: 3.5 / 1000, Output: 7.0 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen2.5-14b-instruct": {Input: 2.0 / 1000, Output: 6.0 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen2.5-7b-instruct": {Input: 1.0 / 1000, Output: 2.0 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen2.5-3b-instruct": {Input: 0.3 / 1000, Output: 0.6 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen-plus": {Input: 0.8 / 1000, Output: 2.0 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen-turbo": {Input: 0.3 / 1000, Output: 0.6 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen-max": {Input: 40.0 / 1000, Output: 120.0 / 1000, Unit: 0.001, Currency: "RMB"}, + "qwen-long": {Input: 0.5 / 1000, Output: 2.0 / 1000, Unit: 0.001, Currency: "RMB"}, +} diff --git a/admin/server/service/gaia/model_provider.go b/admin/server/service/gaia/model_provider.go index 0cf0b0bd1..3721bba68 100644 --- a/admin/server/service/gaia/model_provider.go +++ b/admin/server/service/gaia/model_provider.go @@ -152,27 +152,54 @@ func rmbToUSD(rmb float64) float64 { return rmb / rmbToUSDRate } +// resolvePricing 返回模型定价:优先用从 Dify 拉取的 pricing, +// 其次查内置兜底定价表(BuiltinModelPricing),最后返回 nil。 +func resolvePricing(pricing *gaia.ModelPricing, modelName string) *gaia.ModelPricing { + if pricing != nil && pricing.Unit > 0 { + return pricing + } + // 内置定价表精确匹配 + if p, ok := gaia.BuiltinModelPricing[modelName]; ok { + return &p + } + // 前缀模糊匹配(如 "qwen3.5-plus-xxx" 匹配 "qwen3.5-plus") + lower := strings.ToLower(modelName) + for k, p := range gaia.BuiltinModelPricing { + if strings.HasPrefix(lower, strings.ToLower(k)) { + cp := p + return &cp + } + } + return nil +} + // calcQuotaDelta 根据定价和 token 用量计算本次消耗的配额金额(统一以 USD 计)。 // Dify pricing 字段语义:input/output 为每「unit」个 token 的价格,unit 通常为 0.001(千分之一), -// 即 input=0.0014, unit=0.001 表示每千 token 收 $0.0014 × (tokens/1000)。 +// 即 input=0.0014, unit=0.001 表示每千 token ¥0.0014 × (tokens/1000)。 // 公式:cost = tokens × input × unit(因为 unit=1/1000,等价于 tokens/1000 × input)。 -// 若货币为 RMB,则除以汇率 7.26 换算为 USD,与 account_money_extend.used_quota 存储单位保持一致。 -// 若未找到定价则回退到合理默认值:$0.001/1000 tokens(即每 token $0.000001)。 -func calcQuotaDelta(pricing *gaia.ModelPricing, promptTokens, completionTokens int) float64 { - if pricing == nil || pricing.Unit == 0 { - // 回退:按 $0.001 / 千token 计费(约 GPT-3.5 量级),避免按每 token 计费导致超额扣费 +// 若货币为 RMB/CNY,则除以汇率 7.26 换算为 USD,与 account_money_extend.used_quota 存储单位保持一致。 +// 若 Dify 未返回定价则查内置兜底表;均未命中时按极小默认值记账,避免多扣。 +func calcQuotaDelta(pricing *gaia.ModelPricing, modelName string, promptTokens, completionTokens int) float64 { + p := resolvePricing(pricing, modelName) + if p == nil { + // 兜底:$0.001/千token,仅做记账占位,不应大量触发 + global.GVA_LOG.Warn("calcQuotaDelta 未找到模型定价,使用兜底值", + zap.String("model", modelName), + zap.Int("prompt_tokens", promptTokens), + zap.Int("completion_tokens", completionTokens), + ) return float64(promptTokens+completionTokens) * 0.001 * 0.001 } - inputCost := float64(promptTokens) * pricing.Input * pricing.Unit - outputPrice := pricing.Output + inputCost := float64(promptTokens) * p.Input * p.Unit + outputPrice := p.Output if outputPrice == 0 { - outputPrice = pricing.Input + outputPrice = p.Input } - outputCost := float64(completionTokens) * outputPrice * pricing.Unit + outputCost := float64(completionTokens) * outputPrice * p.Unit total := inputCost + outputCost - // RMB 定价统一换算为 USD 后再扣费,与 used_quota 存储单位保持一致 - if strings.EqualFold(pricing.Currency, "RMB") || strings.EqualFold(pricing.Currency, "CNY") { + // RMB/CNY 定价统一换算为 USD 后再扣费,与 used_quota 存储单位保持一致 + if strings.EqualFold(p.Currency, "RMB") || strings.EqualFold(p.Currency, "CNY") { total = rmbToUSD(total) } return total @@ -1239,9 +1266,9 @@ func (s *ModelProviderService) ProxyRequest( // 计费:仅成功时扣费 if logStatus == "success" { if promptTokens > 0 || completionTokens > 0 { - // LLM 类型:按 token 计费 - pricing, _ := s.fetchModelPricingFromDify(modelOrPath) - delta := calcQuotaDelta(pricing, promptTokens, completionTokens) + // LLM 类型:按 token 计费 + pricing, _ := s.fetchModelPricingFromDify(modelOrPath) + delta := calcQuotaDelta(pricing, modelOrPath, promptTokens, completionTokens) deductAccountQuota(userID, delta) } else if isImageOrPerRequestPath(path) { // 图片生成等无 usage 的接口:按请求次数计费(固定 0.04 USD/次,约 1 张图片单价)