Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 

339 righe
8.3 KiB

  1. package controller
  2. import (
  3. "encoding/json"
  4. "sort"
  5. "strconv"
  6. "strings"
  7. "github.com/QuantumNous/new-api/common"
  8. "github.com/QuantumNous/new-api/constant"
  9. "github.com/QuantumNous/new-api/model"
  10. "github.com/gin-gonic/gin"
  11. )
  12. // GetAllModelsMeta 获取模型列表(分页)
  13. func GetAllModelsMeta(c *gin.Context) {
  14. pageInfo := common.GetPageQuery(c)
  15. modelsMeta, err := model.GetAllModels(pageInfo.GetStartIdx(), pageInfo.GetPageSize())
  16. if err != nil {
  17. common.ApiError(c, err)
  18. return
  19. }
  20. // 批量填充附加字段,提升列表接口性能
  21. enrichModels(modelsMeta)
  22. var total int64
  23. model.DB.Model(&model.Model{}).Count(&total)
  24. // 统计供应商计数(全部数据,不受分页影响)
  25. vendorCounts, _ := model.GetVendorModelCounts()
  26. pageInfo.SetTotal(int(total))
  27. pageInfo.SetItems(modelsMeta)
  28. common.ApiSuccess(c, gin.H{
  29. "items": modelsMeta,
  30. "total": total,
  31. "page": pageInfo.GetPage(),
  32. "page_size": pageInfo.GetPageSize(),
  33. "vendor_counts": vendorCounts,
  34. })
  35. }
  36. // SearchModelsMeta 搜索模型列表
  37. func SearchModelsMeta(c *gin.Context) {
  38. keyword := c.Query("keyword")
  39. vendor := c.Query("vendor")
  40. pageInfo := common.GetPageQuery(c)
  41. modelsMeta, total, err := model.SearchModels(keyword, vendor, pageInfo.GetStartIdx(), pageInfo.GetPageSize())
  42. if err != nil {
  43. common.ApiError(c, err)
  44. return
  45. }
  46. // 批量填充附加字段,提升列表接口性能
  47. enrichModels(modelsMeta)
  48. pageInfo.SetTotal(int(total))
  49. pageInfo.SetItems(modelsMeta)
  50. common.ApiSuccess(c, pageInfo)
  51. }
  52. // GetModelMeta 根据 ID 获取单条模型信息
  53. func GetModelMeta(c *gin.Context) {
  54. idStr := c.Param("id")
  55. id, err := strconv.Atoi(idStr)
  56. if err != nil {
  57. common.ApiError(c, err)
  58. return
  59. }
  60. var m model.Model
  61. if err := model.DB.First(&m, id).Error; err != nil {
  62. common.ApiError(c, err)
  63. return
  64. }
  65. enrichModels([]*model.Model{&m})
  66. common.ApiSuccess(c, &m)
  67. }
  68. // CreateModelMeta 新建模型
  69. func CreateModelMeta(c *gin.Context) {
  70. var m model.Model
  71. if err := c.ShouldBindJSON(&m); err != nil {
  72. common.ApiError(c, err)
  73. return
  74. }
  75. if m.ModelName == "" {
  76. common.ApiErrorMsg(c, "模型名称不能为空")
  77. return
  78. }
  79. // 名称冲突检查
  80. if dup, err := model.IsModelNameDuplicated(0, m.ModelName); err != nil {
  81. common.ApiError(c, err)
  82. return
  83. } else if dup {
  84. common.ApiErrorMsg(c, "模型名称已存在")
  85. return
  86. }
  87. if strings.TrimSpace(m.Endpoints) == "" {
  88. m.Endpoints = `["openai"]`
  89. }
  90. if err := m.Insert(); err != nil {
  91. common.ApiError(c, err)
  92. return
  93. }
  94. model.RefreshPricing()
  95. common.ApiSuccess(c, &m)
  96. }
  97. // UpdateModelMeta 更新模型
  98. func UpdateModelMeta(c *gin.Context) {
  99. statusOnly := c.Query("status_only") == "true"
  100. var m model.Model
  101. if err := c.ShouldBindJSON(&m); err != nil {
  102. common.ApiError(c, err)
  103. return
  104. }
  105. if m.Id == 0 {
  106. common.ApiErrorMsg(c, "缺少模型 ID")
  107. return
  108. }
  109. if statusOnly {
  110. // 只更新状态,防止误清空其他字段
  111. if err := model.DB.Model(&model.Model{}).Where("id = ?", m.Id).Update("status", m.Status).Error; err != nil {
  112. common.ApiError(c, err)
  113. return
  114. }
  115. } else {
  116. // 名称冲突检查
  117. if dup, err := model.IsModelNameDuplicated(m.Id, m.ModelName); err != nil {
  118. common.ApiError(c, err)
  119. return
  120. } else if dup {
  121. common.ApiErrorMsg(c, "模型名称已存在")
  122. return
  123. }
  124. if strings.TrimSpace(m.Endpoints) == "" {
  125. m.Endpoints = `["openai"]`
  126. }
  127. if err := m.Update(); err != nil {
  128. common.ApiError(c, err)
  129. return
  130. }
  131. }
  132. model.RefreshPricing()
  133. common.ApiSuccess(c, &m)
  134. }
  135. // DeleteModelMeta 删除模型
  136. func DeleteModelMeta(c *gin.Context) {
  137. idStr := c.Param("id")
  138. id, err := strconv.Atoi(idStr)
  139. if err != nil {
  140. common.ApiError(c, err)
  141. return
  142. }
  143. if err := model.DB.Delete(&model.Model{}, id).Error; err != nil {
  144. common.ApiError(c, err)
  145. return
  146. }
  147. model.RefreshPricing()
  148. common.ApiSuccess(c, nil)
  149. }
  150. // enrichModels 批量填充附加信息:端点、渠道、分组、计费类型,避免 N+1 查询
  151. func enrichModels(models []*model.Model) {
  152. if len(models) == 0 {
  153. return
  154. }
  155. // 1) 拆分精确与规则匹配
  156. exactNames := make([]string, 0)
  157. exactIdx := make(map[string][]int) // modelName -> indices in models
  158. ruleIndices := make([]int, 0)
  159. for i, m := range models {
  160. if m == nil {
  161. continue
  162. }
  163. if m.NameRule == model.NameRuleExact {
  164. exactNames = append(exactNames, m.ModelName)
  165. exactIdx[m.ModelName] = append(exactIdx[m.ModelName], i)
  166. } else {
  167. ruleIndices = append(ruleIndices, i)
  168. }
  169. }
  170. // 2) 批量查询精确模型的绑定渠道
  171. channelsByModel, _ := model.GetBoundChannelsByModelsMap(exactNames)
  172. // 3) 精确模型:端点从缓存、渠道批量映射、分组/计费类型从缓存
  173. for name, indices := range exactIdx {
  174. chs := channelsByModel[name]
  175. for _, idx := range indices {
  176. mm := models[idx]
  177. if mm.Endpoints == "" {
  178. eps := model.GetModelSupportEndpointTypes(mm.ModelName)
  179. if b, err := json.Marshal(eps); err == nil {
  180. mm.Endpoints = string(b)
  181. }
  182. }
  183. mm.BoundChannels = chs
  184. mm.EnableGroups = model.GetModelEnableGroups(mm.ModelName)
  185. mm.QuotaTypes = model.GetModelQuotaTypes(mm.ModelName)
  186. }
  187. }
  188. if len(ruleIndices) == 0 {
  189. return
  190. }
  191. // 4) 一次性读取定价缓存,内存匹配所有规则模型
  192. pricings := model.GetPricing()
  193. // 为全部规则模型收集匹配名集合、端点并集、分组并集、配额集合
  194. matchedNamesByIdx := make(map[int][]string)
  195. endpointSetByIdx := make(map[int]map[constant.EndpointType]struct{})
  196. groupSetByIdx := make(map[int]map[string]struct{})
  197. quotaSetByIdx := make(map[int]map[int]struct{})
  198. for _, p := range pricings {
  199. for _, idx := range ruleIndices {
  200. mm := models[idx]
  201. var matched bool
  202. switch mm.NameRule {
  203. case model.NameRulePrefix:
  204. matched = strings.HasPrefix(p.ModelName, mm.ModelName)
  205. case model.NameRuleSuffix:
  206. matched = strings.HasSuffix(p.ModelName, mm.ModelName)
  207. case model.NameRuleContains:
  208. matched = strings.Contains(p.ModelName, mm.ModelName)
  209. }
  210. if !matched {
  211. continue
  212. }
  213. matchedNamesByIdx[idx] = append(matchedNamesByIdx[idx], p.ModelName)
  214. es := endpointSetByIdx[idx]
  215. if es == nil {
  216. es = make(map[constant.EndpointType]struct{})
  217. endpointSetByIdx[idx] = es
  218. }
  219. for _, et := range p.SupportedEndpointTypes {
  220. es[et] = struct{}{}
  221. }
  222. gs := groupSetByIdx[idx]
  223. if gs == nil {
  224. gs = make(map[string]struct{})
  225. groupSetByIdx[idx] = gs
  226. }
  227. for _, g := range p.EnableGroup {
  228. gs[g] = struct{}{}
  229. }
  230. qs := quotaSetByIdx[idx]
  231. if qs == nil {
  232. qs = make(map[int]struct{})
  233. quotaSetByIdx[idx] = qs
  234. }
  235. qs[p.QuotaType] = struct{}{}
  236. }
  237. }
  238. // 5) 汇总所有匹配到的模型名称,批量查询一次渠道
  239. allMatchedSet := make(map[string]struct{})
  240. for _, names := range matchedNamesByIdx {
  241. for _, n := range names {
  242. allMatchedSet[n] = struct{}{}
  243. }
  244. }
  245. allMatched := make([]string, 0, len(allMatchedSet))
  246. for n := range allMatchedSet {
  247. allMatched = append(allMatched, n)
  248. }
  249. matchedChannelsByModel, _ := model.GetBoundChannelsByModelsMap(allMatched)
  250. // 6) 回填每个规则模型的并集信息
  251. for _, idx := range ruleIndices {
  252. mm := models[idx]
  253. // 端点并集 -> 序列化
  254. if es, ok := endpointSetByIdx[idx]; ok && mm.Endpoints == "" {
  255. eps := make([]constant.EndpointType, 0, len(es))
  256. for et := range es {
  257. eps = append(eps, et)
  258. }
  259. if b, err := json.Marshal(eps); err == nil {
  260. mm.Endpoints = string(b)
  261. }
  262. }
  263. // 分组并集
  264. if gs, ok := groupSetByIdx[idx]; ok {
  265. groups := make([]string, 0, len(gs))
  266. for g := range gs {
  267. groups = append(groups, g)
  268. }
  269. mm.EnableGroups = groups
  270. }
  271. // 配额类型集合(保持去重并排序)
  272. if qs, ok := quotaSetByIdx[idx]; ok {
  273. arr := make([]int, 0, len(qs))
  274. for k := range qs {
  275. arr = append(arr, k)
  276. }
  277. sort.Ints(arr)
  278. mm.QuotaTypes = arr
  279. }
  280. // 渠道并集
  281. names := matchedNamesByIdx[idx]
  282. channelSet := make(map[string]model.BoundChannel)
  283. for _, n := range names {
  284. for _, ch := range matchedChannelsByModel[n] {
  285. key := ch.Name + "_" + strconv.Itoa(ch.Type)
  286. channelSet[key] = ch
  287. }
  288. }
  289. if len(channelSet) > 0 {
  290. chs := make([]model.BoundChannel, 0, len(channelSet))
  291. for _, ch := range channelSet {
  292. chs = append(chs, ch)
  293. }
  294. mm.BoundChannels = chs
  295. }
  296. // 匹配信息
  297. mm.MatchedModels = names
  298. mm.MatchedCount = len(names)
  299. }
  300. }