package cli import ( "encoding/json" "fmt" "text/tabwriter" "time" "github.com/spf13/cobra" "gitea.mynodes.xyz/ilker/llmgw/internal/control/adminapi" ) // newUsageCmd shows what each group of the tenancy tree spent. // // Quotas could be written on an organization, a team or a project long before // anything could show what any of them had used: usage came back per key, per // user and per model only. A quota whose consumption cannot be seen is one // nobody reviews until the day it refuses somebody. func newUsageCmd(client func() *ctlClient, asJSON *bool) *cobra.Command { var by string var since time.Duration c := &cobra.Command{ Use: "usage", Short: "What each organization, team or project has spent", Long: "Grouped by a level of the tenancy tree, which is the level a quota is\n" + "written on. Spend is summed across every key beneath it, so two people\n" + "sharing a team budget appear as one number — the number that will refuse\n" + "them.\n\n" + "The budget column is what that level sets, and is blank when it sets none.\n" + "A team bounded only by its organization has no budget of its own, and\n" + "printing the organization's beside each of its teams would read as though\n" + "each had that much to spend.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { var rows []adminapi.ScopeUsageView path := fmt.Sprintf("/api/v1/usage?by=%s&since=%s", by, since) if err := client().get(cmd.Context(), path, &rows); err != nil { return err } if *asJSON { return json.NewEncoder(cmd.OutOrStdout()).Encode(rows) } if len(rows) == 0 { fmt.Fprintf(cmd.OutOrStdout(), "No %s used anything in the last %s.\n", by, since) return nil } w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 0, 2, ' ', 0) fmt.Fprintln(w, "NAME\tID\tKEYS\tREQUESTS\tERRORS\tTOKENS\tSPEND\tBUDGET\tUSED\tLAST") for _, u := range rows { budget, used := "—", "—" if u.Budget != nil { budget = fmt.Sprintf("%.2f", float64(*u.Budget)/1e6) if u.BudgetPeriod != "" { budget += "/" + u.BudgetPeriod } } if u.Fraction != nil { used = fmt.Sprintf("%.0f%%", *u.Fraction*100) } fmt.Fprintf(w, "%s\t%s\t%d\t%d\t%d\t%d\t%.2f\t%s\t%s\t%s\n", u.Name, u.ID, u.Keys, u.Requests, u.Errors, u.Prompt+u.Completion, float64(u.CostMicros)/1e6, budget, used, age(u.LastAt)) } return w.Flush() }, } c.Flags().StringVar(&by, "by", "team", "level to group by: org, team or project") c.Flags().DurationVar(&since, "since", 24*time.Hour, "how far back to total") return c }