@@ -0,0 +1,120 @@
|
||||
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)
|
||||
|
||||
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,
|
||||
},
|
||||
"cost_method": "estimated from provider-reported token usage using pinned OpenAI standard public token rates; not invoice reconciliation",
|
||||
"recent": recent,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user