Elements

72 / 122 · Observability

Cost meter

What the run spent, split by model, against the session total.

$2.76this run$18.40 session
Opus 548.2k in · 12.4k out$1.66
Sonnet 592.8k in · 21.1k out$0.86
Haiku 4.5140.0k in · 8.9k out$0.24

Installation

npx shadcn@latest add "@assistant-ui/elements-cost-meter"

Usage

import { CostMeter } from "@/components/elements/cost-meter";

<CostMeter runCost="$2.76" sessionCost="$18.40" lines={lines} />

Props

CostMeter

runCost*stringPre-formatted cost of this run.
sessionCost*stringPre-formatted cumulative cost.
lines*CostLine[]Per-model breakdown, ordered largest first so the bar reads left to right.
classNamestringExtra classes merged onto the root.

CostLine

model*stringModel this line accounts for.
inputTokens*numberInput tokens consumed. The element divides by 1000 for display.
outputTokens*numberOutput tokens produced.
share*numberFraction of the run's cost, 0 to 1. Drives the stacked bar.
cost*stringPre-formatted. The element never does currency math.

Source

cost-meter.tsx
"use client";

import { cn } from "@/lib/utils";
import { mono, paper } from "./surfaces";

export interface CostLine {
  model: string;
  inputTokens: number;
  outputTokens: number;
  cost: string;
  share: number;
}

export function CostMeter({
  runCost,
  sessionCost,
  lines,
  className,
}: {
  runCost: string;
  sessionCost: string;
  lines: readonly CostLine[];
  className?: string;
}) {
  return (
    <div
      className={cn(
        paper,
        "flex w-full max-w-sm flex-col gap-3 rounded-2xl p-4",
        className,
      )}
    >
      <div className="flex items-baseline gap-2">
        <span className="text-2xl font-medium tracking-tight tabular-nums">
          {runCost}
        </span>
        <span className={cn(mono, "text-foreground/30")}>this run</span>
        <span className={cn(mono, "text-foreground/35 ms-auto tabular-nums")}>
          {sessionCost} session
        </span>
      </div>

      <div className="bg-foreground/[0.06] flex h-1.5 w-full overflow-hidden rounded-full">
        {lines.map((line, i) => (
          <span
            key={line.model}
            className={cn(
              "h-full transition-[width] duration-500 motion-reduce:transition-none",
              i === 0
                ? "bg-blue-500 dark:bg-blue-400"
                : i === 1
                  ? "bg-blue-500/55 dark:bg-blue-400/55"
                  : "bg-foreground/25",
            )}
            style={{ width: `${line.share * 100}%` }}
          />
        ))}
      </div>

      <div className="flex flex-col gap-1.5">
        {lines.map((line) => (
          <div key={line.model} className="flex items-baseline gap-2">
            <span className="text-foreground/75 min-w-0 flex-1 truncate text-[13px]">
              {line.model}
            </span>
            <span
              className={cn(mono, "text-foreground/25 shrink-0 tabular-nums")}
            >
              {(line.inputTokens / 1000).toFixed(1)}k in ·{" "}
              {(line.outputTokens / 1000).toFixed(1)}k out
            </span>
            <span
              className={cn(mono, "text-foreground/55 shrink-0 tabular-nums")}
            >
              {line.cost}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}