Add cost metrics to analytics dashboard
This commit is contained in:
@@ -3,18 +3,19 @@
|
||||
import { useEffect, useState, useCallback, useRef, startTransition } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { motion } from "motion/react";
|
||||
import { Users, Calendar, Hash, Zap, ArrowUpDown, ArrowDown, ArrowUp, HelpCircle } from "lucide-react";
|
||||
import { Users, Calendar, Hash, Zap, DollarSign, ArrowUpDown, ArrowDown, ArrowUp, HelpCircle } from "lucide-react";
|
||||
import { TimeRangeSelector } from "@/components/TimeRangeSelector";
|
||||
import { buildQuery, formatNumber, formatTokens } from "@/lib/utils";
|
||||
import { buildQuery, formatNumber, formatTokens, formatUSD } from "@/lib/utils";
|
||||
import { useTimeRange } from "@/lib/time-range-context";
|
||||
import { useI18n } from "@/lib/i18n";
|
||||
|
||||
interface AggItem {
|
||||
rank: number; name: string; calls: number;
|
||||
prompt_tokens: number; completion_tokens: number; cache_creation_tokens: number; cache_read_tokens: number; total_tokens: number;
|
||||
quota: number; quota_usd: number;
|
||||
}
|
||||
|
||||
type SortKey = "total_tokens" | "calls" | "prompt_tokens" | "completion_tokens" | "cache_creation_tokens" | "cache_read_tokens" | "ratio";
|
||||
type SortKey = "total_tokens" | "calls" | "prompt_tokens" | "completion_tokens" | "cache_creation_tokens" | "cache_read_tokens" | "quota_usd" | "ratio";
|
||||
|
||||
function RatioTooltip({ text }: { text: string }) {
|
||||
const [show, setShow] = useState(false);
|
||||
@@ -103,8 +104,8 @@ export default function AggregationPage() {
|
||||
});
|
||||
|
||||
const totals = data.reduce(
|
||||
(acc, d) => ({ calls: acc.calls + d.calls, tokens: acc.tokens + d.total_tokens }),
|
||||
{ calls: 0, tokens: 0 }
|
||||
(acc, d) => ({ calls: acc.calls + d.calls, tokens: acc.tokens + d.total_tokens, cost: acc.cost + d.quota_usd }),
|
||||
{ calls: 0, tokens: 0, cost: 0 }
|
||||
);
|
||||
|
||||
function handleSort(key: SortKey) {
|
||||
@@ -126,6 +127,7 @@ export default function AggregationPage() {
|
||||
{ key: "cache_read_tokens", label: t("th.cacheRead") },
|
||||
{ key: "completion_tokens", label: t("th.output") },
|
||||
{ key: "total_tokens", label: t("th.totalToken") },
|
||||
{ key: "quota_usd", label: t("th.cost") },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -155,6 +157,11 @@ export default function AggregationPage() {
|
||||
<span style={{ color: "var(--text-muted)" }}>{t("agg.totalToken")}</span>
|
||||
<span className="font-semibold font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>{formatTokens(totals.tokens)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<DollarSign className="h-3.5 w-3.5" style={{ color: "var(--accent)", opacity: 0.5 }} />
|
||||
<span style={{ color: "var(--text-muted)" }}>{t("agg.totalCost")}</span>
|
||||
<span className="font-semibold font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>{formatUSD(totals.cost)}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
@@ -183,7 +190,7 @@ export default function AggregationPage() {
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading ? (
|
||||
<tr><td colSpan={10} className="px-4 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
|
||||
<tr><td colSpan={11} className="px-4 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
|
||||
) : sorted.map((item, i) => {
|
||||
const pct = totals.tokens > 0 ? (item.total_tokens / totals.tokens * 100) : 0;
|
||||
const ratio = item.prompt_tokens > 0 ? (item.completion_tokens / item.prompt_tokens) : 0;
|
||||
@@ -197,6 +204,7 @@ export default function AggregationPage() {
|
||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.cache_read_tokens)}</td>
|
||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.completion_tokens)}</td>
|
||||
<td className="px-4 py-3 text-right tabular-nums font-medium font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>{formatTokens(item.total_tokens)}</td>
|
||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-secondary)" }}>{formatUSD(item.quota_usd)}</td>
|
||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: ratio >= 1 ? "var(--accent)" : "var(--text-muted)" }}>{ratio.toFixed(2)}</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
|
||||
Reference in New Issue
Block a user