Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

262 rindas
8.7 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. var cacheRatio, cacheCreationRatio, imageRatio, audioRatio, audioCompletionRatio float64
  45. // 尝试获取渠道定价(优先于全局定价)
  46. channelMetaAvailable := info != nil && info.ChannelMeta != nil && info.ChannelId > 0
  47. if channelMetaAvailable {
  48. cp, found := model.GetEffectivePricing(info.OriginModelName, info.ChannelId)
  49. if found {
  50. modelRatio = cp.ModelRatio
  51. completionRatio = cp.CompletionRatio
  52. modelPrice = cp.ModelPrice
  53. usePrice = cp.QuotaType == model.QuotaTypeByCall
  54. channelPricingFound = true
  55. // 渠道定价的扩展比率(非零值直接使用)
  56. cacheRatio = cp.CacheRatio
  57. cacheCreationRatio = cp.CacheCreationRatio
  58. imageRatio = cp.ImageRatio
  59. audioRatio = cp.AudioRatio
  60. audioCompletionRatio = cp.AudioCompletionRatio
  61. if common.DebugEnabled {
  62. println(fmt.Sprintf("[ChannelPricing] hit: model=%s channel=%d source=cache", info.OriginModelName, info.ChannelId))
  63. }
  64. }
  65. }
  66. // 如果没有渠道定价,使用全局定价
  67. if !channelPricingFound {
  68. modelPrice, usePrice = ratio_setting.GetModelPrice(info.OriginModelName, false)
  69. }
  70. groupRatioInfo := HandleGroupRatio(c, info)
  71. var preConsumedQuota int
  72. var freeModel bool
  73. if !usePrice {
  74. preConsumedTokens := common.Max(promptTokens, common.PreConsumedQuota)
  75. if meta.MaxTokens != 0 {
  76. preConsumedTokens += meta.MaxTokens
  77. }
  78. // 如果没有找到渠道定价,获取全局 modelRatio 和 completionRatio
  79. if !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. ratio := modelRatio * groupRatioInfo.GroupRatio
  95. preConsumedQuota = int(float64(preConsumedTokens) * ratio)
  96. } else {
  97. if meta.ImagePriceRatio != 0 {
  98. modelPrice = modelPrice * meta.ImagePriceRatio
  99. }
  100. preConsumedQuota = int(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
  101. }
  102. // 全局比率作为回退(仅当渠道未设置、值为 0 时生效)
  103. // 必须放在 usePrice 判断之外,因为 UpdatePriceDataForChannelPricing 可能改变 UsePrice
  104. if cacheRatio == 0 {
  105. cacheRatio, _ = ratio_setting.GetCacheRatio(info.OriginModelName)
  106. }
  107. if cacheCreationRatio == 0 {
  108. cacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(info.OriginModelName)
  109. }
  110. if imageRatio == 0 {
  111. imageRatio, _ = ratio_setting.GetImageRatio(info.OriginModelName)
  112. }
  113. if audioRatio == 0 {
  114. audioRatio = ratio_setting.GetAudioRatio(info.OriginModelName)
  115. }
  116. if audioCompletionRatio == 0 {
  117. audioCompletionRatio = ratio_setting.GetAudioCompletionRatio(info.OriginModelName)
  118. }
  119. // check if free model pre-consume is disabled
  120. if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume {
  121. // if model price or ratio is 0, do not pre-consume quota
  122. if groupRatioInfo.GroupRatio == 0 {
  123. preConsumedQuota = 0
  124. freeModel = true
  125. } else if usePrice {
  126. if modelPrice == 0 {
  127. preConsumedQuota = 0
  128. freeModel = true
  129. }
  130. } else {
  131. if modelRatio == 0 {
  132. preConsumedQuota = 0
  133. freeModel = true
  134. }
  135. }
  136. }
  137. priceData := types.PriceData{
  138. FreeModel: freeModel,
  139. ModelPrice: modelPrice,
  140. ModelRatio: modelRatio,
  141. CompletionRatio: completionRatio,
  142. GroupRatioInfo: groupRatioInfo,
  143. UsePrice: usePrice,
  144. QuotaToPreConsume: preConsumedQuota,
  145. CacheRatio: cacheRatio,
  146. ImageRatio: imageRatio,
  147. AudioRatio: audioRatio,
  148. AudioCompletionRatio: audioCompletionRatio,
  149. CacheCreationRatio: cacheCreationRatio,
  150. CacheCreation5mRatio: cacheCreationRatio,
  151. CacheCreation1hRatio: cacheCreationRatio * types.ClaudeCacheCreation1hMultiplier,
  152. }
  153. if common.DebugEnabled {
  154. println(fmt.Sprintf("model_price_helper result: %s", priceData.ToSetting()))
  155. }
  156. info.PriceData = priceData
  157. return priceData, nil
  158. }
  159. // ModelPriceHelperPerCall 按次计费的 PriceHelper (MJ、Task)
  160. func ModelPriceHelperPerCall(c *gin.Context, info *relaycommon.RelayInfo) types.PriceData {
  161. groupRatioInfo := HandleGroupRatio(c, info)
  162. modelPrice, success := ratio_setting.GetModelPrice(info.OriginModelName, true)
  163. // 如果没有配置价格,则使用默认价格
  164. if !success {
  165. defaultPrice, ok := ratio_setting.GetDefaultModelPriceMap()[info.OriginModelName]
  166. if !ok {
  167. modelPrice = 0.1
  168. } else {
  169. modelPrice = defaultPrice
  170. }
  171. }
  172. quota := int(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
  173. // 免费模型检测(与 ModelPriceHelper 对齐)
  174. freeModel := false
  175. if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume {
  176. if groupRatioInfo.GroupRatio == 0 || modelPrice == 0 {
  177. quota = 0
  178. freeModel = true
  179. }
  180. }
  181. priceData := types.PriceData{
  182. FreeModel: freeModel,
  183. ModelPrice: modelPrice,
  184. Quota: quota,
  185. GroupRatioInfo: groupRatioInfo,
  186. }
  187. return priceData
  188. }
  189. func ContainPriceOrRatio(modelName string) bool {
  190. _, ok := ratio_setting.GetModelPrice(modelName, false)
  191. if ok {
  192. return true
  193. }
  194. _, ok, _ = ratio_setting.GetModelRatio(modelName)
  195. if ok {
  196. return true
  197. }
  198. return false
  199. }
  200. // UpdatePriceDataForChannelPricing 在渠道选择后更新 PriceData 以使用渠道定价
  201. // ModelPriceHelper 在渠道选择之前被调用,此时 ChannelMeta 为 nil,导致渠道定价无法使用。
  202. // 需要在渠道选择后调用此函数更新价格数据,用于后续的日志记录和计费计算。
  203. func UpdatePriceDataForChannelPricing(c *gin.Context, info *relaycommon.RelayInfo, channelId int) {
  204. if info == nil || channelId <= 0 {
  205. return
  206. }
  207. cp, found := model.GetEffectivePricing(info.OriginModelName, channelId)
  208. if !found {
  209. return
  210. }
  211. info.PriceData.ModelRatio = cp.ModelRatio
  212. info.PriceData.CompletionRatio = cp.CompletionRatio
  213. info.PriceData.UsePrice = cp.QuotaType == model.QuotaTypeByCall
  214. if info.PriceData.UsePrice {
  215. info.PriceData.ModelPrice = cp.ModelPrice
  216. } else {
  217. info.PriceData.ModelPrice = -1
  218. }
  219. info.PriceData.ApplyChannelPricingRatios(cp.CacheRatio, cp.CacheCreationRatio, cp.ImageRatio, cp.AudioRatio, cp.AudioCompletionRatio)
  220. if info.PriceData.UsePrice {
  221. info.PriceData.QuotaToPreConsume = int(
  222. cp.ModelPrice * common.QuotaPerUnit * info.PriceData.GroupRatioInfo.GroupRatio)
  223. } else {
  224. estimateTokens := info.GetEstimatePromptTokens()
  225. if estimateTokens > 0 {
  226. ratio := cp.ModelRatio * info.PriceData.GroupRatioInfo.GroupRatio
  227. info.PriceData.QuotaToPreConsume = int(float64(estimateTokens) * ratio)
  228. }
  229. }
  230. if common.DebugEnabled {
  231. println(fmt.Sprintf("[ChannelPricing] updatePriceData: model=%s channel=%d modelRatio=%.4f completionRatio=%.4f cacheRatio=%.4f imageRatio=%.4f audioRatio=%.4f",
  232. info.OriginModelName, channelId, cp.ModelRatio, cp.CompletionRatio, cp.CacheRatio, cp.ImageRatio, cp.AudioRatio))
  233. }
  234. }