Skip to content

Teach org billing usage to stop panicking about money#21

Merged
cretz merged 2 commits into
basetenlabs:mainfrom
quinnypig:org-billing-usage
Jun 23, 2026
Merged

Teach org billing usage to stop panicking about money#21
cretz merged 2 commits into
basetenlabs:mainfrom
quinnypig:org-billing-usage

Conversation

@quinnypig

Copy link
Copy Markdown
Contributor

What this does

baseten org billing usage previously responded to being run by panicking
and printing a goroutine stack trace. As a billing command this had the
twin virtues of being honest about the codebase's feelings toward finance
and not working at all. It was a panic("TODO: implement org billing usage") shipped to users.

It now returns your usage. From the management API. In a table.

How it behaves

  • --since (default 7d, capped at 31d) for a sliding window, or
    --start/--end for an explicit ISO 8601 range. Mutually exclusive,
    because "the last 7 days, but also specifically May 1st through 8th" is
    not a window. Won't go back past 2026-01-01, where the data stops anyway.
  • A per-category table (dedicated, model APIs, training), MINUTES · TOTAL · CREDITS · SUBTOTAL, with an All total row that declines to appear
    when there's only one category to total.
  • --output json for the raw thing, --jq for when you only wanted one
    number anyway.

The part you'll want to argue about

The API encodes money as a union of "number or string." Not one.
Either. Per field. In the wild it returns "0.00"—a string—for a dollar
amount. Rather than pick a side and get paged when the other shows up, the
parser eats both. I've made my peace with it; you may need a minute.

Flag types went from string to time.Duration/time.Time. This makes
billing consistent with model deployment metrics and lets the framework do
the duration and ISO-8601 parsing it already knows. The old string version
was the outlier reinventing the wheel. It does change OrgBillingUsageFlags,
so: flagged, on purpose, in writing.

Diligence, performed

Build, vet, and the full suite pass; lint is clean on the changed files. I
ran it against the live API for the default window, an explicit range,
--jq, --output json, and every validation error, and confirmed the
string-encoded-money branch—the one most likely to detonate later—works on
real responses.

For the #13 author

--since is now a time.Duration, so the docs-schema golden will need a
regen. The new shape already matches what logs and metrics emit, so
billing finally stops being the weird one.

For its entire existence, `baseten org billing usage` has answered the
question "how much am I spending" by panicking and dumping a goroutine
stack trace at whoever asked. It was a literal
`panic("TODO: implement org billing usage")`—the most honest a stub has
ever been, and completely unshippable.

It now asks the management API how much you owe and prints it in a table.
Users, I'm told, prefer this to the stack trace.

The parts worth knowing:
- Every cost field comes back as either a JSON number or a JSON string,
  seemingly per the API's mood. In production you get "0.00"—a string—for
  an amount of money. I've stopped asking why; the parser accepts both and
  gets on with its day.
- Money and minutes now carry thousands separators, because a billing tool
  that renders a real invoice as $1234567.89 is just a confession.
- --since (default 7d, max 31d) or --start/--end, never both. Asking for a
  relative and an absolute window at once isn't a query, it's a koan.
- Flag types went from string to time.Duration/time.Time so the window
  parsing matches `model deployment metrics` instead of hand-rolling date
  math out of spite.

Tested against the live API, where it confirmed I owe nothing—the first
good news this command has delivered in its life.
Copilot AI review requested due to automatic review settings June 23, 2026 04:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements baseten org billing usage by replacing the previous panic("TODO") stub with a real Management API call that validates time windows and renders either JSON (--output json / --jq) or a human-readable usage table.

Changes:

  • Add window parsing/validation for --since (default 7d, max 31d) vs --start/--end (ISO-8601), including an earliest-available cutoff.
  • Fetch /v1/billing/usage_summary from the Management API and render usage as JSON or as a per-category table with an optional “All” total row.
  • Add thorough command tests and extend the management API test harness to capture query parameters.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
internal/cmd/command.org.go Implements billing usage command logic, API call, table rendering, and money parsing helpers.
internal/cmd/command.org_billing_test.go Adds integration-style tests covering table output, totals arithmetic, JSON output, and flag validation.
internal/cmd/command_test.go Extends mock API call recording to include query parameters for assertions.
cmd/command.org.go Updates the declarative command definition, output schema, examples, and flag types to time.Duration/time.Time.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/cmd/command.org.go
Comment thread internal/cmd/command.org.go
Comment thread cmd/command.org.go Outdated
Two fixes from PR review:
- If a category's cost won't parse, the "All" row no longer prints a
  confident sum that quietly omits it. The affected column shows "-"
  instead, because a total that silently understates is worse than no
  total. (Adding 0 was never an undercount, but the displayed sum was
  still a lie of omission.)
- The 2026-01-01 floor is a UTC instant while bare --start/--end parse as
  local, so the cutoff error and the help text now say so out loud.

@cretz cretz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, thanks! I am not marking approved yet because I have to setup CLA on this repo and get you to click the button. Give me a bit to set that up, thanks.

EDIT: Ok, I have setup CLA. There is a comment below with a CLA link badge. If you could click through CLA to sign, I can merge this. Thanks again!

// billingMinutes renders an optional minutes count with thousands separators,
// using "-" for categories (e.g. model APIs) that are billed by tokens rather
// than time.
func billingMinutes(minutes *int) string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these tiny helpers could be inlined, but not a big deal since they are reasonably qualified

@CLAassistant

CLAassistant commented Jun 23, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@cretz cretz merged commit 9bcd28d into basetenlabs:main Jun 23, 2026
4 checks passed
@quinnypig quinnypig deleted the org-billing-usage branch June 24, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants