638 lines · 27.1 KB · TypeScript
RawDownload
import type { UsageProviderKind } from "@t3tools/contracts";import { CheckIcon, RefreshCwIcon, XIcon } from "lucide-react";import { useMemo, useState } from "react"; import type { DailyTotals, HourlyTotals } from "@t3tools/shared/usageMerge"; import { isElectron } from "../../env";import { cn } from "../../lib/utils";import { useUsage, type EnvironmentUsageStatus } from "../../state/usage";import {  enumerateDays,  enumerateHourStarts,  formatCount,  formatDateTimeShort,  formatDayShort,  formatHourShort,  formatPercent,  formatTokens,  formatUsd,  makeWindow,} from "@t3tools/shared/usageFormat";import { ScrollArea } from "../ui/scroll-area";import { SidebarInset } from "../ui/sidebar";import { WorkspaceBreadcrumb, WorkspaceBreadcrumbItem } from "../WorkspaceBreadcrumb";import { COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS } from "../../workspaceTitlebar";import { UsageChartLegend, UsageProviderChart, type UsageChartMetric } from "./UsageProviderChart";import { PROVIDER_COLOR, PROVIDER_LABEL, PROVIDER_MARK, PROVIDER_ORDER } from "./usageProviders"; const WINDOW_OPTIONS = [  { days: 1, label: "Past 24h" },  { days: 7, label: "7 days" },  { days: 30, label: "30 days" },  { days: 90, label: "90 days" },] as const; export function UsagePage() {  const [windowSelection, setWindowSelection] = useState(() => ({    days: 30,    window: makeWindow(30),  }));  const [metric, setMetric] = useState<UsageChartMetric>("cost");  const [breakdown, setBreakdown] = useState<"model" | "time">("model");  const { days: windowDays, window } = windowSelection;  const isPast24Hours = windowDays === 1;  const { merged, environments, isPending, isPartial, refresh } = useUsage(window);   // Hold the content until every environment is terminal. Rendering merged  // totals while devices are still answering makes every number on the page  // jump as each one lands.  const settling = isPending || isPartial;   const days = useMemo(    () => enumerateDays(window.sinceDay, window.untilDay),    [window.sinceDay, window.untilDay],  );  const hours = useMemo(    () =>      window.sinceTime === undefined || window.untilTime === undefined        ? []        : enumerateHourStarts(window.sinceTime, window.untilTime),    [window.sinceTime, window.untilTime],  );  const recentPeriods = useMemo<readonly (DailyTotals | HourlyTotals)[]>(    () => (isPast24Hours ? merged.hourly : merged.daily).toReversed().slice(0, 8),    [isPast24Hours, merged.daily, merged.hourly],  );   // Ranked by whatever the toggle is showing, so the bars always descend.  const orderedProviders = useMemo(    () =>      merged.providers.toSorted((a, b) =>        metric === "cost" ? b.costUsd - a.costUsd : b.totalTokens - a.totalTokens,      ),    [merged.providers, metric],  );   const activePeriods = (isPast24Hours ? merged.hourly : merged.daily).filter(    (period) => period.totalTokens > 0,  ).length;  const periodAverage = activePeriods === 0 ? 0 : merged.totalTokens / activePeriods;  const observedInput = merged.uncachedInputTokens + merged.cachedInputTokens;  const cachedShare = observedInput === 0 ? 0 : merged.cachedInputTokens / observedInput;  const selectWindow = (days: number) => {    setWindowSelection({      days,      window: makeWindow(days, undefined, days === 1 ? "hour" : "day"),    });  };  const refreshWindow = () => {    const nextWindow = makeWindow(windowDays, undefined, isPast24Hours ? "hour" : "day");    if (      nextWindow.sinceDay === window.sinceDay &&      nextWindow.untilDay === window.untilDay &&      nextWindow.sinceTime === window.sinceTime &&      nextWindow.untilTime === window.untilTime    ) {      refresh();    } else {      setWindowSelection({ days: windowDays, window: nextWindow });    }  };   return (    <SidebarInset className="h-dvh min-h-0 overflow-hidden overscroll-y-none bg-background text-foreground isolate">      <div className="flex min-h-0 min-w-0 flex-1 flex-col bg-background text-foreground">        {!isElectron && (          <header            className={cn(              "workspace-topbar px-3 transition-[padding-left] duration-200 ease-linear motion-reduce:transition-none sm:px-5",              COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS,            )}          >            <WorkspaceBreadcrumb ariaLabel="Usage breadcrumb">              <WorkspaceBreadcrumbItem current>Usage</WorkspaceBreadcrumbItem>            </WorkspaceBreadcrumb>          </header>        )}         {isElectron && (          <div            className={cn(              "drag-region flex h-[52px] shrink-0 items-center px-5 transition-[padding-left] duration-200 ease-linear motion-reduce:transition-none wco:h-[env(titlebar-area-height)] wco:pr-[calc(100vw-env(titlebar-area-width)-env(titlebar-area-x)+1em)]",              COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS,            )}          >            <WorkspaceBreadcrumb ariaLabel="Usage breadcrumb">              <WorkspaceBreadcrumbItem current>Usage</WorkspaceBreadcrumbItem>            </WorkspaceBreadcrumb>          </div>        )}         <ScrollArea className="min-h-0 flex-1">          <div className="mx-auto flex w-full max-w-6xl flex-col gap-8 px-6 py-6">            <div className="flex flex-wrap items-center justify-between gap-4">              <p className="text-sm text-muted-foreground">                {isPast24Hours && window.sinceTime !== undefined && window.untilTime !== undefined                  ? `${formatDateTimeShort(window.sinceTime, window.timeZone)} to ${formatDateTimeShort(window.untilTime, window.timeZone)}`                  : `${formatDayShort(window.sinceDay)} to ${formatDayShort(window.untilDay)}`}              </p>              <div className="flex items-center gap-2">                <div className="flex rounded-md border border-border">                  {WINDOW_OPTIONS.map((option) => (                    <button                      key={option.days}                      type="button"                      aria-pressed={option.days === windowDays}                      onClick={() => selectWindow(option.days)}                      className={cn(                        "relative cursor-pointer px-3 py-1.5 text-xs outline-none first:rounded-s-[calc(var(--radius-md)-1px)] last:rounded-e-[calc(var(--radius-md)-1px)] focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",                        option.days === windowDays                          ? "bg-muted text-foreground"                          : "text-muted-foreground hover:text-foreground",                      )}                    >                      {option.label}                    </button>                  ))}                </div>                <button                  type="button"                  onClick={refreshWindow}                  aria-label="Refresh usage"                  className="cursor-pointer rounded-md border border-border p-2 text-muted-foreground hover:text-foreground"                >                  <RefreshCwIcon className="size-3.5" />                </button>              </div>            </div>             {settling ? (              <>                {environments.length > 1 ? <UsageDeviceStrip environments={environments} /> : null}                <UsageSkeleton resolution={isPast24Hours ? "hour" : "day"} />              </>            ) : (              <>                <UsageCoverageNotice                  environments={environments}                  duplicateSources={merged.duplicateSources}                  staleEnvironments={merged.staleEnvironments}                />                 {/* Cost first: the financial answer, then the provider split. */}                <section className="grid gap-6 lg:grid-cols-[minmax(0,20rem)_minmax(0,1fr)]">                  {/* The summary follows the chart toggle, so the headline and the                  series are always reading the same units. */}                  <div className="flex flex-col gap-5">                    <div className="flex flex-col gap-1">                      <span className="text-xs tracking-wide text-muted-foreground uppercase">                        {metric === "cost" ? "Raw token cost" : "Processed tokens"}                      </span>                      <span className="text-4xl font-semibold text-foreground tabular-nums">                        {metric === "cost"                          ? `${formatUsd(merged.costUsd)}*`                          : formatTokens(merged.totalTokens)}                      </span>                      <span className="text-xs text-muted-foreground">                        {metric === "cost"                          ? "* if billed at full API rate"                          : `Input, cache reads and output across ${formatCount(merged.sessions)} sessions.`}                      </span>                    </div>                     {orderedProviders.map((provider) => {                      const share = metric === "cost" ? provider.costShare : provider.tokenShare;                      return (                        <div key={provider.provider} className="flex flex-col gap-1.5">                          <div className="flex items-baseline justify-between">                            <span className="flex items-center gap-2 text-sm text-foreground">                              <ProviderMark provider={provider.provider} className="size-4" />                              {PROVIDER_LABEL[provider.provider]}                            </span>                            <span className="text-sm text-foreground tabular-nums">                              {metric === "cost"                                ? formatUsd(provider.costUsd)                                : formatTokens(provider.totalTokens)}                            </span>                          </div>                          <div className="h-1 w-full overflow-hidden rounded-full bg-muted">                            <div                              className="h-full"                              style={{                                width: `${(share * 100).toFixed(1)}%`,                                backgroundColor: PROVIDER_COLOR[provider.provider],                              }}                            />                          </div>                          <span className="text-xs text-muted-foreground">                            {metric === "cost"                              ? `${formatPercent(share)} of cost · ${formatTokens(provider.totalTokens)} tokens`                              : `${formatPercent(share)} of tokens · ${formatUsd(provider.costUsd)}`}                          </span>                        </div>                      );                    })}                  </div>                   <div className="flex flex-col gap-3">                    <div className="flex flex-wrap items-center justify-between gap-3">                      <h2 className="text-sm font-medium text-foreground">                        {isPast24Hours ? "Hourly" : "Daily"}{" "}                        {metric === "tokens" ? "processed tokens" : "cost"}                      </h2>                      <div className="flex items-center gap-4">                        <div className="flex overflow-hidden rounded-md border border-border">                          {(["cost", "tokens"] as const).map((option) => (                            <button                              key={option}                              type="button"                              onClick={() => setMetric(option)}                              className={cn(                                "cursor-pointer px-2.5 py-1 text-[10px] tracking-wide uppercase",                                option === metric                                  ? "bg-muted text-foreground"                                  : "text-muted-foreground hover:text-foreground",                              )}                            >                              {option}                            </button>                          ))}                        </div>                        <UsageChartLegend />                      </div>                    </div>                    <UsageProviderChart                      days={days}                      daily={merged.daily}                      hours={hours}                      hourly={merged.hourly}                      metric={metric}                      referenceTime={window.untilTime}                      resolution={isPast24Hours ? "hour" : "day"}                      timeZone={window.timeZone}                    />                  </div>                </section>                 <section className="grid grid-cols-2 gap-px border-y border-border bg-border md:grid-cols-5">                  <Metric                    label="Processed tokens"                    value={formatTokens(merged.totalTokens)}                    detail={`${formatTokens(periodAverage)} per active ${isPast24Hours ? "hour" : "day"}`}                  />                  <Metric                    label="Cached input"                    value={formatTokens(merged.cachedInputTokens)}                    detail={`${formatPercent(cachedShare)} of observed input`}                  />                  <Metric                    label="Uncached input"                    value={formatTokens(merged.uncachedInputTokens)}                    detail={`${formatTokens(merged.cacheCreationTokens)} cache writes`}                  />                  <Metric                    label="Output"                    value={formatTokens(merged.outputTokens)}                    detail={`includes ${formatTokens(merged.reasoningTokens)} reasoning`}                  />                  <Metric                    label="Cache savings"                    value={formatUsd(merged.costQuality.cacheSavingsUsd)}                    detail={                      merged.costUsd > 0                        ? `${(merged.costQuality.cacheSavingsUsd / merged.costUsd).toFixed(1)}x the raw token cost`                        : "vs full input rates"                    }                  />                </section>                 <section className="flex flex-col gap-3">                  <div className="flex items-center justify-between gap-3">                    <h2 className="text-sm font-medium text-foreground">Breakdown</h2>                    <div className="flex overflow-hidden rounded-md border border-border">                      {(                        [                          { value: "model", label: "model" },                          { value: "time", label: isPast24Hours ? "hour" : "day" },                        ] as const                      ).map((option) => (                        <button                          key={option.value}                          type="button"                          onClick={() => setBreakdown(option.value)}                          className={cn(                            "cursor-pointer px-2.5 py-1 text-[10px] tracking-wide uppercase",                            option.value === breakdown                              ? "bg-muted text-foreground"                              : "text-muted-foreground hover:text-foreground",                          )}                        >                          {option.label}                        </button>                      ))}                    </div>                  </div>                   {breakdown === "model" ? (                    <table className="w-full text-sm">                      <thead>                        <tr className="border-b border-border text-left text-xs text-muted-foreground">                          <th className="py-2 font-normal">Model</th>                          <th className="py-2 text-right font-normal">Cost</th>                          <th className="py-2 text-right font-normal">Share</th>                          <th className="py-2 text-right font-normal">Tokens</th>                        </tr>                      </thead>                      <tbody>                        {merged.models.length === 0 ? (                          <tr>                            <td colSpan={4} className="py-6 text-center text-muted-foreground">                              No activity in this window.                            </td>                          </tr>                        ) : (                          merged.models.map((model) => (                            <tr                              key={`${model.provider}:${model.model}`}                              className="border-b border-border/50"                            >                              <td className="py-2 text-foreground">                                <span className="flex items-center gap-2">                                  <ProviderMark provider={model.provider} className="size-3.5" />                                  {model.model}                                </span>                              </td>                              <td className="py-2 text-right text-foreground tabular-nums">                                {formatUsd(model.costUsd)}                              </td>                              <td className="py-2 text-right text-muted-foreground tabular-nums">                                {formatPercent(model.costShare)}                              </td>                              <td className="py-2 text-right text-muted-foreground tabular-nums">                                {formatTokens(model.totalTokens)}                              </td>                            </tr>                          ))                        )}                      </tbody>                    </table>                  ) : (                    <table className="w-full text-sm">                      <thead>                        <tr className="border-b border-border text-left text-xs text-muted-foreground">                          <th className="py-2 font-normal">{isPast24Hours ? "Hour" : "Day"}</th>                          {PROVIDER_ORDER.map((provider) => (                            <th key={provider} className="py-2 text-right font-normal">                              {PROVIDER_LABEL[provider]}                            </th>                          ))}                          <th className="py-2 text-right font-normal">Total</th>                          <th className="py-2 text-right font-normal">Tokens</th>                        </tr>                      </thead>                      <tbody>                        {recentPeriods.length === 0 ? (                          <tr>                            <td colSpan={5} className="py-6 text-center text-muted-foreground">                              No activity in this window.                            </td>                          </tr>                        ) : (                          recentPeriods.map((period) => (                            <tr                              key={"hourStart" in period ? period.hourStart : period.day}                              className="border-b border-border/50"                            >                              <td className="py-2 text-foreground">                                {"hourStart" in period                                  ? formatHourShort(period.hourStart, window.timeZone)                                  : formatDayShort(period.day)}                              </td>                              {PROVIDER_ORDER.map((provider) => (                                <td                                  key={provider}                                  className="py-2 text-right text-muted-foreground tabular-nums"                                >                                  {formatUsd(period.byProvider.get(provider)?.costUsd ?? 0)}                                </td>                              ))}                              <td className="py-2 text-right text-foreground tabular-nums">                                {formatUsd(period.costUsd)}                              </td>                              <td className="py-2 text-right text-muted-foreground tabular-nums">                                {formatTokens(period.totalTokens)}                              </td>                            </tr>                          ))                        )}                      </tbody>                    </table>                  )}                </section>              </>            )}          </div>        </ScrollArea>      </div>    </SidebarInset>  );} /** Brand mark for the harness a row belongs to. */function ProviderMark({  provider,  className,}: {  readonly provider: UsageProviderKind;  readonly className: string;}) {  const Mark = PROVIDER_MARK[provider];  return <Mark className={cn("shrink-0", className)} aria-hidden />;} function Metric({  label,  value,  detail,}: {  readonly label: string;  readonly value: string;  readonly detail: string;}) {  return (    <div className="flex flex-col gap-0.5 bg-background px-4 py-3">      <span className="text-xs text-muted-foreground">{label}</span>      <span className="text-lg text-foreground tabular-nums">{value}</span>      <span className="text-xs text-muted-foreground">{detail}</span>    </div>  );} /** * Says plainly when the totals are incomplete: an environment that failed, or * one whose transcripts another environment already reported. Environments * that are still answering never reach this notice; the page shows the * loading skeleton until every one is terminal. */function UsageCoverageNotice({  environments,  duplicateSources,  staleEnvironments,}: {  readonly environments: readonly EnvironmentUsageStatus[];  readonly duplicateSources: readonly string[];  readonly staleEnvironments: readonly string[];}) {  const failed = environments.filter((environment) => environment.error !== null);  const stale = environments.filter((environment) =>    staleEnvironments.includes(environment.environmentId),  );  if (failed.length === 0 && stale.length === 0 && duplicateSources.length === 0) {    return null;  }   return (    <div className="flex flex-col gap-1 border border-border px-3 py-2 text-xs text-muted-foreground">      {failed.map((environment) => (        <span key={environment.label}>{environment.label} could not report usage.</span>      ))}      {stale.map((environment) => (

Lines 1–500 of 638.