Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 

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