metrics: break out the co-op's own (operator) spend in the daily series

metrics_daily.spend_usd is the co-op-wide total and deliberately spans every
team, operators included — the numbers were already right. What was missing
was a way to tell how much of that total is the co-op's OWN usage, so a
member-facing chart could not be misread as member-only activity.

Adds metrics_daily.operator_spend_usd: the per-day portion of spend_usd that
belongs to the accounts in OPERATOR_EMAILS (teams aliased member:<email>, like
anyone else). NULL = not measured (never a confident 0), mirroring spend_usd.

- metrics_snapshot.py: operator_spend_by_day() + write it on snapshot/backfill
- app/main.py: expose it in metrics_series (so /broker/metrics and
  /admin/metrics/series carry it), and add operator_spend /
  operator_all_time_spend to /admin/overview totals as the explicit breakout
  of total_spend / total_all_time_spend.

spend_usd and every existing column are untouched.
This commit is contained in:
inference-bot committed 2026-11-02 15:42:30 +00:00
1 parent ddeefdebe0
commit 5f8bed33e6
2 files changed
+99 -3

No files matched your search

+17 -1
View File
@@ -694,6 +694,7 @@ def get_db() -> sqlite3.Connection:
"newsletter_subs INTEGER, "
"matrix_space_members INTEGER, "
"spend_usd REAL, "
"operator_spend_usd REAL, "
"requests INTEGER, "
"out_tokens INTEGER, "
"energy_wh REAL, "
@@ -709,6 +710,11 @@ def get_db() -> sqlite3.Connection:
# measured-vs-estimated provenance columns after those. All additive: existing
# rows keep NULL, and a NULL split reports as unknown — never as "measured".
for _col, _decl in (
# The co-op's OWN accounts' share of spend_usd (OPERATOR_EMAILS). The
# total keeps spanning every team; this is the breakout, so a
# member-facing surface can say how much of it is the co-op's own
# usage. NULL = not measured (never a confident 0).
("operator_spend_usd", "REAL"),
("out_tokens", "INTEGER"),
("energy_wh", "REAL"),
("co2e_g", "REAL"),
@@ -3097,6 +3103,8 @@ async def admin_overview(request: Request):
admins = []
total_spend = 0.0
total_all_time = 0.0
operator_spend = 0.0
operator_all_time = 0.0
for email, active, balance, user_id, org_slug, matrix_mxid in rows:
team = teams.get(f"member:{email}", {})
spend = float(team.get("spend") or 0.0)
@@ -3140,6 +3148,8 @@ async def admin_overview(request: Request):
admins.append(entry)
total_spend += spend
total_all_time += member_all_time
operator_spend += spend
operator_all_time += member_all_time
else:
members.append(entry)
total_spend += spend
@@ -3177,8 +3187,13 @@ async def admin_overview(request: Request):
"totals": {
"members": len(members),
"active": sum(1 for m in members if m["active"]),
# total_spend spans members AND the co-op's own operator accounts.
# operator_spend is the breakout of that portion, so a consumer
# never has to guess whether a figure includes the co-op's own use.
"total_spend": round(total_spend, 2),
"total_all_time_spend": round(total_all_time, 2),
"operator_spend": round(operator_spend, 2),
"operator_all_time_spend": round(operator_all_time, 2),
},
}
@@ -3232,7 +3247,7 @@ def metrics_series(days: int = 90) -> list[dict]:
try:
rows = conn.execute(
"SELECT day, members_active, members_total, members_new, newsletter_subs, "
"matrix_space_members, spend_usd, requests, out_tokens, energy_wh, co2e_g, "
"matrix_space_members, spend_usd, operator_spend_usd, requests, out_tokens, energy_wh, co2e_g, "
"tokens_measured, tokens_estimated, factor_revision, recorded_at "
"FROM metrics_daily ORDER BY day DESC LIMIT ?",
(limit,),
@@ -3247,6 +3262,7 @@ def metrics_series(days: int = 90) -> list[dict]:
"newsletter_subs",
"matrix_space_members",
"spend_usd",
"operator_spend_usd",
"requests",
"out_tokens",
"energy_wh",