diff --git a/internal/api/log_handler.go b/internal/api/log_handler.go index 592f4c6..6aec9d9 100644 --- a/internal/api/log_handler.go +++ b/internal/api/log_handler.go @@ -568,8 +568,8 @@ func (h *Handler) logStatsByMinute(c *gin.Context, q *gorm.DB, sinceTime, untilT duration := untilTime.Sub(*sinceTime) if duration > maxMinuteRangeDuration { c.JSON(http.StatusBadRequest, gin.H{ - "error": "time range too large for minute-level aggregation", - "max_hours": 6, + "error": "time range too large for minute-level aggregation", + "max_hours": 6, "actual_hours": duration.Hours(), }) return @@ -618,9 +618,10 @@ type TrafficMetrics struct { // TrafficBucket represents one time bucket with model breakdown type TrafficBucket struct { - Time string `json:"time"` - Timestamp int64 `json:"timestamp"` - Breakdown map[string]TrafficMetrics `json:"breakdown"` + Time string `json:"time"` + Timestamp int64 `json:"timestamp"` + // Breakdown contains per-model metrics, keyed by model name (e.g. "gpt-4", "claude-3-opus", "other") + Breakdown map[string]TrafficMetrics `json:"breakdown" swaggertype:"object,object" example:"{\"gpt-4\":{\"count\":10,\"tokens_in\":1000,\"tokens_out\":500},\"claude-3-opus\":{\"count\":5,\"tokens_in\":800,\"tokens_out\":400},\"other\":{\"count\":3,\"tokens_in\":200,\"tokens_out\":100}}"` Total TrafficMetrics `json:"total"` } @@ -734,7 +735,7 @@ func (h *Handler) GetTrafficChart(c *gin.Context) { } var rows []bucketModelStats - if err := q.Select(truncFunc+" as bucket, model_name, COUNT(*) as cnt, COALESCE(SUM(tokens_in),0) as tokens_in, COALESCE(SUM(tokens_out),0) as tokens_out"). + if err := q.Select(truncFunc + " as bucket, model_name, COUNT(*) as cnt, COALESCE(SUM(tokens_in),0) as tokens_in, COALESCE(SUM(tokens_out),0) as tokens_out"). Group("bucket, model_name"). Order("bucket ASC, cnt DESC"). Scan(&rows).Error; err != nil {