Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

273 rader
7.1 KiB

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