package server import ( "database/sql" "net/http" "strconv" "time" ) type artifactUsageRow struct { CreatedAt time.Time `json:"created_at"` TaskID string `json:"task_id,omitempty"` Kind string `json:"kind"` Model string `json:"model"` Endpoint string `json:"endpoint"` Size string `json:"size"` Quality string `json:"quality"` RequestID string `json:"request_id,omitempty"` InputTokens int64 `json:"input_tokens"` InputTextTokens int64 `json:"input_text_tokens"` InputImageTokens int64 `json:"input_image_tokens"` OutputTokens int64 `json:"output_tokens"` TotalTokens int64 `json:"total_tokens"` EstimatedCostUSD *float64 `json:"estimated_cost_usd,omitempty"` PricingBasis string `json:"pricing_basis,omitempty"` } func (s *Server) adminArtifactUsage(w http.ResponseWriter, r *http.Request) { now := time.Now().UTC() dayStartMS := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC).UnixMilli() if raw := r.URL.Query().Get("day_start_ms"); raw != "" { if v, err := strconv.ParseInt(raw, 10, 64); err == nil { // Accept a browser-local midnight in a reasonable window. This lets the // "today" KPI follow the admin's local calendar day without adding a // timezone setting to the server. candidate := time.UnixMilli(v).UTC() if candidate.After(now.Add(-48*time.Hour)) && candidate.Before(now.Add(24*time.Hour)) { dayStartMS = v } } } var todayCalls, todayCards, todayPriced int64 var todayCost float64 _ = s.store.DB.QueryRowContext(r.Context(), `SELECT count(*), COALESCE(sum(CASE WHEN kind='artifact' THEN 1 ELSE 0 END),0), COALESCE(sum(CASE WHEN estimated_cost_usd IS NOT NULL THEN 1 ELSE 0 END),0), COALESCE(sum(estimated_cost_usd),0) FROM artifact_api_usage WHERE created_at>=?`, dayStartMS).Scan(&todayCalls, &todayCards, &todayPriced, &todayCost) var cardCalls, pricedCardCalls int64 var totalCardCost, avgCardCost float64 var inputTokens, inputTextTokens, inputImageTokens, outputTokens, totalTokens int64 _ = s.store.DB.QueryRowContext(r.Context(), `SELECT count(*),count(estimated_cost_usd), COALESCE(sum(estimated_cost_usd),0),COALESCE(avg(estimated_cost_usd),0), COALESCE(sum(input_tokens),0),COALESCE(sum(input_text_tokens),0),COALESCE(sum(input_image_tokens),0), COALESCE(sum(output_tokens),0),COALESCE(sum(total_tokens),0) FROM artifact_api_usage WHERE kind='artifact'`).Scan( &cardCalls, &pricedCardCalls, &totalCardCost, &avgCardCost, &inputTokens, &inputTextTokens, &inputImageTokens, &outputTokens, &totalTokens) var anchorCalls int64 var anchorCost float64 _ = s.store.DB.QueryRowContext(r.Context(), `SELECT count(*),COALESCE(sum(estimated_cost_usd),0) FROM artifact_api_usage WHERE kind='character_anchor'`).Scan(&anchorCalls, &anchorCost) var rollingCalls1H, rollingCalls24H int64 var rollingCost24H float64 oneHourMS := now.Add(-time.Hour).UnixMilli() twentyFourHoursMS := now.Add(-24 * time.Hour).UnixMilli() _ = s.store.DB.QueryRowContext(r.Context(), `SELECT COALESCE(sum(CASE WHEN created_at>=? THEN 1 ELSE 0 END),0), count(*),COALESCE(sum(estimated_cost_usd),0) FROM artifact_api_usage WHERE provider='openai' AND created_at>=?`, oneHourMS, twentyFourHoursMS).Scan(&rollingCalls1H, &rollingCalls24H, &rollingCost24H) cfg := s.settings.Get() circuitBlocked := (cfg.OpenAIMaxCalls1H > 0 && rollingCalls1H >= int64(cfg.OpenAIMaxCalls1H)) || (cfg.OpenAIMaxCalls24H > 0 && rollingCalls24H >= int64(cfg.OpenAIMaxCalls24H)) || (cfg.OpenAIMaxCost24HUSD > 0 && rollingCost24H+cfg.OpenAIBudgetReserveUSD > cfg.OpenAIMaxCost24HUSD) recent := make([]artifactUsageRow, 0, 20) rows, err := s.store.DB.QueryContext(r.Context(), `SELECT created_at,task_id,kind,model,endpoint,size,quality,request_id, input_tokens,input_text_tokens,input_image_tokens,output_tokens,total_tokens,estimated_cost_usd,pricing_basis FROM artifact_api_usage ORDER BY created_at DESC,id DESC LIMIT 20`) if err == nil { defer rows.Close() for rows.Next() { var createdMS int64 var taskID, requestID sql.NullString var cost sql.NullFloat64 var row artifactUsageRow if err := rows.Scan(&createdMS, &taskID, &row.Kind, &row.Model, &row.Endpoint, &row.Size, &row.Quality, &requestID, &row.InputTokens, &row.InputTextTokens, &row.InputImageTokens, &row.OutputTokens, &row.TotalTokens, &cost, &row.PricingBasis); err != nil { continue } row.CreatedAt = time.UnixMilli(createdMS).UTC() if taskID.Valid { row.TaskID = taskID.String } if requestID.Valid { row.RequestID = requestID.String } if cost.Valid { v := cost.Float64 row.EstimatedCostUSD = &v } recent = append(recent, row) } } jsonOut(w, 200, map[string]any{ "day_start": time.UnixMilli(dayStartMS).UTC(), "today_calls": todayCalls, "today_cards": todayCards, "today_priced_calls": todayPriced, "today_cost_usd": todayCost, "card_generations": cardCalls, "priced_card_generations": pricedCardCalls, "total_card_cost_usd": totalCardCost, "avg_card_cost_usd": avgCardCost, "cost_per_1000_usd": avgCardCost * 1000, "anchor_generations": anchorCalls, "anchor_cost_usd": anchorCost, "card_usage": map[string]int64{ "input_tokens": inputTokens, "input_text_tokens": inputTextTokens, "input_image_tokens": inputImageTokens, "output_tokens": outputTokens, "total_tokens": totalTokens, }, "circuit_breaker": map[string]any{ "blocked": circuitBlocked, "calls_1h": rollingCalls1H, "calls_24h": rollingCalls24H, "cost_24h_usd": rollingCost24H, "max_calls_1h": cfg.OpenAIMaxCalls1H, "max_calls_24h": cfg.OpenAIMaxCalls24H, "max_cost_24h_usd": cfg.OpenAIMaxCost24HUSD, "reserve_per_call_usd": cfg.OpenAIBudgetReserveUSD, }, "cost_method": "estimated from provider-reported token usage using pinned OpenAI standard public token rates; not invoice reconciliation", "recent": recent, }) }