diff --git a/ui/app/workspace/dashboard/components/charts/chartCard.tsx b/ui/app/workspace/dashboard/components/charts/chartCard.tsx index b93974db2d5..ce0552595f1 100644 --- a/ui/app/workspace/dashboard/components/charts/chartCard.tsx +++ b/ui/app/workspace/dashboard/components/charts/chartCard.tsx @@ -1,5 +1,6 @@ import { Card } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import type { ReactNode } from "react"; @@ -13,10 +14,21 @@ interface ChartCardProps { className?: string; total?: ReactNode; totalLabel?: string; + totalTooltip?: ReactNode; } -function TotalChip({ total, totalLabel, testId }: { total: ReactNode; totalLabel?: string; testId?: string }) { - return ( +function TotalChip({ + total, + totalLabel, + totalTooltip, + testId, +}: { + total: ReactNode; + totalLabel?: string; + totalTooltip?: ReactNode; + testId?: string; +}) { + const chip = ( {total} ); + + if (totalTooltip === undefined || totalTooltip === null) { + return chip; + } + + return ( + + + + {chip} + + + {totalTooltip} + + ); } function Header({ @@ -33,6 +60,7 @@ function Header({ legend, total, totalLabel, + totalTooltip, testId, }: { title: string; @@ -40,6 +68,7 @@ function Header({ legend?: ReactNode; total?: ReactNode; totalLabel?: string; + totalTooltip?: ReactNode; testId?: string; }) { const hasTotal = total !== undefined && total !== null; @@ -51,7 +80,11 @@ function Header({ {hasActionRow && (
- {hasTotal ? : } + {hasTotal ? ( + + ) : ( + + )} {controls &&
{controls}
}
)} @@ -60,11 +93,30 @@ function Header({ ); } -export function ChartCard({ title, children, controls, legend, loading, testId, className, total, totalLabel }: ChartCardProps) { +export function ChartCard({ + title, + children, + controls, + legend, + loading, + testId, + className, + total, + totalLabel, + totalTooltip, +}: ChartCardProps) { if (loading) { return ( -
+
@@ -74,7 +126,15 @@ export function ChartCard({ title, children, controls, legend, loading, testId, return ( -
+
{children}
); diff --git a/ui/app/workspace/dashboard/components/charts/costChart.tsx b/ui/app/workspace/dashboard/components/charts/costChart.tsx index 71b3d40597c..0dd2491eab2 100644 --- a/ui/app/workspace/dashboard/components/charts/costChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/costChart.tsx @@ -1,4 +1,5 @@ import type { CostHistogramResponse } from "@/lib/types/logs"; +import { formatCurrencyNumber } from "@/lib/utils/numbers"; import { memo, useMemo } from "react"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; import { @@ -136,7 +137,7 @@ function CostChartImpl({ data, chartType, startTime, endTime, selectedModel }: C tickLine={false} axisLine={false} width={50} - tickFormatter={(v) => formatCost(v)} + tickFormatter={(v) => formatCurrencyNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 0.01)]} allowDataOverflow={false} /> @@ -172,7 +173,7 @@ function CostChartImpl({ data, chartType, startTime, endTime, selectedModel }: C tickLine={false} axisLine={false} width={50} - tickFormatter={(v) => formatCost(v)} + tickFormatter={(v) => formatCurrencyNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 0.01)]} allowDataOverflow={false} /> diff --git a/ui/app/workspace/dashboard/components/charts/externalCacheTokenMeterChart.tsx b/ui/app/workspace/dashboard/components/charts/externalCacheTokenMeterChart.tsx index 0788f3fc560..0836097b907 100644 --- a/ui/app/workspace/dashboard/components/charts/externalCacheTokenMeterChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/externalCacheTokenMeterChart.tsx @@ -1,5 +1,6 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import type { TokenHistogramResponse } from "@/lib/types/logs"; +import { formatCompactNumber } from "@/lib/utils/numbers"; import { Info } from "lucide-react"; import { memo, useMemo } from "react"; import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts"; @@ -12,12 +13,6 @@ interface ExternalCacheTokenMeterChartProps { const METER_COLORS = { cached: "#06b6d4", input: "#3b82f6" }; -const formatTokenCount = (count: number): string => { - if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`; - if (count >= 1000) return `${(count / 1000).toFixed(1)}K`; - return count.toLocaleString(); -}; - function ExternalCacheTokenMeterChartImpl({ data }: ExternalCacheTokenMeterChartProps) { const { ref, width, height } = useGaugeSize(); @@ -101,11 +96,11 @@ function ExternalCacheTokenMeterChartImpl({ data }: ExternalCacheTokenMeterChart
- Cached: {formatTokenCount(totalCachedRead)} + Cached: {formatCompactNumber(totalCachedRead)} - Input: {formatTokenCount(totalPromptTokens)} + Input: {formatCompactNumber(totalPromptTokens)}
diff --git a/ui/app/workspace/dashboard/components/charts/logVolumeChart.tsx b/ui/app/workspace/dashboard/components/charts/logVolumeChart.tsx index dc5238c87ac..2e1a0d8e98c 100644 --- a/ui/app/workspace/dashboard/components/charts/logVolumeChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/logVolumeChart.tsx @@ -1,7 +1,8 @@ import type { LogsHistogramResponse } from "@/lib/types/logs"; import { memo, useMemo } from "react"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; -import { CHART_COLORS, formatFullTimestamp, formatTimestamp, formatTokens } from "../../utils/chartUtils"; +import { formatCompactNumber } from "@/lib/utils/numbers"; +import { CHART_COLORS, formatFullTimestamp, formatTimestamp } from "../../utils/chartUtils"; import { ChartErrorBoundary } from "./chartErrorBoundary"; import type { ChartType } from "./chartTypeToggle"; @@ -98,7 +99,7 @@ function LogVolumeChartImpl({ data, chartType, startTime, endTime }: LogVolumeCh tickLine={false} axisLine={false} width={44} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> @@ -140,7 +141,7 @@ function LogVolumeChartImpl({ data, chartType, startTime, endTime }: LogVolumeCh tickLine={false} axisLine={false} width={44} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> diff --git a/ui/app/workspace/dashboard/components/charts/mcpTopToolsChart.tsx b/ui/app/workspace/dashboard/components/charts/mcpTopToolsChart.tsx index 4058c106de6..a0ee9b88c8f 100644 --- a/ui/app/workspace/dashboard/components/charts/mcpTopToolsChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/mcpTopToolsChart.tsx @@ -1,7 +1,8 @@ import type { MCPTopToolsResponse } from "@/lib/types/logs"; import { memo, useMemo } from "react"; import { Bar, BarChart, CartesianGrid, Cell, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; -import { formatCost, formatTokens, getModelColor } from "../../utils/chartUtils"; +import { formatCompactNumber } from "@/lib/utils/numbers"; +import { formatCost, getModelColor } from "../../utils/chartUtils"; import { ChartErrorBoundary } from "./chartErrorBoundary"; interface MCPTopToolsChartProps { @@ -54,7 +55,7 @@ function MCPTopToolsChartImpl({ data }: MCPTopToolsChartProps) { tick={{ fontSize: 11, className: "fill-zinc-500" }} tickLine={false} axisLine={false} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> diff --git a/ui/app/workspace/dashboard/components/charts/mcpVolumeChart.tsx b/ui/app/workspace/dashboard/components/charts/mcpVolumeChart.tsx index 73e99352677..9f88059841e 100644 --- a/ui/app/workspace/dashboard/components/charts/mcpVolumeChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/mcpVolumeChart.tsx @@ -1,7 +1,8 @@ import type { MCPHistogramResponse } from "@/lib/types/logs"; import { memo, useMemo } from "react"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; -import { CHART_COLORS, formatFullTimestamp, formatTimestamp, formatTokens } from "../../utils/chartUtils"; +import { formatCompactNumber } from "@/lib/utils/numbers"; +import { CHART_COLORS, formatFullTimestamp, formatTimestamp } from "../../utils/chartUtils"; import { ChartErrorBoundary } from "./chartErrorBoundary"; import type { ChartType } from "./chartTypeToggle"; @@ -88,7 +89,7 @@ function MCPVolumeChartImpl({ data, chartType, startTime, endTime }: MCPVolumeCh tickLine={false} axisLine={false} width={44} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> @@ -130,7 +131,7 @@ function MCPVolumeChartImpl({ data, chartType, startTime, endTime }: MCPVolumeCh tickLine={false} axisLine={false} width={44} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> diff --git a/ui/app/workspace/dashboard/components/charts/modelUsageChart.tsx b/ui/app/workspace/dashboard/components/charts/modelUsageChart.tsx index 644b3380a0e..a6667922c10 100644 --- a/ui/app/workspace/dashboard/components/charts/modelUsageChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/modelUsageChart.tsx @@ -1,11 +1,11 @@ import type { ModelHistogramResponse } from "@/lib/types/logs"; +import { formatCompactNumber } from "@/lib/utils/numbers"; import { memo, useMemo } from "react"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; import { CHART_COLORS, formatFullTimestamp, formatTimestamp, - formatTokens, getModelColor, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, @@ -163,7 +163,7 @@ function ModelUsageChartImpl({ data, chartType, startTime, endTime, selectedMode tickLine={false} axisLine={false} width={44} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> @@ -222,7 +222,7 @@ function ModelUsageChartImpl({ data, chartType, startTime, endTime, selectedMode tickLine={false} axisLine={false} width={44} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> diff --git a/ui/app/workspace/dashboard/components/charts/providerCostChart.tsx b/ui/app/workspace/dashboard/components/charts/providerCostChart.tsx index abd31e1229d..378d3f18c51 100644 --- a/ui/app/workspace/dashboard/components/charts/providerCostChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/providerCostChart.tsx @@ -1,4 +1,5 @@ import type { ProviderCostHistogramResponse } from "@/lib/types/logs"; +import { formatCurrencyNumber } from "@/lib/utils/numbers"; import { memo, useMemo } from "react"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; import { @@ -138,7 +139,7 @@ function ProviderCostChartImpl({ data, chartType, startTime, endTime, selectedPr tickLine={false} axisLine={false} width={50} - tickFormatter={(v) => formatCost(v)} + tickFormatter={(v) => formatCurrencyNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 0.01)]} allowDataOverflow={false} /> diff --git a/ui/app/workspace/dashboard/components/charts/providerTokenChart.tsx b/ui/app/workspace/dashboard/components/charts/providerTokenChart.tsx index 07c77a3a8a0..a5092cc8762 100644 --- a/ui/app/workspace/dashboard/components/charts/providerTokenChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/providerTokenChart.tsx @@ -1,11 +1,11 @@ import type { ProviderTokenHistogramResponse } from "@/lib/types/logs"; +import { formatCompactNumber } from "@/lib/utils/numbers"; import { memo, useMemo } from "react"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; import { CHART_COLORS, formatFullTimestamp, formatTimestamp, - formatTokens, getModelColor, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, @@ -43,7 +43,7 @@ function AllProvidersTooltip({ active, payload, displayProviders }: any) { {isOther ? OTHER_SERIES_LABEL : provider} - {formatTokens(tokens)} + {formatCompactNumber(tokens)} ); })} @@ -70,18 +70,18 @@ function SingleProviderTooltip({ active, payload, provider }: any) { Input - {formatTokens(stats.prompt_tokens || 0)} + {formatCompactNumber(stats.prompt_tokens || 0)}
Output - {formatTokens(stats.completion_tokens || 0)} + {formatCompactNumber(stats.completion_tokens || 0)}
Total - {formatTokens(stats.total_tokens || 0)} + {formatCompactNumber(stats.total_tokens || 0)}
@@ -167,7 +167,7 @@ function ProviderTokenChartImpl({ data, chartType, startTime, endTime, selectedP tickLine={false} axisLine={false} width={50} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> @@ -232,7 +232,7 @@ function ProviderTokenChartImpl({ data, chartType, startTime, endTime, selectedP tickLine={false} axisLine={false} width={50} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> diff --git a/ui/app/workspace/dashboard/components/charts/tokenUsageChart.tsx b/ui/app/workspace/dashboard/components/charts/tokenUsageChart.tsx index 5c10ced626f..4233369e462 100644 --- a/ui/app/workspace/dashboard/components/charts/tokenUsageChart.tsx +++ b/ui/app/workspace/dashboard/components/charts/tokenUsageChart.tsx @@ -1,7 +1,8 @@ import type { TokenHistogramResponse } from "@/lib/types/logs"; import { memo, useMemo } from "react"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; -import { CHART_COLORS, formatFullTimestamp, formatTimestamp, formatTokens } from "../../utils/chartUtils"; +import { formatCompactNumber } from "@/lib/utils/numbers"; +import { CHART_COLORS, formatFullTimestamp, formatTimestamp } from "../../utils/chartUtils"; import { ChartErrorBoundary } from "./chartErrorBoundary"; import type { ChartType } from "./chartTypeToggle"; @@ -98,7 +99,7 @@ function TokenUsageChartImpl({ data, chartType, startTime, endTime }: TokenUsage tickLine={false} axisLine={false} width={50} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> @@ -149,7 +150,7 @@ function TokenUsageChartImpl({ data, chartType, startTime, endTime }: TokenUsage tickLine={false} axisLine={false} width={50} - tickFormatter={formatTokens} + tickFormatter={(v) => formatCompactNumber(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 1)]} allowDataOverflow={false} /> diff --git a/ui/app/workspace/dashboard/components/mcpTab.tsx b/ui/app/workspace/dashboard/components/mcpTab.tsx index 9d9356be90b..83d9d534f1f 100644 --- a/ui/app/workspace/dashboard/components/mcpTab.tsx +++ b/ui/app/workspace/dashboard/components/mcpTab.tsx @@ -71,6 +71,7 @@ function MCPTabImpl({ testId="chart-mcp-volume" totalLabel="Total" total={mcpVolumeTotal !== null ? : undefined} + totalTooltip={mcpVolumeTotal !== null ? mcpVolumeTotal.toLocaleString("en-US") : undefined} legend={
@@ -105,6 +106,11 @@ function MCPTabImpl({ ) : undefined } + totalTooltip={ + mcpCostTotal !== null + ? mcpCostTotal.toLocaleString("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 6 }) + : undefined + } legend={
@@ -113,7 +119,9 @@ function MCPTabImpl({
} - controls={} + controls={ + + } > @@ -125,6 +133,7 @@ function MCPTabImpl({ testId="chart-mcp-top-tools" totalLabel="Total" total={mcpTopToolsTotal !== null ? : undefined} + totalTooltip={mcpTopToolsTotal !== null ? mcpTopToolsTotal.toLocaleString("en-US") : undefined} > diff --git a/ui/app/workspace/dashboard/components/modelRankingsTab.tsx b/ui/app/workspace/dashboard/components/modelRankingsTab.tsx index f6fedcfea29..670fa37a374 100644 --- a/ui/app/workspace/dashboard/components/modelRankingsTab.tsx +++ b/ui/app/workspace/dashboard/components/modelRankingsTab.tsx @@ -3,8 +3,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import ProviderIcons, { type ProviderIconType, RenderProviderIcon } from "@/lib/constants/icons"; import type { ModelHistogramResponse, ModelRankingEntry, ModelRankingsResponse } from "@/lib/types/logs"; -import { formatCompactNumber as formatNumber } from "@/lib/utils/governance"; -import { COMPACT_NUMBER_FORMAT } from "@/lib/utils/numbers"; +import { COMPACT_NUMBER_FORMAT, formatCompactNumber as formatNumber } from "@/lib/utils/numbers"; import NumberFlow from "@number-flow/react"; import { ArrowDown, ArrowUp, ArrowUpDown, Minus } from "lucide-react"; import { memo, useCallback, useMemo, useState } from "react"; @@ -233,6 +232,7 @@ function TopModelsChart({ className="z-[1] h-full" totalLabel="Total" total={grandTotal !== null ? : undefined} + totalTooltip={grandTotal !== null ? grandTotal.toLocaleString("en-US") : undefined} >
{chartData.length > 0 ? ( diff --git a/ui/app/workspace/dashboard/components/overviewTab.tsx b/ui/app/workspace/dashboard/components/overviewTab.tsx index ff59408e0dc..ebcb7d8065e 100644 --- a/ui/app/workspace/dashboard/components/overviewTab.tsx +++ b/ui/app/workspace/dashboard/components/overviewTab.tsx @@ -1,6 +1,4 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; -import { COMPACT_NUMBER_FORMAT } from "@/lib/utils/numbers"; -import NumberFlow from "@number-flow/react"; import type { CostHistogramResponse, LatencyHistogramResponse, @@ -9,6 +7,8 @@ import type { ModelHistogramResponse, TokenHistogramResponse, } from "@/lib/types/logs"; +import { COMPACT_NUMBER_FORMAT } from "@/lib/utils/numbers"; +import NumberFlow from "@number-flow/react"; import { memo, useMemo } from "react"; import { CHART_COLORS, CHART_HEADER_LEGEND_CLASS, LATENCY_COLORS, getModelColor } from "../utils/chartUtils"; import { ChartCard } from "./charts/chartCard"; @@ -159,6 +159,7 @@ function OverviewTabImpl({ testId="chart-log-volume" totalLabel="Total" total={volumeTotal !== null ? : undefined} + totalTooltip={volumeTotal !== null ? volumeTotal.toLocaleString("en-US") : undefined} legend={
@@ -171,7 +172,9 @@ function OverviewTabImpl({
} - controls={} + controls={ + + } > @@ -183,6 +186,7 @@ function OverviewTabImpl({ testId="chart-token-usage" totalLabel="Total" total={tokenTotal !== null ? : undefined} + totalTooltip={tokenTotal !== null ? tokenTotal.toLocaleString("en-US") : undefined} legend={
@@ -225,6 +229,11 @@ function OverviewTabImpl({ ) : undefined } + totalTooltip={ + costTotal !== null + ? costTotal.toLocaleString("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 6 }) + : undefined + } legend={
{costModel === "all" ? ( @@ -300,6 +309,7 @@ function OverviewTabImpl({ testId="chart-model-usage" totalLabel="Total" total={modelUsageTotal !== null ? : undefined} + totalTooltip={modelUsageTotal !== null ? modelUsageTotal.toLocaleString("en-US") : undefined} legend={
{usageModel === "all" ? ( @@ -380,6 +390,7 @@ function OverviewTabImpl({ ) : undefined } + totalTooltip={latencyAvg !== null ? `${latencyAvg.toLocaleString("en-US", { maximumFractionDigits: 6 })}ms` : undefined} legend={
@@ -401,11 +412,7 @@ function OverviewTabImpl({
} controls={ - + } > diff --git a/ui/app/workspace/dashboard/components/providerUsageTab.tsx b/ui/app/workspace/dashboard/components/providerUsageTab.tsx index c19df8868b8..dd1034fda4e 100644 --- a/ui/app/workspace/dashboard/components/providerUsageTab.tsx +++ b/ui/app/workspace/dashboard/components/providerUsageTab.tsx @@ -131,6 +131,11 @@ function ProviderUsageTabImpl({ ) : undefined } + totalTooltip={ + providerCostTotal !== null + ? providerCostTotal.toLocaleString("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 6 }) + : undefined + } legend={
{providerCostProvider === "all" ? ( @@ -215,6 +220,7 @@ function ProviderUsageTabImpl({ testId="chart-provider-tokens" totalLabel="Total" total={providerTokenTotal !== null ? : undefined} + totalTooltip={providerTokenTotal !== null ? providerTokenTotal.toLocaleString("en-US") : undefined} legend={
{providerTokenProvider === "all" ? ( @@ -304,6 +310,9 @@ function ProviderUsageTabImpl({ ) : undefined } + totalTooltip={ + providerLatencyAvg !== null ? `${providerLatencyAvg.toLocaleString("en-US", { maximumFractionDigits: 6 })}ms` : undefined + } legend={
{providerLatencyProvider === "all" ? ( diff --git a/ui/app/workspace/dashboard/utils/chartUtils.ts b/ui/app/workspace/dashboard/utils/chartUtils.ts index 3e967beb212..12019a3ce7f 100644 --- a/ui/app/workspace/dashboard/utils/chartUtils.ts +++ b/ui/app/workspace/dashboard/utils/chartUtils.ts @@ -30,23 +30,15 @@ export function formatFullTimestamp(timestamp: string): string { // Format cost values export function formatCost(cost: number): string { + if (cost === 0) { + return `$0`; + } if (cost < 0.01) { return `$${cost.toFixed(4)}`; } return `$${cost.toFixed(2)}`; } -// Format token values -export function formatTokens(tokens: number): string { - if (tokens >= 1000000) { - return `${(tokens / 1000000).toFixed(1)}M`; - } - if (tokens >= 1000) { - return `${(tokens / 1000).toFixed(1)}K`; - } - return tokens.toLocaleString(); -} - // Color palette for models. Length governs TOP_SERIES_LIMIT (top-N rollup cap), // so colors and named-series count stay coupled — adding a color expands top-N. export const MODEL_COLORS = [ diff --git a/ui/app/workspace/logs/sheets/logDetailView.tsx b/ui/app/workspace/logs/sheets/logDetailView.tsx index e6bda63ae6c..03eb7c91736 100644 --- a/ui/app/workspace/logs/sheets/logDetailView.tsx +++ b/ui/app/workspace/logs/sheets/logDetailView.tsx @@ -1,4 +1,5 @@ -import { formatCost, formatLatency, formatTokens } from "@/app/workspace/dashboard/utils/chartUtils"; +import { formatCost, formatLatency } from "@/app/workspace/dashboard/utils/chartUtils"; +import { formatCompactNumber } from "@/lib/utils/numbers"; import { AlertDialog, AlertDialogAction, @@ -764,14 +765,14 @@ export function LogDetailView({ mono value={ log.token_usage - ? `${formatTokens(log.token_usage.prompt_tokens ?? 0)} / ${formatTokens(log.token_usage.completion_tokens ?? 0)}` + ? `${formatCompactNumber(log.token_usage.prompt_tokens ?? 0)} / ${formatCompactNumber(log.token_usage.completion_tokens ?? 0)}` : "—" } sub={ log.token_usage - ? `total ${formatTokens(log.token_usage.total_tokens ?? 0)}${ + ? `total ${formatCompactNumber(log.token_usage.total_tokens ?? 0)}${ log.token_usage.completion_tokens_details?.reasoning_tokens - ? ` · reasoning ${formatTokens(log.token_usage.completion_tokens_details.reasoning_tokens)}` + ? ` · reasoning ${formatCompactNumber(log.token_usage.completion_tokens_details.reasoning_tokens)}` : "" }` : "—" diff --git a/ui/app/workspace/logs/views/columns.tsx b/ui/app/workspace/logs/views/columns.tsx index 4a2381157c5..fb9a7f13676 100644 --- a/ui/app/workspace/logs/views/columns.tsx +++ b/ui/app/workspace/logs/views/columns.tsx @@ -1,4 +1,5 @@ -import { formatCost, formatLatency, formatTokens } from "@/app/workspace/dashboard/utils/chartUtils"; +import { formatCost, formatLatency } from "@/app/workspace/dashboard/utils/chartUtils"; +import { formatCompactNumber } from "@/lib/utils/numbers"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdownMenu"; @@ -295,7 +296,7 @@ export const createColumns = ( return (
- {formatTokens(total)} + {formatCompactNumber(total)} {hasSplit && (
@@ -305,9 +306,9 @@ export const createColumns = (
{hasSplit && (
- {formatTokens(prompt)} + {formatCompactNumber(prompt)} / - {formatTokens(completion)} + {formatCompactNumber(completion)}
)}
diff --git a/ui/components/rateLimitDisplay.tsx b/ui/components/rateLimitDisplay.tsx index 20b457ccf6f..1ba294a0c26 100644 --- a/ui/components/rateLimitDisplay.tsx +++ b/ui/components/rateLimitDisplay.tsx @@ -2,7 +2,7 @@ import { Progress } from "@/components/ui/progress"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { resetDurationLabels } from "@/lib/constants/governance"; import { cn } from "@/lib/utils"; -import { formatCompactNumber } from "@/lib/utils/governance"; +import { formatCompactNumber } from "@/lib/utils/numbers"; interface RateLimitShape { token_max_limit?: number | null; diff --git a/ui/lib/utils/governance.ts b/ui/lib/utils/governance.ts index e257ac2b1b2..578aba4c282 100644 --- a/ui/lib/utils/governance.ts +++ b/ui/lib/utils/governance.ts @@ -24,25 +24,12 @@ export function parseResetPeriod(duration: string): string { return `${timeValue} ${unitName}`; } +import { formatCompactNumber } from "./numbers"; + export function formatCurrency(dollars: number) { return `$${dollars.toFixed(2)}`; } -/** - * Formats a number compactly (e.g. 10000 → "10K", 1500000 → "1.5M"). - * Uses Intl.NumberFormat so boundary values promote correctly (999,950 → "1M", not "1000K") - * and trailing zeros are dropped (10,000 → "10K", not "10.0K"). - */ -const compactNumberFormatter = new Intl.NumberFormat(undefined, { - notation: "compact", - maximumFractionDigits: 1, -}); - -export function formatCompactNumber(n: number): string { - if (Math.abs(n) >= 1_000) return compactNumberFormatter.format(n); - return n.toLocaleString(); -} - const shortDurationLabels: Record = { "1m": "/min", "5m": "/5min", diff --git a/ui/lib/utils/numbers.ts b/ui/lib/utils/numbers.ts index 11fa4d24425..22fceec249e 100644 --- a/ui/lib/utils/numbers.ts +++ b/ui/lib/utils/numbers.ts @@ -10,4 +10,17 @@ export function formatCompactNumber(value: number, maximumFractionDigits = 2): s ...COMPACT_NUMBER_FORMAT, maximumFractionDigits, }).format(value); +} + +export function formatCurrencyNumber(value: number, maximumFractionDigits = 2): string { + if (!Number.isFinite(value)) return "$0"; + if (value !== 0 && Math.abs(value) < 0.01) { + return `$${value.toFixed(4)}`; + } + return new Intl.NumberFormat("en-US", { + ...COMPACT_NUMBER_FORMAT, + style: "currency", + currency: "USD", + maximumFractionDigits, + }).format(value); } \ No newline at end of file