dashboard.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. package controller
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/gin-gonic/gin"
  8. "github.com/labring/sealos/service/aiproxy/common"
  9. "github.com/labring/sealos/service/aiproxy/common/rpmlimit"
  10. "github.com/labring/sealos/service/aiproxy/middleware"
  11. "github.com/labring/sealos/service/aiproxy/model"
  12. "gorm.io/gorm"
  13. )
  14. func getDashboardTime(t string) (time.Time, time.Time, model.TimeSpanType) {
  15. end := time.Now()
  16. var start time.Time
  17. var timeSpan model.TimeSpanType
  18. switch t {
  19. case "month":
  20. start = end.AddDate(0, 0, -30)
  21. timeSpan = model.TimeSpanDay
  22. case "two_week":
  23. start = end.AddDate(0, 0, -15)
  24. timeSpan = model.TimeSpanDay
  25. case "week":
  26. start = end.AddDate(0, 0, -7)
  27. timeSpan = model.TimeSpanDay
  28. case "day":
  29. fallthrough
  30. default:
  31. start = end.AddDate(0, 0, -1)
  32. timeSpan = model.TimeSpanHour
  33. }
  34. return start, end, timeSpan
  35. }
  36. func fillGaps(data []*model.ChartData, start, end time.Time, t model.TimeSpanType) []*model.ChartData {
  37. if len(data) == 0 {
  38. return data
  39. }
  40. var timeSpan time.Duration
  41. switch t {
  42. case model.TimeSpanDay:
  43. timeSpan = time.Hour * 24
  44. default:
  45. timeSpan = time.Hour
  46. }
  47. // Handle first point
  48. firstPoint := time.Unix(data[0].Timestamp, 0)
  49. firstAlignedTime := firstPoint
  50. for !firstAlignedTime.Add(-timeSpan).Before(start) {
  51. firstAlignedTime = firstAlignedTime.Add(-timeSpan)
  52. }
  53. var firstIsZero bool
  54. if !firstAlignedTime.Equal(firstPoint) {
  55. data = append([]*model.ChartData{
  56. {
  57. Timestamp: firstAlignedTime.Unix(),
  58. },
  59. }, data...)
  60. firstIsZero = true
  61. }
  62. // Handle last point
  63. lastPoint := time.Unix(data[len(data)-1].Timestamp, 0)
  64. lastAlignedTime := lastPoint
  65. for !lastAlignedTime.Add(timeSpan).After(end) {
  66. lastAlignedTime = lastAlignedTime.Add(timeSpan)
  67. }
  68. var lastIsZero bool
  69. if !lastAlignedTime.Equal(lastPoint) {
  70. data = append(data, &model.ChartData{
  71. Timestamp: lastAlignedTime.Unix(),
  72. })
  73. lastIsZero = true
  74. }
  75. result := make([]*model.ChartData, 0, len(data))
  76. result = append(result, data[0])
  77. for i := 1; i < len(data); i++ {
  78. curr := data[i]
  79. prev := data[i-1]
  80. hourDiff := (curr.Timestamp - prev.Timestamp) / int64(timeSpan.Seconds())
  81. // If gap is 1 hour or less, continue
  82. if hourDiff <= 1 {
  83. result = append(result, curr)
  84. continue
  85. }
  86. // If gap is more than 3 hours, only add boundary points
  87. if hourDiff > 3 {
  88. // Add point for hour after prev
  89. if i != 1 || (i == 1 && !firstIsZero) {
  90. result = append(result, &model.ChartData{
  91. Timestamp: prev.Timestamp + int64(timeSpan.Seconds()),
  92. })
  93. }
  94. // Add point for hour before curr
  95. if i != len(data)-1 || (i == len(data)-1 && !lastIsZero) {
  96. result = append(result, &model.ChartData{
  97. Timestamp: curr.Timestamp - int64(timeSpan.Seconds()),
  98. })
  99. }
  100. result = append(result, curr)
  101. continue
  102. }
  103. // Fill gaps of 2-3 hours with zero points
  104. for j := prev.Timestamp + int64(timeSpan.Seconds()); j < curr.Timestamp; j += int64(timeSpan.Seconds()) {
  105. result = append(result, &model.ChartData{
  106. Timestamp: j,
  107. })
  108. }
  109. result = append(result, curr)
  110. }
  111. return result
  112. }
  113. func GetDashboard(c *gin.Context) {
  114. log := middleware.GetLogger(c)
  115. start, end, timeSpan := getDashboardTime(c.Query("type"))
  116. modelName := c.Query("model")
  117. dashboards, err := model.GetDashboardData(start, end, modelName, timeSpan)
  118. if err != nil {
  119. middleware.ErrorResponse(c, http.StatusOK, err.Error())
  120. return
  121. }
  122. dashboards.ChartData = fillGaps(dashboards.ChartData, start, end, timeSpan)
  123. if common.RedisEnabled {
  124. rpm, err := rpmlimit.GetRPM(c.Request.Context(), "", modelName)
  125. if err != nil {
  126. log.Errorf("failed to get rpm: %v", err)
  127. } else {
  128. dashboards.RPM = rpm
  129. }
  130. }
  131. middleware.SuccessResponse(c, dashboards)
  132. }
  133. func GetGroupDashboard(c *gin.Context) {
  134. log := middleware.GetLogger(c)
  135. group := c.Param("group")
  136. if group == "" {
  137. middleware.ErrorResponse(c, http.StatusOK, "invalid parameter")
  138. return
  139. }
  140. start, end, timeSpan := getDashboardTime(c.Query("type"))
  141. tokenName := c.Query("token_name")
  142. modelName := c.Query("model")
  143. dashboards, err := model.GetGroupDashboardData(group, start, end, tokenName, modelName, timeSpan)
  144. if err != nil {
  145. middleware.ErrorResponse(c, http.StatusOK, "failed to get statistics")
  146. return
  147. }
  148. dashboards.ChartData = fillGaps(dashboards.ChartData, start, end, timeSpan)
  149. if common.RedisEnabled && tokenName == "" {
  150. rpm, err := rpmlimit.GetRPM(c.Request.Context(), group, modelName)
  151. if err != nil {
  152. log.Errorf("failed to get rpm: %v", err)
  153. } else {
  154. dashboards.RPM = rpm
  155. }
  156. }
  157. middleware.SuccessResponse(c, dashboards)
  158. }
  159. func GetGroupDashboardModels(c *gin.Context) {
  160. group := c.Param("group")
  161. if group == "" {
  162. middleware.ErrorResponse(c, http.StatusOK, "invalid parameter")
  163. return
  164. }
  165. groupCache, err := model.CacheGetGroup(group)
  166. if err != nil {
  167. if errors.Is(err, gorm.ErrRecordNotFound) {
  168. middleware.SuccessResponse(c, model.LoadModelCaches().EnabledModelConfigs)
  169. } else {
  170. middleware.ErrorResponse(c, http.StatusOK, fmt.Sprintf("failed to get group: %v", err))
  171. }
  172. return
  173. }
  174. enabledModelConfigs := model.LoadModelCaches().EnabledModelConfigs
  175. newEnabledModelConfigs := make([]*model.ModelConfig, len(enabledModelConfigs))
  176. for i, mc := range enabledModelConfigs {
  177. newEnabledModelConfigs[i] = middleware.GetGroupAdjustedModelConfig(groupCache, mc)
  178. }
  179. middleware.SuccessResponse(c, newEnabledModelConfigs)
  180. }
  181. func GetModelCostRank(c *gin.Context) {
  182. startTime, endTime := parseTimeRange(c)
  183. models, err := model.GetModelCostRank("", startTime, endTime)
  184. if err != nil {
  185. middleware.ErrorResponse(c, http.StatusOK, err.Error())
  186. return
  187. }
  188. middleware.SuccessResponse(c, models)
  189. }
  190. func GetGroupModelCostRank(c *gin.Context) {
  191. group := c.Param("group")
  192. if group == "" {
  193. middleware.ErrorResponse(c, http.StatusOK, "group is required")
  194. return
  195. }
  196. startTime, endTime := parseTimeRange(c)
  197. models, err := model.GetModelCostRank(group, startTime, endTime)
  198. if err != nil {
  199. middleware.ErrorResponse(c, http.StatusOK, err.Error())
  200. return
  201. }
  202. middleware.SuccessResponse(c, models)
  203. }