ai-sdk-token-usage
Version:
A lightweight Typescript library to track and visualize token usage across multiple AI model providers.
284 lines (276 loc) • 11.7 kB
TypeScript
import { UIMessage, LanguageModelUsage } from 'ai';
/**
* Formats a numeric token count into a compact, human-readable string.
*
* Uses the built-in `Intl.NumberFormat` API with `"compact"` notation
* (e.g., `1K`, `2.5M`) and a U.S. English locale.
*
* @example
* ```ts
* formatTokenAmount(15230) // "15.2K"
* formatTokenAmount(1000000) // "1M"
* ```
*
* @param tokens - The number of tokens to format.
* @returns A compact, localized string representation of the token amount.
*/
declare function formatTokenAmount(tokens: number): string;
/**
* Formats a numeric value as a localized currency string.
*
* Uses the built-in `Intl.NumberFormat` API to format the given price
* in the specified currency. Defaults to USD if no currency code is provided.
*
* @example
* ```ts
* formatPrice(0.032) // "$0.03"
* formatPrice(1.2, "EUR") // "€1.20"
* ```
*
* @param price - The numeric value to format as a currency.
* @param currency - The ISO 4217 currency code (defaults to `"USD"`).
* @returns A localized currency string representing the formatted price.
*/
declare function formatPrice(price: number, currency?: string): string;
/**
* Represents the result state of an asynchronous token usage computation.
*
* This type follows the SWR-style pattern used throughout the library,
* providing a consistent structure for all React hooks in the package.
*
* @typeParam T - The type of the resolved data (e.g., {@link Cost}, {@link Context}, or {@link ModelDetails}).
*
* @property data - The resolved data, or `undefined` if still loading or an error occurred.
* @property isLoading - Indicates whether the data is still being loaded.
* @property error - A {@link TokenUsageError} object if an error occurred, otherwise `null`.
*/
type Result<T> = {
data: T | undefined;
isLoading: boolean;
error: TokenUsageError | null;
};
/**
* Represents an error that occurred in a hook.
*
* All errors surfaced by hooks such as {@link useTokenContext}, {@link useTokenCost},
* and {@link useModelDetails} are normalized to this structure. This allows a
* consistent error-handling interface across the library, regardless of the
* underlying error type or source.
*
* @property name - The name of the error (e.g., `"MissingMetadataError"`, `"ModelNotFoundError"`).
* @property message - A human-readable description of what went wrong.
* @property status - The numeric status code representing the error category (e.g., `400`, `404`, `500`).
* @property info - Additional structured information about the error, such as model IDs or provider details.
*/
type TokenUsageError = {
name: string;
message: string;
status: number;
info: unknown;
};
/**
* Represents a raw token usage breakdown across different token categories.
*
* @property input - Number of standard input tokens processed by the model.
* @property output - Number of output tokens generated by the model.
* @property reasoning - Number of reasoning tokens used (for models that support them).
* @property cachedInput - Number of cached input tokens, used when the provider offers token reuse.
*/
type Breakdown = {
input: number;
output: number;
reasoning: number;
cachedInput: number;
};
/**
* Represents the detailed cost breakdown across different token categories.
*
* Each property corresponds to a token type and includes both the number of
* tokens consumed (`amount`) and the monetary cost (`cost`) for that category.
*
* This structure mirrors {@link Breakdown}, but augments it with cost data,
* enabling detailed per-category cost analysis and visualization.
*
* @property input - Cost and token count for standard input tokens.
* @property output - Cost and token count for output tokens generated by the model.
* @property reasoning - Cost and token count for reasoning tokens (if supported by the model).
* @property cachedInput - Cost and token count for cached input tokens, when token reuse is supported.
*/
type CostBreakdown = {
input: {
amount: number;
cost: number;
};
output: {
amount: number;
cost: number;
};
reasoning: {
amount: number;
cost: number;
};
cachedInput: {
amount: number;
cost: number;
};
};
/**
* Represents the contextual token usage information for a model response.
*
* This structure quantifies how much of the model’s context window is used
* and how much remains, based on aggregated token usage data.
*
* It is returned by {@link useTokenContext} and used to visualize context
* consumption, detect truncation risks, and provide real-time feedback
* in chat or reporting interfaces.
*
* @property breakdown - Detailed token usage across input, output, reasoning, and cached input tokens.
* @property used - The total number of tokens used by the model in this context window.
* @property limit - The maximum number of tokens the model can process in its context window.
* @property remaining - The number of tokens still available before reaching the context limit.
* @property fractionUsed - The fraction of the context window that has been used (between `0` and `1`).
* @property percentageUsed - The percentage of the context window that has been used (between `0` and `100`).
* @property isExceeded - Whether the total token usage exceeds the model’s context window limit.
*/
type Context = {
breakdown: Breakdown;
used: number;
limit: number;
remaining: number;
fractionUsed: number;
percentageUsed: number;
isExceeded: boolean;
};
/**
* Represents the total monetary cost of token usage for a model response.
*
* This structure combines a detailed {@link CostBreakdown} with the aggregated
* total cost and the currency used for pricing. It provides a unified view of
* how much a model interaction costs, both overall and per token category.
*
* It is returned by {@link useTokenCost} and used to display or calculate
* pricing information in dashboards, analytics, or chat interfaces.
*
* @property breakdown - Detailed per-category cost and token count information.
* @property total - The total computed cost across all token categories.
* @property currency - The currency code for the total cost, currently fixed to `"USD"`.
*/
type Cost = {
breakdown: CostBreakdown;
total: number;
currency: "USD";
};
/**
* Represents the detailed configuration and limits of a specific model.
*
* This structure includes the model’s canonical identifier, per-token pricing,
* and token limits for both context and output. It provides the necessary data
* for computing token costs and for displaying model information in UIs.
*
* It is returned by {@link useModelDetails} and used internally by other hooks
* to resolve pricing, compute costs, and calculate remaining context window capacity.
*
* @property canonicalSlug - The canonical model identifier, combining provider and model ID
* (e.g., `"openai/gpt-5"`).
* @property pricing - The per-token pricing structure for input, output, reasoning, and cached input tokens.
* @property limit - The token limits for the model, defining both the total context window
* (`context`) and the maximum number of output tokens (`output`).
*/
type ModelDetails = {
canonicalSlug: string;
pricing: Breakdown;
limit: {
context: number;
output: number;
};
};
/**
* React hook that retrieves **model details**, such as pricing and token limits.
*
* The hook derives model information directly from the model registry based on
* the provided {@link canonicalSlug}. It returns structured data including
* per-token pricing (input, output, reasoning, cached input) and context/output
* token limits.
*
* Internally, the hook leverages SWR and follows the SWR-style return pattern with
* `data`, `isLoading`, and `error` states for consistent asynchronous handling.
*
* @param params - The parameters for the hook.
* @param params.canonicalSlug - The canonical model identifier, composed of provider and model ID
* (e.g. `"openai/gpt-5"`).
*
* @returns A {@link Result} object containing:
* - `data`: The resolved {@link ModelDetails}, including pricing and limits.
* - `isLoading`: Whether model data is still being loaded.
* - `error`: A {@link TokenUsageError} if an error occurred.
*/
declare function useModelDetails({ canonicalSlug }: {
canonicalSlug: string;
}): Result<ModelDetails>;
/**
* React hook that provides insight into how much of the model’s context window is remaining.
*
* The hook derives usage information directly from the message metadata and the model’s defined
* context window. It returns pre-computed values such as used and remaining tokens, percentage used,
* and whether the context limit has been exceeded.
*
* Internally, the hook leverages SWR and follows the SWR-style return pattern with
* `data`, `isLoading`, and `error` states for consistent asynchronous handling.
*
* @param params - The parameters for the hook.
* @param params.messages - The messages in the chat, typically returned from `useChat`.
* @param params.canonicalSlug - The canonical model identifier, composed of provider and model ID (e.g. `"openai/gpt-5"`).
*
* @returns A {@link Result} object containing:
* - `data`: The computed {@link Context} with usage metrics.
* - `isLoading`: Whether model data is still being loaded.
* - `error`: A {@link TokenUsageError} if an error occurred.
*/
declare function useTokenContext({ messages, canonicalSlug, }: {
messages: readonly UIMessage[];
canonicalSlug: string;
}): Result<Context>;
/**
* React hook that computes the **monetary cost** of token usage for assistant messages.
*
* The hook derives cost directly from message metadata and the model’s pricing
* information. It returns pre-computed values such as the total cost, a detailed
* cost breakdown per token type (input, output, reasoning, cached input), and the
* currency.
*
* Internally, the hook leverages SWR and follows the SWR-style return pattern with
* `data`, `isLoading`, and `error` states for consistent asynchronous handling.
*
* @param params - The parameters for the hook. Exactly one of the following must be provided:
* - `messages`: All chat messages (typically returned from `useChat`).
* - `message`: A single assistant message.
*
* @returns A {@link Result} object containing:
* - `data`: The computed {@link Cost} with `breakdown`, `total`, and `currency`.
* - `isLoading`: Whether model pricing data is still being loaded.
* - `error`: A {@link TokenUsageError} if an error occurred.
*/
declare function useTokenCost({ messages }: {
messages: readonly UIMessage[];
}): Result<Cost>;
declare function useTokenCost({ message }: {
message: UIMessage;
}): Result<Cost>;
/**
* Represents the token usage metadata attached to a message.
*
* This metadata is generated by AI SDK Token Usage and includes
* both the total token usage reported by the model and the
* canonical model identifier.
*
* Used by the `messageMetadata` callback in
* `toUIMessageStreamResponse` to store cost and usage information
* for each completed model response.
*/
type TokenUsageMetadata = {
/** The total token usage for the model response, as reported by the provider. */
totalUsage: LanguageModelUsage;
/** The canonical model identifier (e.g., "openai/gpt-5"). */
canonicalSlug: string;
};
export { type Breakdown, type Context, type Cost, type CostBreakdown, type ModelDetails, type Result, type TokenUsageError, type TokenUsageMetadata, formatPrice, formatTokenAmount, useModelDetails, useTokenContext, useTokenCost };