{"version":3,"sources":["../src/lib/cost-estimator.ts"],"names":[],"mappings":"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","file":"chunk-O272W6MO.cjs","sourcesContent":["/**\n * Cost estimator — live pricing from OpenRouter (primary) + LiteLLM (fallback)\n * with a 24h localStorage cache and a hardcoded offline fallback.\n *\n * Data sources:\n *   • https://openrouter.ai/api/v1/models (367+ models, CORS-clean, USD/token)\n *   • https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json\n *     (2700+ entries, CORS-clean, input_cost_per_token/output_cost_per_token)\n *\n * Normalization: Bedrock IDs like `global.anthropic.claude-opus-4-7` are\n * stripped of the region/profile prefix before matching.\n */\n\ntype PriceRow = { input: number; output: number; context?: number }\n\nconst CACHE_KEY = 'careless-pricing-cache-v1'\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000  // 24h\nconst OPENROUTER_URL = 'https://openrouter.ai/api/v1/models'\nconst LITELLM_URL =\n  'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json'\n\n/** In-memory parsed table: normalized-model-id → price row. Populated lazily. */\nlet priceTable: Record<string, PriceRow> = {}\nlet tableReady = false\nlet fetchInFlight: Promise<void> | null = null\n\n/* ── Offline fallback (USD per 1M tokens) ──────────────────────────────── */\nconst FALLBACK_PRICES: Record<string, PriceRow> = {\n  // Anthropic\n  'claude-opus-4-20250514': { input: 15, output: 75, context: 200_000 },\n  'claude-sonnet-4-20250514': { input: 3, output: 15, context: 200_000 },\n  'claude-opus-4-7': { input: 15, output: 75, context: 200_000 },\n  'claude-opus-4-6': { input: 15, output: 75, context: 200_000 },\n  'claude-sonnet-4-6': { input: 3, output: 15, context: 200_000 },\n  'claude-sonnet-4-5': { input: 3, output: 15, context: 200_000 },\n  'claude-haiku-4-5': { input: 1, output: 5, context: 200_000 },\n  'claude-3-5-haiku-20241022': { input: 0.8, output: 4, context: 200_000 },\n  // OpenAI\n  'gpt-4o': { input: 2.5, output: 10, context: 128_000 },\n  'gpt-4o-mini': { input: 0.15, output: 0.6, context: 128_000 },\n  'gpt-5': { input: 2, output: 10, context: 400_000 },\n  'gpt-5.5': { input: 2, output: 10, context: 400_000 },\n  'gpt-5.5-2026-04-23': { input: 2, output: 10, context: 400_000 },\n  'gpt-5.2-2025-12-11': { input: 1.5, output: 7.5, context: 256_000 },\n  'gpt-5-mini-2025-10-14': { input: 0.3, output: 1.2, context: 128_000 },\n  'o1-mini': { input: 3, output: 12, context: 128_000 },\n  // Google\n  'gemini-2.5-flash': { input: 0.3, output: 2.5, context: 1_000_000 },\n  'gemini-2.5-pro': { input: 1.25, output: 10, context: 2_000_000 },\n}\n\n/** Default row for unknown models (conservative mid-tier) */\nconst DEFAULT_ROW: PriceRow = { input: 3, output: 15, context: 128_000 }\n\n/* ── Normalization ────────────────────────────────────────────────────── */\n\n/**\n * Strip region/profile prefixes and return a lowercase canonical id.\n * Examples:\n *   global.anthropic.claude-opus-4-7      → claude-opus-4-7\n *   us.anthropic.claude-sonnet-4-20250514 → claude-sonnet-4-20250514\n *   anthropic/claude-opus-4.7             → claude-opus-4.7\n *   openai/gpt-4o                         → gpt-4o\n */\nfunction normalizeModelId(id: string): string {\n  if (!id) return ''\n  let m = id.toLowerCase().trim()\n  // Bedrock prefixes: global. / us. / eu. / apac. / anthropic.\n  m = m.replace(/^(global|us|eu|apac)\\./, '')\n  m = m.replace(/^anthropic\\./, '')\n  // OpenRouter/LiteLLM `provider/model` prefixes\n  m = m.replace(/^(anthropic|openai|google|meta|mistralai|deepseek|xai|cohere|perplexity)\\//, '')\n  return m\n}\n\n/** Do these two normalized ids refer to the same model? */\nfunction idMatches(needle: string, haystack: string): boolean {\n  if (needle === haystack) return true\n  // Either is a dotted/dashed substring of the other (e.g. claude-opus-4 vs claude-opus-4-7)\n  return haystack.includes(needle) || needle.includes(haystack)\n}\n\n/* ── Fetch + parse ─────────────────────────────────────────────────────── */\n\nasync function fetchOpenRouter(): Promise<Record<string, PriceRow>> {\n  const res = await fetch(OPENROUTER_URL, { cache: 'no-cache' })\n  if (!res.ok) throw new Error(`openrouter ${res.status}`)\n  const data = await res.json() as {\n    data: Array<{\n      id: string\n      context_length?: number\n      pricing?: { prompt?: string; completion?: string }\n    }>\n  }\n  const out: Record<string, PriceRow> = {}\n  for (const m of data.data || []) {\n    const prompt = parseFloat(m.pricing?.prompt || '0')\n    const completion = parseFloat(m.pricing?.completion || '0')\n    if (!Number.isFinite(prompt) || !Number.isFinite(completion)) continue\n    const nid = normalizeModelId(m.id)\n    if (!nid) continue\n    out[nid] = {\n      input: prompt * 1_000_000,        // USD/token → USD per 1M tokens\n      output: completion * 1_000_000,\n      context: m.context_length,\n    }\n  }\n  return out\n}\n\nasync function fetchLiteLLM(): Promise<Record<string, PriceRow>> {\n  const res = await fetch(LITELLM_URL, { cache: 'no-cache' })\n  if (!res.ok) throw new Error(`litellm ${res.status}`)\n  const data = await res.json() as Record<string, {\n    input_cost_per_token?: number\n    output_cost_per_token?: number\n    max_input_tokens?: number\n    max_tokens?: number\n    mode?: string\n  }>\n  const out: Record<string, PriceRow> = {}\n  for (const [id, row] of Object.entries(data)) {\n    if (row.mode && row.mode !== 'chat' && row.mode !== 'completion') continue\n    const inp = row.input_cost_per_token\n    const outp = row.output_cost_per_token\n    if (typeof inp !== 'number' || typeof outp !== 'number') continue\n    const nid = normalizeModelId(id)\n    if (!nid) continue\n    out[nid] = {\n      input: inp * 1_000_000,\n      output: outp * 1_000_000,\n      context: row.max_input_tokens || row.max_tokens,\n    }\n  }\n  return out\n}\n\n/* ── Cache ─────────────────────────────────────────────────────────────── */\n\nfunction loadCache(): Record<string, PriceRow> | null {\n  try {\n    const raw = localStorage.getItem(CACHE_KEY)\n    if (!raw) return null\n    const parsed = JSON.parse(raw) as { ts: number; table: Record<string, PriceRow> }\n    if (!parsed.ts || Date.now() - parsed.ts > CACHE_TTL_MS) return null\n    return parsed.table\n  } catch { return null }\n}\n\nfunction saveCache(table: Record<string, PriceRow>) {\n  try {\n    localStorage.setItem(CACHE_KEY, JSON.stringify({ ts: Date.now(), table }))\n  } catch { /* quota, private mode, etc. */ }\n}\n\n/** Fetch both sources concurrently; merge (OpenRouter wins on conflict). */\nasync function refreshPriceTable(): Promise<void> {\n  if (fetchInFlight) return fetchInFlight\n  fetchInFlight = (async () => {\n    const [orRes, llRes] = await Promise.allSettled([fetchOpenRouter(), fetchLiteLLM()])\n    const merged: Record<string, PriceRow> = { ...FALLBACK_PRICES }\n    if (llRes.status === 'fulfilled') Object.assign(merged, llRes.value)\n    if (orRes.status === 'fulfilled') Object.assign(merged, orRes.value)\n    if (orRes.status === 'rejected' && llRes.status === 'rejected') {\n      console.warn('[cost-estimator] both pricing sources failed, using fallback table')\n    }\n    priceTable = merged\n    tableReady = true\n    saveCache(merged)\n  })()\n  try { await fetchInFlight } finally { fetchInFlight = null }\n}\n\n/** Boot the price table: cache → immediate; network → 24h refresh. */\nexport function initPricing(): void {\n  const cached = loadCache()\n  if (cached) {\n    priceTable = cached\n    tableReady = true\n  } else {\n    priceTable = { ...FALLBACK_PRICES }\n    tableReady = true  // fallback is fine to use synchronously\n  }\n  // Refresh in background regardless (stale-while-revalidate)\n  refreshPriceTable().catch(() => { /* already logged */ })\n}\n\n// Auto-init on first import (browser only).\nif (typeof window !== 'undefined') {\n  try { initPricing() } catch { /* noop */ }\n}\n\n/* ── Lookup ────────────────────────────────────────────────────────────── */\n\nfunction lookup(model: string): PriceRow {\n  const nid = normalizeModelId(model)\n  if (!nid) return DEFAULT_ROW\n  // Exact match first\n  const table = tableReady ? priceTable : FALLBACK_PRICES\n  if (table[nid]) return table[nid]\n  // Fuzzy: longest matching key wins (so 'claude-opus-4-7' beats 'claude-opus-4')\n  let best: PriceRow | null = null\n  let bestLen = 0\n  for (const k of Object.keys(table)) {\n    if (idMatches(nid, k) && k.length > bestLen) {\n      best = table[k]\n      bestLen = k.length\n    }\n  }\n  return best || DEFAULT_ROW\n}\n\n/* ── Public API ────────────────────────────────────────────────────────── */\n\n/** Estimate USD cost for a turn. */\nexport function estimateCost(model: string, inputTokens: number, outputTokens: number): number {\n  const row = lookup(model)\n  return (inputTokens * row.input + outputTokens * row.output) / 1_000_000\n}\n\n/** Get the context window size for a model (defaults to 128k). */\nexport function getContextWindow(model: string): number {\n  return lookup(model).context ?? 128_000\n}\n\nexport function formatCost(usd: number): string {\n  if (!Number.isFinite(usd) || usd <= 0) return '$0.00'\n  if (usd < 0.0001) return '<$0.0001'\n  if (usd < 0.01) return `$${usd.toFixed(4)}`\n  if (usd < 1) return `$${usd.toFixed(3)}`\n  return `$${usd.toFixed(2)}`\n}\n\n/** Return per-1M-token input/output prices (for display, e.g. Settings tab). */\nexport function getPriceRow(model: string): PriceRow {\n  return lookup(model)\n}\n\n/** Force-refresh the pricing table (skips cache). Useful for \"Update\" buttons. */\nexport async function forceRefreshPricing(): Promise<void> {\n  try { localStorage.removeItem(CACHE_KEY) } catch { /* noop */ }\n  await refreshPriceTable()\n}\n\n/** Is the pricing table ready to answer lookups? */\nexport function isPricingReady(): boolean { return tableReady }\n\n/** How many entries are in the live table (useful for diagnostics). */\nexport function getPricingEntryCount(): number { return Object.keys(priceTable).length }\n"]}