Spaces:
Running
Running
| package api | |
| import ( | |
| "net/http" | |
| "time" | |
| "cpa-usage-keeper/internal/redact" | |
| "cpa-usage-keeper/internal/service" | |
| "github.com/gin-gonic/gin" | |
| ) | |
| type usageAnalysisResponse struct { | |
| APIs []usageAnalysisAPIPayload `json:"apis"` | |
| Models []usageAnalysisModelPayload `json:"models"` | |
| } | |
| type usageAnalysisAPIPayload struct { | |
| APIKey string `json:"api_key"` | |
| DisplayName string `json:"display_name"` | |
| TotalRequests int64 `json:"total_requests"` | |
| SuccessCount int64 `json:"success_count"` | |
| FailureCount int64 `json:"failure_count"` | |
| InputTokens int64 `json:"input_tokens"` | |
| OutputTokens int64 `json:"output_tokens"` | |
| ReasoningTokens int64 `json:"reasoning_tokens"` | |
| CachedTokens int64 `json:"cached_tokens"` | |
| TotalTokens int64 `json:"total_tokens"` | |
| Models []usageAnalysisModelPayload `json:"models"` | |
| } | |
| type usageAnalysisModelPayload struct { | |
| Model string `json:"model"` | |
| TotalRequests int64 `json:"total_requests"` | |
| SuccessCount int64 `json:"success_count"` | |
| FailureCount int64 `json:"failure_count"` | |
| InputTokens int64 `json:"input_tokens"` | |
| OutputTokens int64 `json:"output_tokens"` | |
| ReasoningTokens int64 `json:"reasoning_tokens"` | |
| CachedTokens int64 `json:"cached_tokens"` | |
| TotalTokens int64 `json:"total_tokens"` | |
| TotalLatencyMS int64 `json:"total_latency_ms"` | |
| LatencySampleCount int64 `json:"latency_sample_count"` | |
| } | |
| func registerUsageAnalysisRoute(router gin.IRoutes, usageProvider service.UsageProvider) { | |
| router.GET("/usage/analysis", func(c *gin.Context) { | |
| if usageProvider == nil { | |
| c.JSON(http.StatusOK, usageAnalysisResponse{APIs: []usageAnalysisAPIPayload{}, Models: []usageAnalysisModelPayload{}}) | |
| return | |
| } | |
| filter, err := parseUsageFilterQuery(c.Request, time.Now().UTC()) | |
| if err != nil { | |
| c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| analysis, err := usageProvider.GetUsageAnalysis(c.Request.Context(), filter) | |
| if err != nil { | |
| writeInternalError(c, "get usage analysis failed", err) | |
| return | |
| } | |
| c.JSON(http.StatusOK, buildUsageAnalysisPayload(analysis)) | |
| }) | |
| } | |
| func buildUsageAnalysisPayload(snapshot *service.UsageAnalysisSnapshot) usageAnalysisResponse { | |
| if snapshot == nil { | |
| return usageAnalysisResponse{APIs: []usageAnalysisAPIPayload{}, Models: []usageAnalysisModelPayload{}} | |
| } | |
| apis := make([]usageAnalysisAPIPayload, 0, len(snapshot.APIs)) | |
| for _, api := range snapshot.APIs { | |
| models := make([]usageAnalysisModelPayload, 0, len(api.Models)) | |
| for _, model := range api.Models { | |
| models = append(models, usageAnalysisModelPayload{ | |
| Model: model.Model, | |
| TotalRequests: model.TotalRequests, | |
| SuccessCount: model.SuccessCount, | |
| FailureCount: model.FailureCount, | |
| InputTokens: model.InputTokens, | |
| OutputTokens: model.OutputTokens, | |
| ReasoningTokens: model.ReasoningTokens, | |
| CachedTokens: model.CachedTokens, | |
| TotalTokens: model.TotalTokens, | |
| TotalLatencyMS: model.TotalLatencyMS, | |
| LatencySampleCount: model.LatencySampleCount, | |
| }) | |
| } | |
| apiKey := redact.APIAlias(api.APIKey) | |
| displayName := redact.APIKeyDisplayName(api.APIKey) | |
| apis = append(apis, usageAnalysisAPIPayload{ | |
| APIKey: apiKey, | |
| DisplayName: displayName, | |
| TotalRequests: api.TotalRequests, | |
| SuccessCount: api.SuccessCount, | |
| FailureCount: api.FailureCount, | |
| InputTokens: api.InputTokens, | |
| OutputTokens: api.OutputTokens, | |
| ReasoningTokens: api.ReasoningTokens, | |
| CachedTokens: api.CachedTokens, | |
| TotalTokens: api.TotalTokens, | |
| Models: models, | |
| }) | |
| } | |
| models := make([]usageAnalysisModelPayload, 0, len(snapshot.Models)) | |
| for _, model := range snapshot.Models { | |
| models = append(models, usageAnalysisModelPayload{ | |
| Model: model.Model, | |
| TotalRequests: model.TotalRequests, | |
| SuccessCount: model.SuccessCount, | |
| FailureCount: model.FailureCount, | |
| InputTokens: model.InputTokens, | |
| OutputTokens: model.OutputTokens, | |
| ReasoningTokens: model.ReasoningTokens, | |
| CachedTokens: model.CachedTokens, | |
| TotalTokens: model.TotalTokens, | |
| TotalLatencyMS: model.TotalLatencyMS, | |
| LatencySampleCount: model.LatencySampleCount, | |
| }) | |
| } | |
| return usageAnalysisResponse{APIs: apis, Models: models} | |
| } | |