Skip to content

Cache Savings Calculator — JavaScript source

See what prompt caching saves — uncached vs cached cost over N requests, with the write-premium break-even point.

This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.

/**
 * Cache Savings Calculator — uncached vs prompt-cached LLM cost comparison.
 *
 * Language: JavaScript (ES2022+, ES module; runs unmodified in Node 18+
 *           and modern browsers)
 * Source:   CosmoDev polyglot showcase port of the Cache Savings Calculator
 *           tool, ported from src/lib/cacheSavings.ts (the canonical
 *           TypeScript implementation).
 * Tool:     https://dev.cosmolabs.org/tools/cache-savings-calculator
 * License:  display source — part of CosmoDev's polyglot tool pages.
 *
 * Design goals:
 *   - Pure + deterministic; never throws.
 *   - Functionally equivalent to the TS reference: same inputs -> same outputs.
 *   - Self-contained: stdlib only (no npm dependencies).
 *
 * The TS original takes a full AiModel record but reads only its four pricing
 * rates, so this port narrows the parameter to exactly those fields. Any
 * missing (null) rate makes every output null — the caller renders an
 * explanatory empty state instead of partial math. All rates are per-1M-token
 * USD, mirroring the cost conventions of llmCost.ts.
 *
 * Reference vectors (fixture model 10 / 50 / 1 / 12.5 — see
 * cacheSavings.test.ts, the lock-step contract every port mirrors):
 *   10k in / 1k out / 5 hits -> uncached 0.75, cached 0.425, savings 0.325,
 *   43.333...% saved, break-even 13 hits. At 1 hit caching LOSES 0.035 (an
 *   honest negative saving). hits < 1 counts as 1. Zero tokens -> zero costs
 *   with 0%. cacheRead 0 -> breakEvenHits null (write premium never repaid).
 */

/**
 * The four per-1M-token USD pricing rates cacheMath reads from the TS AiModel.
 * @typedef {Object} ModelRates
 * @property {number|null} inputPerM      Uncached prompt (input) rate, USD per 1M tokens.
 * @property {number|null} outputPerM     Completion (output) rate, USD per 1M tokens.
 * @property {number|null} cacheReadPerM  Cached prompt read rate, USD per 1M tokens.
 * @property {number|null} cacheWritePerM Cache write premium rate, USD per 1M tokens.
 */

/**
 * Request shape.
 * @typedef {Object} CacheInput
 * @property {number} promptTokens  Prompt (input) tokens per request.
 * @property {number} outputTokens  Completion (output) tokens per request.
 * @property {number} hits          Requests reusing the cached prompt. Values < 1 are treated as 1.
 */

/**
 * Result shape. Any missing rate nulls every field.
 * @typedef {Object} CacheMath
 * @property {number|null} uncached      hits × (prompt·in$/M + output·out$/M) / 1e6.
 * @property {number|null} cached        (prompt·write$/M + hits × (prompt·read$/M + output·out$/M)) / 1e6 —
 *                                       one cache write, `hits` cache reads, output billed every request.
 * @property {number|null} savings       uncached − cached (negative when caching costs more).
 * @property {number|null} savingsPct    savings / uncached × 100; 0 when uncached is 0.
 * @property {number|null} breakEvenHits ceil(write$/M / read$/M) when read$/M > 0 — cache hits needed
 *                                       for cumulative READ spend to equal ONE write premium; null otherwise.
 */

/**
 * Compare uncached vs prompt-cached cost for one model.
 * @param {ModelRates} model
 * @param {CacheInput} input
 * @returns {CacheMath}
 */
export function cacheMath(model, input) {
  const { inputPerM: ipM, outputPerM: opM, cacheReadPerM: cr, cacheWritePerM: cw } = model;
  if (ipM === null || opM === null || cr === null || cw === null) {
    return { uncached: null, cached: null, savings: null, savingsPct: null, breakEvenHits: null };
  }

  const hits = Math.max(1, input.hits);
  const inT = input.promptTokens;
  const outT = input.outputTokens;

  // One cache write, `hits` cache reads; output tokens are billed on every request.
  const uncached = (hits * (inT * ipM + outT * opM)) / 1_000_000;
  const cached = (inT * cw + hits * (inT * cr + outT * opM)) / 1_000_000;
  const savings = uncached - cached;
  const savingsPct = uncached === 0 ? 0 : (savings / uncached) * 100;
  const breakEvenHits = cr > 0 ? Math.ceil(cw / cr) : null;

  return { uncached, cached, savings, savingsPct, breakEvenHits };
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →