import type { ModelPricing, Usage } from "./types"; export interface CostBreakdown { inputUsd: number; cachedInputUsd: number; outputUsd: number; totalUsd: number; /** false when pricing is missing → totalUsd is 0 and should be shown as "n/a". */ known: boolean; } /** * Estimate the cost of one request. Reasoning tokens are billed as output tokens by * every current provider; cached input tokens are billed at the cached rate when known * (otherwise at the normal input rate). Long-context tiers apply when the prompt exceeds * the provider's threshold. */ export function estimateCost(usage: Usage | undefined | null, pricing: ModelPricing | null | undefined): CostBreakdown { if (!usage || !pricing || (pricing.inputPerMillion === undefined && pricing.outputPerMillion === undefined)) { return { inputUsd: 0, cachedInputUsd: 0, outputUsd: 0, totalUsd: 0, known: false }; } let inRate = pricing.inputPerMillion ?? 0; let outRate = pricing.outputPerMillion ?? 0; let cachedRate = pricing.cachedInputPerMillion ?? inRate; if (pricing.longContext && usage.inputTokens > pricing.longContext.thresholdTokens) { inRate = pricing.longContext.inputPerMillion ?? inRate; outRate = pricing.longContext.outputPerMillion ?? outRate; cachedRate = pricing.longContext.cachedInputPerMillion ?? cachedRate; } const cached = Math.min(usage.cachedInputTokens ?? 0, usage.inputTokens); const uncached = Math.max(0, usage.inputTokens - cached); const inputUsd = (uncached / 1_000_000) * inRate; const cachedInputUsd = (cached / 1_000_000) * cachedRate; const outputUsd = (usage.outputTokens / 1_000_000) * outRate; const totalUsd = inputUsd + cachedInputUsd + outputUsd; return { inputUsd, cachedInputUsd, outputUsd, totalUsd: round(totalUsd), known: true }; } function round(n: number): number { return Math.round(n * 1e8) / 1e8; } export function tokensPerSecond(outputTokens: number | undefined, latencyMs: number | undefined, ttftMs?: number | undefined): number | null { if (!outputTokens || !latencyMs) return null; const gen = Math.max(1, latencyMs - (ttftMs ?? 0)); return Math.round((outputTokens / gen) * 1000 * 10) / 10; }