You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

214 lines
5.6 KiB

  1. package controller
  2. import (
  3. "fmt"
  4. "math"
  5. "strings"
  6. "github.com/QuantumNous/new-api/common"
  7. "github.com/QuantumNous/new-api/model"
  8. "github.com/QuantumNous/new-api/service"
  9. "github.com/QuantumNous/new-api/setting/ratio_setting"
  10. "github.com/gin-gonic/gin"
  11. )
  12. // resolveCurrentUser 从 gin.Context 解析当前登录用户信息。
  13. // 未登录或查询失败时 loggedIn=false,groupRatio=1.0。
  14. func resolveCurrentUser(c *gin.Context) (loggedIn bool, userId int, userGroup string, groupRatio float64) {
  15. groupRatio = 1.0
  16. raw, exists := c.Get("id")
  17. if !exists {
  18. return
  19. }
  20. userId = raw.(int)
  21. user, err := model.GetUserCache(userId)
  22. if err != nil {
  23. return
  24. }
  25. loggedIn = true
  26. userGroup = user.Group
  27. groupRatio = service.GetUserGroupRatio(userGroup, userGroup)
  28. return
  29. }
  30. func filterPricingByUsableGroups(pricing []model.Pricing, usableGroup map[string]string) []model.Pricing {
  31. if len(pricing) == 0 {
  32. return pricing
  33. }
  34. if len(usableGroup) == 0 {
  35. return []model.Pricing{}
  36. }
  37. filtered := make([]model.Pricing, 0, len(pricing))
  38. for _, item := range pricing {
  39. if common.StringsContains(item.EnableGroup, "all") {
  40. filtered = append(filtered, item)
  41. continue
  42. }
  43. for _, group := range item.EnableGroup {
  44. if _, ok := usableGroup[group]; ok {
  45. filtered = append(filtered, item)
  46. break
  47. }
  48. }
  49. }
  50. return filtered
  51. }
  52. func GetPricing(c *gin.Context) {
  53. pricing := model.GetPricing()
  54. userId, exists := c.Get("id")
  55. usableGroup := map[string]string{}
  56. groupRatio := map[string]float64{}
  57. for s, f := range ratio_setting.GetGroupRatioCopy() {
  58. groupRatio[s] = f
  59. }
  60. var group string
  61. if exists {
  62. user, err := model.GetUserCache(userId.(int))
  63. if err == nil {
  64. group = user.Group
  65. for g := range groupRatio {
  66. ratio, ok := ratio_setting.GetGroupGroupRatio(group, g)
  67. if ok {
  68. groupRatio[g] = ratio
  69. }
  70. }
  71. }
  72. }
  73. usableGroup = service.GetUserUsableGroups(group)
  74. pricing = filterPricingByUsableGroups(pricing, usableGroup)
  75. // check groupRatio contains usableGroup
  76. for group := range ratio_setting.GetGroupRatioCopy() {
  77. if _, ok := usableGroup[group]; !ok {
  78. delete(groupRatio, group)
  79. }
  80. }
  81. c.JSON(200, gin.H{
  82. "success": true,
  83. "data": pricing,
  84. "vendors": model.GetVendors(),
  85. "group_ratio": groupRatio,
  86. "usable_group": usableGroup,
  87. "supported_endpoint": model.GetSupportedEndpointMap(),
  88. "auto_groups": service.GetUserAutoGroup(group),
  89. "_": "a42d372ccf0b5dd13ecf71203521f9d2",
  90. })
  91. }
  92. // GetUserPricing 获取用户对指定模型的价格信息(原价 vs 用户价)
  93. func GetUserPricing(c *gin.Context) {
  94. modelName := c.Param("model")
  95. // *model 通配符返回值带前导 /,需要去掉
  96. modelName = strings.TrimPrefix(modelName, "/")
  97. if modelName == "" {
  98. common.ApiErrorMsg(c, "模型名不能为空")
  99. return
  100. }
  101. pricingData := model.GetPricingByModel(modelName)
  102. if pricingData == nil {
  103. common.ApiErrorMsg(c, "未找到该模型的定价信息")
  104. return
  105. }
  106. loggedIn, userId, userGroup, groupRatio := resolveCurrentUser(c)
  107. if !loggedIn {
  108. respondOriginalPrice(c, pricingData)
  109. return
  110. }
  111. bestUserChannelRatio := model.GetBestUserChannelRatio(userId, modelName)
  112. totalRatio := groupRatio * bestUserChannelRatio
  113. savingsPercent := int(math.Round((1 - totalRatio) * 100))
  114. result := gin.H{
  115. "success": true,
  116. "model_name": pricingData.ModelName,
  117. "quota_type": pricingData.QuotaType,
  118. "group": userGroup,
  119. "group_ratio": groupRatio,
  120. "user_channel_ratio": bestUserChannelRatio,
  121. "savings_percent": savingsPercent,
  122. "logged_in": true,
  123. }
  124. if pricingData.QuotaType == model.QuotaTypeByTokens {
  125. originalInput := pricingData.ModelRatio * 2
  126. originalOutput := pricingData.ModelRatio * pricingData.CompletionRatio * 2
  127. result["original_input"] = originalInput
  128. result["original_output"] = originalOutput
  129. result["user_input"] = originalInput * totalRatio
  130. result["user_output"] = originalOutput * totalRatio
  131. } else {
  132. result["original_price"] = pricingData.ModelPrice
  133. result["user_price"] = pricingData.ModelPrice * totalRatio
  134. }
  135. if savingsPercent > 0 {
  136. result["discount"] = formatDiscount(totalRatio)
  137. }
  138. c.JSON(200, result)
  139. }
  140. // respondOriginalPrice 未登录或用户查询失败时,只返回原价
  141. func respondOriginalPrice(c *gin.Context, pricingData *model.Pricing) {
  142. result := gin.H{
  143. "success": true,
  144. "model_name": pricingData.ModelName,
  145. "quota_type": pricingData.QuotaType,
  146. "logged_in": false,
  147. }
  148. if pricingData.QuotaType == model.QuotaTypeByTokens {
  149. result["original_input"] = pricingData.ModelRatio * 2
  150. result["original_output"] = pricingData.ModelRatio * pricingData.CompletionRatio * 2
  151. } else {
  152. result["original_price"] = pricingData.ModelPrice
  153. }
  154. c.JSON(200, result)
  155. }
  156. // formatDiscount 将倍率转换为中文折扣格式
  157. func formatDiscount(ratio float64) string {
  158. if ratio <= 0 {
  159. return "免费"
  160. }
  161. discount := int(math.Round(ratio * 10))
  162. if discount >= 10 {
  163. return ""
  164. }
  165. remainder := int(math.Round(ratio*100)) % 10
  166. if remainder == 0 {
  167. return fmt.Sprintf("%d折", discount)
  168. }
  169. return fmt.Sprintf("%.1f折", ratio*10)
  170. }
  171. func ResetModelRatio(c *gin.Context) {
  172. defaultStr := ratio_setting.DefaultModelRatio2JSONString()
  173. err := model.UpdateOption("ModelRatio", defaultStr)
  174. if err != nil {
  175. c.JSON(200, gin.H{
  176. "success": false,
  177. "message": err.Error(),
  178. })
  179. return
  180. }
  181. err = ratio_setting.UpdateModelRatioByJSONString(defaultStr)
  182. if err != nil {
  183. c.JSON(200, gin.H{
  184. "success": false,
  185. "message": err.Error(),
  186. })
  187. return
  188. }
  189. c.JSON(200, gin.H{
  190. "success": true,
  191. "message": "重置模型倍率成功",
  192. })
  193. }