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.
 
 
 

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