import { IconLoader2, IconRefresh } from "@tabler/icons-react"; import { useEffect, useState } from "react"; import { agentNativePath } from "../api-path.js"; interface UsageBucket { key: string; cents: number; cost: UsageCostAggregate; calls: number; inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheWriteTokens: number; } interface DailyBucket { date: string; cents: number; cost: UsageCostAggregate; calls: number; } interface UsageRecentEntry { id: number; createdAt: number; label: string; app: string; model: string; inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheWriteTokens: number; cents: number; costSource: "reported" | "estimated" | "unavailable"; } type UsageCostAggregate = | { status: "known"; knownCents: number; unavailableCalls: 0 } | { status: "partial"; knownCents: number; unavailableCalls: number } | { status: "unavailable"; knownCents: 0; unavailableCalls: number }; interface UsageBillingMode { unit: "usd" | "builder-credits"; label: string; shortLabel: string; source: "estimated-provider-cost" | "builder-agent-credits"; hardCostMarginMultiplier?: number; creditsPerUsd?: number; } interface UsageSummary { billing?: UsageBillingMode; totalCents: number; totalCost: UsageCostAggregate; totalCalls: number; totalInputTokens: number; totalOutputTokens: number; totalCacheReadTokens: number; totalCacheWriteTokens: number; sinceMs: number; byLabel: UsageBucket[]; byModel: UsageBucket[]; byApp: UsageBucket[]; byDay: DailyBucket[]; recent: UsageRecentEntry[]; } const RANGES = [ { value: 1, label: "24h" }, { value: 7, label: "7d" }, { value: 30, label: "30d" }, { value: 90, label: "90d" }, ]; const USD_BILLING: UsageBillingMode = { unit: "usd", label: "Estimated spend", shortLabel: "Cost", source: "estimated-provider-cost", }; function displayAmountFromCostCents( cents: number, billing: UsageBillingMode, ): number { if (billing.unit !== "builder-credits") return cents; const margin = billing.hardCostMarginMultiplier ?? 1.25; const creditsPerUsd = billing.creditsPerUsd ?? 20; const credits = (cents / 100) * margin * creditsPerUsd; return credits <= 0 ? 0 : Math.ceil(credits * 1000) / 1000; } function formatCredits(credits: number): string { if (!Number.isFinite(credits) || credits === 0) return "0 credits"; const maximumFractionDigits = credits < 1 ? 3 : credits < 10 ? 2 : 1; const value = credits.toLocaleString(undefined, { maximumFractionDigits, }); return `${value} ${credits === 1 ? "credit" : "credits"}`; } function formatSpend(cents: number, billing: UsageBillingMode): string { if (billing.unit === "builder-credits") { return formatCredits(displayAmountFromCostCents(cents, billing)); } // Sub-cent values (e.g. a single LLM call at $0.0045 = 0.45¢) — keep // three decimals so tiny calls don't round to 0.00¢. The prior impl // multiplied by 100 in this branch, overstating small costs 100×. if (cents < 1) return `${cents.toFixed(3)}¢`; if (cents < 100) return `${cents.toFixed(2)}¢`; return `$${(cents / 100).toFixed(2)}`; } function formatAggregateCost( cost: UsageCostAggregate, billing: UsageBillingMode, ): string { if (cost.status === "known") return formatSpend(cost.knownCents, billing); if (cost.status === "unavailable") return "Unknown"; return `${formatSpend(cost.knownCents, billing)} + ${cost.unavailableCalls} unpriced`; } function formatTokens(n: number): string { if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`; return String(n); } function BucketBars({ buckets, emptyMessage, billing, }: { buckets: UsageBucket[]; emptyMessage: string; billing: UsageBillingMode; }) { if (buckets.length === 0) { return (
{emptyMessage}
); } const max = Math.max( ...buckets.map((b) => displayAmountFromCostCents(b.cents, billing)), 0.0001, ); return ({error}
} {data && ( <> {/* Totals */}Builder.io credits are estimated from hard token cost, a{" "} {billing.hardCostMarginMultiplier ?? 1.25}x margin, and{" "} {billing.creditsPerUsd ?? 20} credits per dollar.
) : (Spend is estimated from published Anthropic pricing and your own recorded token counts. Cached input is priced at ~10% of regular input. Calls without provider pricing are marked unknown and excluded from known-cost subtotals.
)} > )}