mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-30 03:00:41 -04:00
feat(platform/admin): enhance cost dashboard with token breakdown and per-request averages
Add deeper cost visibility to the admin platform cost dashboard: - Show prompt vs completion tokens separately in provider table - Add summary cards for avg cost/request, avg input/output tokens, total token split - Add avg cost per request column to the per-user table - Compute aggregate token totals and per-request averages in backend
This commit is contained in:
@@ -167,6 +167,11 @@ class PlatformCostDashboard(BaseModel):
|
||||
total_cost_microdollars: int
|
||||
total_requests: int
|
||||
total_users: int
|
||||
total_input_tokens: int = 0
|
||||
total_output_tokens: int = 0
|
||||
avg_input_tokens_per_request: float = 0.0
|
||||
avg_output_tokens_per_request: float = 0.0
|
||||
avg_cost_microdollars_per_request: float = 0.0
|
||||
|
||||
|
||||
def _si(row: dict, field: str) -> int:
|
||||
@@ -293,7 +298,11 @@ async def get_platform_cost_dashboard(
|
||||
PrismaLog.prisma().group_by(
|
||||
by=["provider"],
|
||||
where=where,
|
||||
sum={"costMicrodollars": True},
|
||||
sum={
|
||||
"costMicrodollars": True,
|
||||
"inputTokens": True,
|
||||
"outputTokens": True,
|
||||
},
|
||||
count=True,
|
||||
),
|
||||
)
|
||||
@@ -322,6 +331,8 @@ async def get_platform_cost_dashboard(
|
||||
# Grand totals — sum across all provider groups (no LIMIT applied above).
|
||||
total_cost = sum(_si(r, "costMicrodollars") for r in total_agg_groups)
|
||||
total_requests = sum(_ca(r) for r in total_agg_groups)
|
||||
total_input_tokens = sum(_si(r, "inputTokens") for r in total_agg_groups)
|
||||
total_output_tokens = sum(_si(r, "outputTokens") for r in total_agg_groups)
|
||||
|
||||
return PlatformCostDashboard(
|
||||
by_provider=[
|
||||
@@ -354,6 +365,17 @@ async def get_platform_cost_dashboard(
|
||||
total_cost_microdollars=total_cost,
|
||||
total_requests=total_requests,
|
||||
total_users=total_users,
|
||||
total_input_tokens=total_input_tokens,
|
||||
total_output_tokens=total_output_tokens,
|
||||
avg_input_tokens_per_request=(
|
||||
total_input_tokens / total_requests if total_requests > 0 else 0.0
|
||||
),
|
||||
avg_output_tokens_per_request=(
|
||||
total_output_tokens / total_requests if total_requests > 0 else 0.0
|
||||
),
|
||||
avg_cost_microdollars_per_request=(
|
||||
total_cost / total_requests if total_requests > 0 else 0.0
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Alert, AlertDescription } from "@/components/molecules/Alert/Alert";
|
||||
import { Skeleton } from "@/components/atoms/Skeleton/Skeleton";
|
||||
import { formatMicrodollars } from "../helpers";
|
||||
import { formatMicrodollars, formatTokens } from "../helpers";
|
||||
import { SummaryCard } from "./SummaryCard";
|
||||
import { ProviderTable } from "./ProviderTable";
|
||||
import { UserTable } from "./UserTable";
|
||||
@@ -215,7 +215,7 @@ export function PlatformCostContent({ searchParams }: Props) {
|
||||
) : (
|
||||
<>
|
||||
{dashboard && (
|
||||
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4">
|
||||
<SummaryCard
|
||||
label="Known Cost"
|
||||
value={formatMicrodollars(dashboard.total_cost_microdollars)}
|
||||
@@ -234,6 +234,36 @@ export function PlatformCostContent({ searchParams }: Props) {
|
||||
label="Active Users"
|
||||
value={dashboard.total_users.toLocaleString()}
|
||||
/>
|
||||
<SummaryCard
|
||||
label="Avg Cost / Request"
|
||||
value={
|
||||
dashboard.avg_cost_microdollars_per_request
|
||||
? formatMicrodollars(
|
||||
dashboard.avg_cost_microdollars_per_request,
|
||||
)
|
||||
: "$0.00"
|
||||
}
|
||||
subtitle="Known cost divided by total requests"
|
||||
/>
|
||||
<SummaryCard
|
||||
label="Avg Input Tokens"
|
||||
value={Math.round(
|
||||
dashboard.avg_input_tokens_per_request ?? 0,
|
||||
).toLocaleString()}
|
||||
subtitle="Prompt tokens per request (context size)"
|
||||
/>
|
||||
<SummaryCard
|
||||
label="Avg Output Tokens"
|
||||
value={Math.round(
|
||||
dashboard.avg_output_tokens_per_request ?? 0,
|
||||
).toLocaleString()}
|
||||
subtitle="Completion tokens per request (response length)"
|
||||
/>
|
||||
<SummaryCard
|
||||
label="Total Tokens"
|
||||
value={`${formatTokens(dashboard.total_input_tokens ?? 0)} in / ${formatTokens(dashboard.total_output_tokens ?? 0)} out`}
|
||||
subtitle="Prompt vs completion token split"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
defaultRateFor,
|
||||
estimateCostForRow,
|
||||
formatMicrodollars,
|
||||
formatTokens,
|
||||
rateKey,
|
||||
rateUnitLabel,
|
||||
trackingValue,
|
||||
@@ -33,6 +34,12 @@ function ProviderTable({ data, rateOverrides, onRateOverride }: Props) {
|
||||
<th scope="col" className="px-4 py-3 text-right">
|
||||
Usage
|
||||
</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">
|
||||
Input Tokens
|
||||
</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">
|
||||
Output Tokens
|
||||
</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">
|
||||
Requests
|
||||
</th>
|
||||
@@ -74,6 +81,16 @@ function ProviderTable({ data, rateOverrides, onRateOverride }: Props) {
|
||||
<TrackingBadge trackingType={row.tracking_type} />
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">{trackingValue(row)}</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
{row.total_input_tokens > 0
|
||||
? formatTokens(row.total_input_tokens)
|
||||
: "-"}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
{row.total_output_tokens > 0
|
||||
? formatTokens(row.total_output_tokens)
|
||||
: "-"}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
{row.request_count.toLocaleString()}
|
||||
</td>
|
||||
@@ -124,7 +141,7 @@ function ProviderTable({ data, rateOverrides, onRateOverride }: Props) {
|
||||
{data.length === 0 && (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={8}
|
||||
colSpan={10}
|
||||
className="px-4 py-8 text-center text-muted-foreground"
|
||||
>
|
||||
No cost data yet
|
||||
|
||||
@@ -26,6 +26,9 @@ function UserTable({ data }: Props) {
|
||||
<th scope="col" className="px-4 py-3 text-right">
|
||||
Output Tokens
|
||||
</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">
|
||||
Avg Cost / Req
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -54,12 +57,21 @@ function UserTable({ data }: Props) {
|
||||
<td className="px-4 py-3 text-right">
|
||||
{formatTokens(row.total_output_tokens)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
{row.request_count > 0 && row.total_cost_microdollars > 0
|
||||
? formatMicrodollars(
|
||||
Math.round(
|
||||
row.total_cost_microdollars / row.request_count,
|
||||
),
|
||||
)
|
||||
: "-"}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{data.length === 0 && (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={5}
|
||||
colSpan={6}
|
||||
className="px-4 py-8 text-center text-muted-foreground"
|
||||
>
|
||||
No cost data yet
|
||||
|
||||
@@ -12141,7 +12141,12 @@
|
||||
"title": "Total Cost Microdollars"
|
||||
},
|
||||
"total_requests": { "type": "integer", "title": "Total Requests" },
|
||||
"total_users": { "type": "integer", "title": "Total Users" }
|
||||
"total_users": { "type": "integer", "title": "Total Users" },
|
||||
"total_input_tokens": { "type": "integer", "title": "Total Input Tokens", "default": 0 },
|
||||
"total_output_tokens": { "type": "integer", "title": "Total Output Tokens", "default": 0 },
|
||||
"avg_input_tokens_per_request": { "type": "number", "title": "Avg Input Tokens Per Request", "default": 0.0 },
|
||||
"avg_output_tokens_per_request": { "type": "number", "title": "Avg Output Tokens Per Request", "default": 0.0 },
|
||||
"avg_cost_microdollars_per_request": { "type": "number", "title": "Avg Cost Microdollars Per Request", "default": 0.0 }
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
Reference in New Issue
Block a user