diff --git a/app/main.py b/app/main.py index e5ccea5..050d580 100644 --- a/app/main.py +++ b/app/main.py @@ -1883,12 +1883,38 @@ async def admin_overview(request: Request): except Exception as e: logger.warning("admin overview: team/list failed: %s", e) + # All-time spend per member team, from SpendLogs (the team `spend` counter + # is cycle-only and resets at each member's budget_reset_at — the panel + # needs both to avoid the "month change wiped the numbers" confusion). + all_time_spend: dict[str, float] = {} + try: + conn = _pg_connect() + try: + with conn.cursor() as cur: + cur.execute( + """ + SELECT t.team_alias, COALESCE(SUM(s.spend), 0) + FROM "LiteLLM_TeamTable" t + LEFT JOIN "LiteLLM_SpendLogs" s ON s."team_id" = t.team_id + WHERE t.team_alias LIKE 'member:%' + GROUP BY 1 + """ + ) + all_time_spend = {alias: float(v or 0) for alias, v in cur.fetchall()} + finally: + conn.close() + except Exception as e: + logger.warning("admin overview: all-time spend query failed: %s", e) + members = [] total_spend = 0.0 + total_all_time = 0.0 for email, active, balance, user_id in rows: team = teams.get(f"member:{email}", {}) spend = float(team.get("spend") or 0.0) total_spend += spend + member_all_time = all_time_spend.get(f"member:{email}", 0.0) + total_all_time += member_all_time credits = get_credit_balance(email) eff = compute_max_budget( float(balance) if balance is not None else MEMBER_BUDGET, @@ -1898,6 +1924,7 @@ async def admin_overview(request: Request): members.append( { "email": email, + "all_time_spend": round(member_all_time, 2), "username": usernames.get(user_id), "activated": invite_accepted.get(user_id, False), "active": bool(active), @@ -1918,7 +1945,8 @@ async def admin_overview(request: Request): "totals": { "members": len(members), "active": sum(1 for m in members if m["active"]), - "total_spend": total_spend, + "total_spend": round(total_spend, 2), + "total_all_time_spend": round(total_all_time, 2), }, }