選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 

233 行
7.2 KiB

  1. package helper
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/QuantumNous/new-api/common"
  6. "github.com/QuantumNous/new-api/dto"
  7. "github.com/QuantumNous/new-api/logger"
  8. relaycommon "github.com/QuantumNous/new-api/relay/common"
  9. "github.com/QuantumNous/new-api/setting/operation_setting"
  10. "github.com/QuantumNous/new-api/setting/ratio_setting"
  11. "github.com/QuantumNous/new-api/types"
  12. "github.com/gin-gonic/gin"
  13. )
  14. // HandleGroupRatio checks for "auto_group" in the context and updates the group ratio and relayInfo.UsingGroup if present
  15. func HandleGroupRatio(ctx *gin.Context, relayInfo *relaycommon.RelayInfo) types.GroupRatioInfo {
  16. groupRatioInfo := types.GroupRatioInfo{
  17. GroupRatio: 1.0,
  18. GroupSpecialRatio: -1,
  19. }
  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. userGroupRatio, ok := ratio_setting.GetGroupGroupRatio(relayInfo.UserGroup, relayInfo.UsingGroup)
  26. if ok {
  27. groupRatioInfo.GroupSpecialRatio = userGroupRatio
  28. groupRatioInfo.GroupRatio = userGroupRatio
  29. groupRatioInfo.HasSpecialRatio = true
  30. } else {
  31. groupRatioInfo.GroupRatio = ratio_setting.GetGroupRatio(relayInfo.UsingGroup)
  32. }
  33. return groupRatioInfo
  34. }
  35. func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens int, meta *types.TokenCountMeta) (types.PriceData, error) {
  36. var modelPrice float64
  37. var usePrice bool
  38. var modelRatio float64
  39. var completionRatio float64
  40. modelPrice, usePrice = ratio_setting.GetModelPrice(info.OriginModelName, false)
  41. groupRatioInfo := HandleGroupRatio(c, info)
  42. var preConsumedQuota int
  43. var freeModel bool
  44. if !usePrice {
  45. preConsumedTokens := common.Max(promptTokens, common.PreConsumedQuota)
  46. if meta.MaxTokens != 0 {
  47. preConsumedTokens += meta.MaxTokens
  48. }
  49. var success bool
  50. var matchName string
  51. modelRatio, success, matchName = ratio_setting.GetModelRatio(info.OriginModelName)
  52. if !success {
  53. acceptUnsetRatio := false
  54. if info.UserSetting.AcceptUnsetRatioModel {
  55. acceptUnsetRatio = true
  56. }
  57. if !acceptUnsetRatio {
  58. return types.PriceData{}, fmt.Errorf("模型 %s 倍率或价格未配置,请联系管理员设置或开启自用模式;Model %s ratio or price not set, please set or start self-use mode", matchName, matchName)
  59. }
  60. }
  61. completionRatio = ratio_setting.GetCompletionRatio(info.OriginModelName)
  62. ratio := modelRatio * groupRatioInfo.GroupRatio
  63. preConsumedQuota = int(float64(preConsumedTokens) * ratio)
  64. } else {
  65. if meta.ImagePriceRatio != 0 {
  66. modelPrice = modelPrice * meta.ImagePriceRatio
  67. }
  68. preConsumedQuota = int(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
  69. }
  70. cacheRatio, _ := ratio_setting.GetCacheRatio(info.OriginModelName)
  71. cacheCreationRatio, _ := ratio_setting.GetCreateCacheRatio(info.OriginModelName)
  72. imageRatio, _ := ratio_setting.GetImageRatio(info.OriginModelName)
  73. audioRatio := ratio_setting.GetAudioRatio(info.OriginModelName)
  74. audioCompletionRatio := ratio_setting.GetAudioCompletionRatio(info.OriginModelName)
  75. if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume {
  76. if groupRatioInfo.GroupRatio == 0 {
  77. preConsumedQuota = 0
  78. freeModel = true
  79. } else if usePrice {
  80. if modelPrice == 0 {
  81. preConsumedQuota = 0
  82. freeModel = true
  83. }
  84. } else if modelRatio == 0 {
  85. preConsumedQuota = 0
  86. freeModel = true
  87. }
  88. }
  89. priceData := types.PriceData{
  90. FreeModel: freeModel,
  91. ModelPrice: modelPrice,
  92. ModelRatio: modelRatio,
  93. CompletionRatio: completionRatio,
  94. GroupRatioInfo: groupRatioInfo,
  95. UsePrice: usePrice,
  96. QuotaToPreConsume: preConsumedQuota,
  97. CacheRatio: cacheRatio,
  98. ImageRatio: imageRatio,
  99. AudioRatio: audioRatio,
  100. AudioCompletionRatio: audioCompletionRatio,
  101. CacheCreationRatio: cacheCreationRatio,
  102. CacheCreation5mRatio: cacheCreationRatio,
  103. CacheCreation1hRatio: cacheCreationRatio * types.ClaudeCacheCreation1hMultiplier,
  104. }
  105. if common.DebugEnabled {
  106. println(fmt.Sprintf("model_price_helper result: %s", priceData.ToSetting()))
  107. }
  108. info.PriceData = priceData
  109. return priceData, nil
  110. }
  111. // ModelPriceHelperPerCall 按次计费的 PriceHelper (MJ、Task)
  112. func ModelPriceHelperPerCall(c *gin.Context, info *relaycommon.RelayInfo) (types.PriceData, *dto.TaskError) {
  113. groupRatioInfo := HandleGroupRatio(c, info)
  114. if info.PricingDecisionFrozen != nil {
  115. priceData := priceDataFromDecision(info.PricingDecisionFrozen)
  116. info.PriceData = priceData
  117. return priceData, nil
  118. }
  119. if info.OriginPricing != nil {
  120. var (
  121. decision *types.PricingDecision
  122. taskErr *dto.TaskError
  123. )
  124. if info.OriginPricing.BillingMode == types.BillingModeMatrix {
  125. decision, taskErr = buildRemixMatrixDecision(info.OriginPricing, groupRatioInfo)
  126. } else {
  127. decision, taskErr = buildRemixLegacyDecision(info.OriginPricing, groupRatioInfo)
  128. }
  129. if taskErr != nil {
  130. return types.PriceData{}, taskErr
  131. }
  132. info.PricingDecisionFrozen = decision
  133. priceData := priceDataFromDecision(decision)
  134. info.PriceData = priceData
  135. return priceData, nil
  136. }
  137. if info.PricingConfigSnapshotLoaded && info.PricingConfigSnapshot != nil {
  138. resolver, err := NewDimensionResolver(c)
  139. if err != nil {
  140. return types.PriceData{}, pricingDimensionTaskError(err)
  141. }
  142. resolved, err := resolver.ResolveDimensions(info.PricingConfigSnapshot.Dimensions)
  143. if err != nil {
  144. return types.PriceData{}, pricingDimensionTaskError(err)
  145. }
  146. result, taskErr := lookupPricingConfig(info.PricingConfigSnapshot, resolved)
  147. if taskErr != nil {
  148. return types.PriceData{}, taskErr
  149. }
  150. decision := buildMatchedDecision(info.PricingConfigSnapshot, result, groupRatioInfo)
  151. if result.Mode != types.PricingModeMatched {
  152. decision = buildFallbackDecision(info.PricingConfigSnapshot, result, groupRatioInfo)
  153. }
  154. info.PricingDecisionFrozen = decision
  155. priceData := priceDataFromDecision(decision)
  156. info.PriceData = priceData
  157. return priceData, nil
  158. }
  159. modelPrice, success := ratio_setting.GetModelPrice(info.OriginModelName, true)
  160. if !success {
  161. defaultPrice, ok := ratio_setting.GetDefaultModelPriceMap()[info.OriginModelName]
  162. if !ok {
  163. modelPrice = 0.1
  164. } else {
  165. modelPrice = defaultPrice
  166. }
  167. }
  168. quota := int(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
  169. freeModel := false
  170. if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume {
  171. if groupRatioInfo.GroupRatio == 0 || modelPrice == 0 {
  172. quota = 0
  173. freeModel = true
  174. }
  175. }
  176. priceData := types.PriceData{
  177. FreeModel: freeModel,
  178. ModelPrice: modelPrice,
  179. Quota: quota,
  180. GroupRatioInfo: groupRatioInfo,
  181. }
  182. decision := buildLegacyDecision(modelPrice, groupRatioInfo, freeModel, quota)
  183. priceData.PricingDecision = decision
  184. info.PricingDecisionFrozen = decision
  185. info.PriceData = priceData
  186. return priceData, nil
  187. }
  188. func pricingDimensionTaskError(err error) *dto.TaskError {
  189. return &dto.TaskError{
  190. Code: "pricing_dimension_resolve_failed",
  191. Message: err.Error(),
  192. StatusCode: http.StatusBadRequest,
  193. LocalError: true,
  194. Error: err,
  195. }
  196. }
  197. func ContainPriceOrRatio(modelName string) bool {
  198. _, ok := ratio_setting.GetModelPrice(modelName, false)
  199. if ok {
  200. return true
  201. }
  202. _, ok, _ = ratio_setting.GetModelRatio(modelName)
  203. if ok {
  204. return true
  205. }
  206. return false
  207. }