/** * Per-model token pricing for cost estimation in analyzer_analyses.estimated_cost_usd. * * Rates are USD per 1,000,000 tokens. * * VERIFY QUARTERLY against: * - https://docs.claude.com/en/docs/about-claude/pricing * - https://openrouter.ai/api/v1/models (deepseek/* entries) * * Last verified: 2026-05-02 (V4 Pro/Flash + R1-0528 added from OpenRouter live list) */ import { HAIKU, SONNET, OPUS, DEEPSEEK_V4_FLASH, DEEPSEEK_V4_PRO, DEEPSEEK_R1, type ModelId, } from './models'; interface ModelRate { /** USD per 1M input tokens */ input: number; /** USD per 1M output tokens */ output: number; /** USD per 1M tokens read from prompt cache (~0.1× input on Anthropic; not applicable on OpenRouter — set equal to input). */ cacheRead: number; /** USD per 1M tokens written to 5-minute prompt cache (~1.25× input on Anthropic; not applicable on OpenRouter — set equal to input). */ cacheWrite5m: number; } export const PRICING: Record = { // Anthropic [HAIKU]: { input: 1.0, output: 5.0, cacheRead: 0.1, cacheWrite5m: 1.25 }, [SONNET]: { input: 3.0, output: 15.0, cacheRead: 0.3, cacheWrite5m: 3.75 }, [OPUS]: { input: 5.0, output: 25.0, cacheRead: 0.5, cacheWrite5m: 6.25 }, // OpenRouter / DeepSeek (no prompt-cache discount surfaced via the OpenAI- // compatible API; we treat cacheRead/cacheWrite as the input rate so the // estimator stays additive even if those usage fields ever come back filled). [DEEPSEEK_V4_FLASH]: { input: 0.14, output: 0.28, cacheRead: 0.14, cacheWrite5m: 0.14 }, [DEEPSEEK_V4_PRO]: { input: 0.435, output: 0.87, cacheRead: 0.435, cacheWrite5m: 0.435 }, [DEEPSEEK_R1]: { input: 0.50, output: 2.15, cacheRead: 0.50, cacheWrite5m: 0.50 }, }; export interface TokenUsage { input_tokens: number; output_tokens: number; cache_creation_input_tokens?: number; cache_read_input_tokens?: number; } /** * USD cost of a single LLM call given its token usage. Caches are billed * separately from raw input tokens — cache_read at ~0.1×, cache_write at ~1.25×. */ export function estimateCostUsd(model: ModelId, usage: TokenUsage): number { const rate = PRICING[model]; const cacheRead = usage.cache_read_input_tokens ?? 0; const cacheWrite = usage.cache_creation_input_tokens ?? 0; const uncachedInput = usage.input_tokens; // SDK reports this as the uncached remainder const cost = (uncachedInput / 1_000_000) * rate.input + (usage.output_tokens / 1_000_000) * rate.output + (cacheRead / 1_000_000) * rate.cacheRead + (cacheWrite / 1_000_000) * rate.cacheWrite5m; // Round to 4 decimals (matches analyzer_analyses.estimated_cost_usd numeric(10,4)). return Math.round(cost * 10_000) / 10_000; }