"use client" import type { LanguageModelUsage } from "./types" import { type ComponentProps, createContext, useContext } from "react" import { getUsage } from "tokenlens" import { Button } from "@/components/ui/button" import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card" import { Progress } from "@/components/ui/progress" import { cn } from "@/lib/utils" const PERCENT_MAX = 100 const ICON_RADIUS = 10 const ICON_VIEWBOX = 24 const ICON_CENTER = 12 const ICON_STROKE_WIDTH = 2 type ModelId = string interface ContextSchema { usedTokens: number maxTokens: number usage?: LanguageModelUsage modelId?: ModelId } const ContextContext = createContext(null) const useContextValue = () => { const context = useContext(ContextContext) if (!context) { throw new Error("Context components must be used within Context") } return context } export type ContextProps = ComponentProps & ContextSchema export const Context = ({ usedTokens, maxTokens, usage, modelId, ...props }: ContextProps) => ( ) const ContextIcon = () => { const { usedTokens, maxTokens } = useContextValue() const circumference = 2 * Math.PI * ICON_RADIUS const usedPercent = usedTokens / maxTokens const dashOffset = circumference * (1 - usedPercent) return ( ) } export type ContextTriggerProps = ComponentProps export const ContextTrigger = ({ children, ...props }: ContextTriggerProps) => { const { usedTokens, maxTokens } = useContextValue() const usedPercent = usedTokens / maxTokens const renderedPercent = new Intl.NumberFormat("en-US", { style: "percent", maximumFractionDigits: 1, }).format(usedPercent) return ( {children ?? ( )} ) } export type ContextContentProps = ComponentProps export const ContextContent = ({ className, ...props }: ContextContentProps) => ( ) export type ContextContentHeaderProps = ComponentProps<"div"> export const ContextContentHeader = ({ children, className, ...props }: ContextContentHeaderProps) => { const { usedTokens, maxTokens } = useContextValue() const usedPercent = usedTokens / maxTokens const displayPct = new Intl.NumberFormat("en-US", { style: "percent", maximumFractionDigits: 1, }).format(usedPercent) const used = new Intl.NumberFormat("en-US", { notation: "compact", }).format(usedTokens) const total = new Intl.NumberFormat("en-US", { notation: "compact", }).format(maxTokens) return (
{children ?? ( <>

{displayPct}

{used} / {total}

)}
) } export type ContextContentBodyProps = ComponentProps<"div"> export const ContextContentBody = ({ children, className, ...props }: ContextContentBodyProps) => (
{children}
) export type ContextContentFooterProps = ComponentProps<"div"> export const ContextContentFooter = ({ children, className, ...props }: ContextContentFooterProps) => { const { modelId, usage } = useContextValue() const costUSD = modelId ? getUsage({ modelId, usage: { input: usage?.inputTokens ?? 0, output: usage?.outputTokens ?? 0, }, }).costUSD?.totalUSD : undefined const totalCost = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(costUSD ?? 0) return (
{children ?? ( <> Total cost {totalCost} )}
) } export type ContextInputUsageProps = ComponentProps<"div"> export const ContextInputUsage = ({ className, children, ...props }: ContextInputUsageProps) => { const { usage, modelId } = useContextValue() const inputTokens = usage?.inputTokens ?? 0 if (children) { return children } if (!inputTokens) { return null } const inputCost = modelId ? getUsage({ modelId, usage: { input: inputTokens, output: 0 }, }).costUSD?.totalUSD : undefined const inputCostText = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(inputCost ?? 0) return (
Input
) } export type ContextOutputUsageProps = ComponentProps<"div"> export const ContextOutputUsage = ({ className, children, ...props }: ContextOutputUsageProps) => { const { usage, modelId } = useContextValue() const outputTokens = usage?.outputTokens ?? 0 if (children) { return children } if (!outputTokens) { return null } const outputCost = modelId ? getUsage({ modelId, usage: { input: 0, output: outputTokens }, }).costUSD?.totalUSD : undefined const outputCostText = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(outputCost ?? 0) return (
Output
) } export type ContextReasoningUsageProps = ComponentProps<"div"> export const ContextReasoningUsage = ({ className, children, ...props }: ContextReasoningUsageProps) => { const { usage, modelId } = useContextValue() const reasoningTokens = usage?.reasoningTokens ?? 0 if (children) { return children } if (!reasoningTokens) { return null } const reasoningCost = modelId ? getUsage({ modelId, usage: { reasoningTokens }, }).costUSD?.totalUSD : undefined const reasoningCostText = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(reasoningCost ?? 0) return (
Reasoning
) } export type ContextCacheUsageProps = ComponentProps<"div"> export const ContextCacheUsage = ({ className, children, ...props }: ContextCacheUsageProps) => { const { usage, modelId } = useContextValue() const cacheTokens = usage?.cachedInputTokens ?? 0 if (children) { return children } if (!cacheTokens) { return null } const cacheCost = modelId ? getUsage({ modelId, usage: { cacheReads: cacheTokens, input: 0, output: 0 }, }).costUSD?.totalUSD : undefined const cacheCostText = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(cacheCost ?? 0) return (
Cache
) } const TokensWithCost = ({ tokens, costText }: { tokens?: number; costText?: string }) => ( {tokens === undefined ? "—" : new Intl.NumberFormat("en-US", { notation: "compact", }).format(tokens)} {costText ? • {costText} : null} ) /** Demo component for preview */ export default function ContextDemo() { return (
) }