fix: restore analytics page functionality
Implemented missing /api/usage/detailed endpoint and ensured analytics breakdown and time-series return data in the expected format.
This commit is contained in:
@@ -329,8 +329,15 @@ func (s *Server) handleTimeSeries(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add granularity for the UI
|
||||||
|
granularity := "day"
|
||||||
|
if filter.Period == "24h" || filter.Period == "today" {
|
||||||
|
// If we had hourly bucketing we'd use 'hour'
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, SuccessResponse(gin.H{
|
c.JSON(http.StatusOK, SuccessResponse(gin.H{
|
||||||
"series": series,
|
"series": series,
|
||||||
|
"granularity": granularity,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,6 +401,52 @@ func (s *Server) handleAnalyticsBreakdown(c *gin.Context) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleDetailedUsage(c *gin.Context) {
|
||||||
|
var filter UsagePeriodFilter
|
||||||
|
if err := c.ShouldBindQuery(&filter); err != nil {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
clause, binds := filter.ToSQL()
|
||||||
|
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
SELECT
|
||||||
|
strftime('%%Y-%%m-%%d', timestamp) as date,
|
||||||
|
client_id as client,
|
||||||
|
provider,
|
||||||
|
model,
|
||||||
|
COUNT(*) as requests,
|
||||||
|
SUM(total_tokens) as tokens,
|
||||||
|
SUM(cache_read_tokens) as cache_read_tokens,
|
||||||
|
SUM(cache_write_tokens) as cache_write_tokens,
|
||||||
|
SUM(cost) as cost
|
||||||
|
FROM llm_requests
|
||||||
|
WHERE 1=1 %s
|
||||||
|
GROUP BY date, client, provider, model
|
||||||
|
ORDER BY date DESC, cost DESC
|
||||||
|
`, clause)
|
||||||
|
|
||||||
|
var rows []struct {
|
||||||
|
Date string `db:"date" json:"date"`
|
||||||
|
Client string `db:"client" json:"client"`
|
||||||
|
Provider string `db:"provider" json:"provider"`
|
||||||
|
Model string `db:"model" json:"model"`
|
||||||
|
Requests int `db:"requests" json:"requests"`
|
||||||
|
Tokens int `db:"tokens" json:"tokens"`
|
||||||
|
CacheReadTokens int `db:"cache_read_tokens" json:"cache_read_tokens"`
|
||||||
|
CacheWriteTokens int `db:"cache_write_tokens" json:"cache_write_tokens"`
|
||||||
|
Cost float64 `db:"cost" json:"cost"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.database.Select(&rows, query, binds...)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, SuccessResponse(rows))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) handleGetClients(c *gin.Context) {
|
func (s *Server) handleGetClients(c *gin.Context) {
|
||||||
var clients []db.Client
|
var clients []db.Client
|
||||||
err := s.database.Select(&clients, "SELECT * FROM clients ORDER BY created_at DESC")
|
err := s.database.Select(&clients, "SELECT * FROM clients ORDER BY created_at DESC")
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ func (s *Server) setupRoutes() {
|
|||||||
admin.GET("/usage/summary", s.handleUsageSummary)
|
admin.GET("/usage/summary", s.handleUsageSummary)
|
||||||
admin.GET("/usage/time-series", s.handleTimeSeries)
|
admin.GET("/usage/time-series", s.handleTimeSeries)
|
||||||
admin.GET("/usage/providers", s.handleProvidersUsage)
|
admin.GET("/usage/providers", s.handleProvidersUsage)
|
||||||
|
admin.GET("/usage/detailed", s.handleDetailedUsage)
|
||||||
admin.GET("/analytics/breakdown", s.handleAnalyticsBreakdown)
|
admin.GET("/analytics/breakdown", s.handleAnalyticsBreakdown)
|
||||||
|
|
||||||
admin.GET("/clients", s.handleGetClients)
|
admin.GET("/clients", s.handleGetClients)
|
||||||
|
|||||||
Reference in New Issue
Block a user