Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

255 lignes
8.5 KiB

  1. package helper
  2. import (
  3. "fmt"
  4. "github.com/QuantumNous/new-api/common"
  5. "github.com/QuantumNous/new-api/logger"
  6. "github.com/QuantumNous/new-api/model"
  7. relaycommon "github.com/QuantumNous/new-api/relay/common"
  8. "github.com/QuantumNous/new-api/setting/operation_setting"
  9. "github.com/QuantumNous/new-api/setting/ratio_setting"
  10. "github.com/QuantumNous/new-api/types"
  11. "github.com/gin-gonic/gin"
  12. )
  13. // HandleGroupRatio checks for "auto_group" in the context and updates the group ratio and relayInfo.UsingGroup if present
  14. func HandleGroupRatio(ctx *gin.Context, relayInfo *relaycommon.RelayInfo) types.GroupRatioInfo {
  15. groupRatioInfo := types.GroupRatioInfo{
  16. GroupRatio: 1.0, // default ratio
  17. GroupSpecialRatio: -1,
  18. }
  19. // check auto group
  20. autoGroup, exists := ctx.Get("auto_group")
  21. if exists {
  22. logger.LogDebug(ctx, fmt.Sprintf("final group: %s", autoGroup))
  23. relayInfo.UsingGroup = autoGroup.(string)
  24. }
  25. // check user group special ratio
  26. userGroupRatio, ok := ratio_setting.GetGroupGroupRatio(relayInfo.UserGroup, relayInfo.UsingGroup)
  27. if ok {
  28. // user group special ratio
  29. groupRatioInfo.GroupSpecialRatio = userGroupRatio
  30. groupRatioInfo.GroupRatio = userGroupRatio
  31. groupRatioInfo.HasSpecialRatio = true
  32. } else {
  33. // normal group ratio
  34. groupRatioInfo.GroupRatio = ratio_setting.GetGroupRatio(relayInfo.UsingGroup)
  35. }
  36. return groupRatioInfo
  37. }
  38. func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens int, meta *types.TokenCountMeta) (types.PriceData, error) {
  39. var modelPrice float64
  40. var usePrice bool
  41. var modelRatio float64
  42. var completionRatio float64
  43. var channelPricingFound bool
  44. // 尝试获取渠道定价(优先于全局定价)
  45. channelMetaAvailable := info != nil && info.ChannelMeta != nil && info.ChannelId > 0
  46. if channelMetaAvailable {
  47. cp, found := model.GetEffectivePricing(info.OriginModelName, info.ChannelId)
  48. if found {
  49. modelRatio = cp.ModelRatio
  50. completionRatio = cp.CompletionRatio
  51. modelPrice = cp.ModelPrice
  52. usePrice = cp.QuotaType == model.QuotaTypeByCall
  53. channelPricingFound = true
  54. if common.DebugEnabled {
  55. println(fmt.Sprintf("[ChannelPricing] hit: model=%s channel=%d source=cache", info.OriginModelName, info.ChannelId))
  56. }
  57. }
  58. }
  59. // 如果没有渠道定价,使用全局定价
  60. if !channelPricingFound {
  61. modelPrice, usePrice = ratio_setting.GetModelPrice(info.OriginModelName, false)
  62. }
  63. groupRatioInfo := HandleGroupRatio(c, info)
  64. var preConsumedQuota int
  65. var freeModel bool
  66. if !usePrice {
  67. preConsumedTokens := common.Max(promptTokens, common.PreConsumedQuota)
  68. if meta.MaxTokens != 0 {
  69. preConsumedTokens += meta.MaxTokens
  70. }
  71. // 如果没有找到渠道定价,获取全局 modelRatio 和 completionRatio
  72. if !channelPricingFound {
  73. var success bool
  74. var matchName string
  75. modelRatio, success, matchName = ratio_setting.GetModelRatio(info.OriginModelName)
  76. if !success {
  77. acceptUnsetRatio := false
  78. if info.UserSetting.AcceptUnsetRatioModel {
  79. acceptUnsetRatio = true
  80. }
  81. if !acceptUnsetRatio {
  82. return types.PriceData{}, fmt.Errorf("模型 %s 倍率或价格未配置,请联系管理员设置或开启自用模式;Model %s ratio or price not set, please set or start self-use mode", matchName, matchName)
  83. }
  84. }
  85. completionRatio = ratio_setting.GetCompletionRatio(info.OriginModelName)
  86. }
  87. ratio := modelRatio * groupRatioInfo.GroupRatio
  88. preConsumedQuota = int(float64(preConsumedTokens) * ratio)
  89. } else {
  90. if meta.ImagePriceRatio != 0 {
  91. modelPrice = modelPrice * meta.ImagePriceRatio
  92. }
  93. preConsumedQuota = int(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
  94. }
  95. // check if free model pre-consume is disabled
  96. if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume {
  97. // if model price or ratio is 0, do not pre-consume quota
  98. if groupRatioInfo.GroupRatio == 0 {
  99. preConsumedQuota = 0
  100. freeModel = true
  101. } else if usePrice {
  102. if modelPrice == 0 {
  103. preConsumedQuota = 0
  104. freeModel = true
  105. }
  106. } else {
  107. if modelRatio == 0 {
  108. preConsumedQuota = 0
  109. freeModel = true
  110. }
  111. }
  112. }
  113. priceData := types.PriceData{
  114. FreeModel: freeModel,
  115. ModelPrice: modelPrice,
  116. ModelRatio: modelRatio,
  117. CompletionRatio: completionRatio,
  118. GroupRatioInfo: groupRatioInfo,
  119. UsePrice: usePrice,
  120. QuotaToPreConsume: preConsumedQuota,
  121. }
  122. // 应用渠道定价的扩展比率(非零值覆盖),然后回退全局比率
  123. if channelMetaAvailable {
  124. if cp, found := model.GetEffectivePricing(info.OriginModelName, info.ChannelId); found {
  125. priceData.ApplyChannelPricingRatios(cp.CacheRatio, cp.CacheCreationRatio, cp.ImageRatio, cp.AudioRatio, cp.AudioCompletionRatio)
  126. }
  127. }
  128. // 全局比率作为回退(仅当对应字段仍为 0 时生效)
  129. if priceData.CacheRatio == 0 {
  130. priceData.CacheRatio, _ = ratio_setting.GetCacheRatio(info.OriginModelName)
  131. }
  132. if priceData.CacheCreationRatio == 0 {
  133. priceData.CacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(info.OriginModelName)
  134. priceData.CacheCreation5mRatio = priceData.CacheCreationRatio
  135. priceData.CacheCreation1hRatio = priceData.CacheCreationRatio * types.ClaudeCacheCreation1hMultiplier
  136. }
  137. if priceData.ImageRatio == 0 {
  138. priceData.ImageRatio, _ = ratio_setting.GetImageRatio(info.OriginModelName)
  139. }
  140. if priceData.AudioRatio == 0 {
  141. priceData.AudioRatio = ratio_setting.GetAudioRatio(info.OriginModelName)
  142. }
  143. if priceData.AudioCompletionRatio == 0 {
  144. priceData.AudioCompletionRatio = ratio_setting.GetAudioCompletionRatio(info.OriginModelName)
  145. }
  146. if common.DebugEnabled {
  147. println(fmt.Sprintf("model_price_helper result: %s", priceData.ToSetting()))
  148. }
  149. info.PriceData = priceData
  150. return priceData, nil
  151. }
  152. // ModelPriceHelperPerCall 按次计费的 PriceHelper (MJ、Task)
  153. func ModelPriceHelperPerCall(c *gin.Context, info *relaycommon.RelayInfo) types.PriceData {
  154. groupRatioInfo := HandleGroupRatio(c, info)
  155. modelPrice, success := ratio_setting.GetModelPrice(info.OriginModelName, true)
  156. // 如果没有配置价格,则使用默认价格
  157. if !success {
  158. defaultPrice, ok := ratio_setting.GetDefaultModelPriceMap()[info.OriginModelName]
  159. if !ok {
  160. modelPrice = 0.1
  161. } else {
  162. modelPrice = defaultPrice
  163. }
  164. }
  165. quota := int(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
  166. // 免费模型检测(与 ModelPriceHelper 对齐)
  167. freeModel := false
  168. if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume {
  169. if groupRatioInfo.GroupRatio == 0 || modelPrice == 0 {
  170. quota = 0
  171. freeModel = true
  172. }
  173. }
  174. priceData := types.PriceData{
  175. FreeModel: freeModel,
  176. ModelPrice: modelPrice,
  177. Quota: quota,
  178. GroupRatioInfo: groupRatioInfo,
  179. }
  180. return priceData
  181. }
  182. func ContainPriceOrRatio(modelName string) bool {
  183. _, ok := ratio_setting.GetModelPrice(modelName, false)
  184. if ok {
  185. return true
  186. }
  187. _, ok, _ = ratio_setting.GetModelRatio(modelName)
  188. if ok {
  189. return true
  190. }
  191. return false
  192. }
  193. // UpdatePriceDataForChannelPricing 在渠道选择后更新 PriceData 以使用渠道定价
  194. // ModelPriceHelper 在渠道选择之前被调用,此时 ChannelMeta 为 nil,导致渠道定价无法使用。
  195. // 需要在渠道选择后调用此函数更新价格数据,用于后续的日志记录和计费计算。
  196. func UpdatePriceDataForChannelPricing(c *gin.Context, info *relaycommon.RelayInfo, channelId int) {
  197. if info == nil || channelId <= 0 {
  198. return
  199. }
  200. cp, found := model.GetEffectivePricing(info.OriginModelName, channelId)
  201. if !found {
  202. return
  203. }
  204. info.PriceData.ModelRatio = cp.ModelRatio
  205. info.PriceData.CompletionRatio = cp.CompletionRatio
  206. info.PriceData.UsePrice = cp.QuotaType == model.QuotaTypeByCall
  207. if info.PriceData.UsePrice {
  208. info.PriceData.ModelPrice = cp.ModelPrice
  209. } else {
  210. info.PriceData.ModelPrice = -1
  211. }
  212. info.PriceData.ApplyChannelPricingRatios(cp.CacheRatio, cp.CacheCreationRatio, cp.ImageRatio, cp.AudioRatio, cp.AudioCompletionRatio)
  213. if info.PriceData.UsePrice {
  214. info.PriceData.QuotaToPreConsume = int(
  215. cp.ModelPrice * common.QuotaPerUnit * info.PriceData.GroupRatioInfo.GroupRatio)
  216. } else {
  217. estimateTokens := info.GetEstimatePromptTokens()
  218. if estimateTokens > 0 {
  219. ratio := cp.ModelRatio * info.PriceData.GroupRatioInfo.GroupRatio
  220. info.PriceData.QuotaToPreConsume = int(float64(estimateTokens) * ratio)
  221. }
  222. }
  223. if common.DebugEnabled {
  224. println(fmt.Sprintf("[ChannelPricing] updatePriceData: model=%s channel=%d modelRatio=%.4f completionRatio=%.4f cacheRatio=%.4f imageRatio=%.4f audioRatio=%.4f",
  225. info.OriginModelName, channelId, cp.ModelRatio, cp.CompletionRatio, cp.CacheRatio, cp.ImageRatio, cp.AudioRatio))
  226. }
  227. }