No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

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