Skip to content

Cache Savings Calculator — Go source

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

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

// Package cachingsavings is the Go twin of CosmoDev's src/lib/cacheSavings.ts
// (dual source: the web lib is TypeScript, the CLI lib is Go — kept in
// lock-step). All rates flow from the model snapshot accessor
// (cosmodev/aimodels) — never hardcoded here — mirroring the cost conventions
// of the llmcost twin (per-1M-token USD rates). Pure + deterministic, never
// panics: any missing rate nulls every field, mirroring the TS lib's nulled()
// empty state. The table-driven tests in cache-savings-calculator_test.go
// share vectors with src/lib/cacheSavings.test.ts so the two implementations
// are held to the same contract.
package cachingsavings

import (
	"math"

	"cosmodev/aimodels"
)

// CacheInput describes one cached workload. It mirrors the CacheInput
// interface in src/lib/cacheSavings.ts.
type CacheInput struct {
	PromptTokens int // prompt (input) tokens per request
	OutputTokens int // completion (output) tokens per request
	Hits         int // requests that reuse the cached prompt; values < 1 are treated as 1
}

// CacheMathResult is the uncached-vs-cached comparison for one model. Pointer
// fields are nil when the TS lib yields null: an unpriced model nulls every
// field, and BreakEvenHits alone is nil when the cache-read rate is 0. It
// mirrors the CacheMath interface in src/lib/cacheSavings.ts.
type CacheMathResult struct {
	Uncached      *float64 // hits × (prompt·in$/M + output·out$/M) / 1e6
	Cached        *float64 // (prompt·write$/M + hits × (prompt·read$/M + output·out$/M)) / 1e6 — one cache write, `hits` cache reads, output billed every request
	Savings       *float64 // uncached − cached (negative when caching costs more)
	SavingsPct    *float64 // savings / uncached × 100; 0 when uncached is 0
	BreakEvenHits *int     // ceil(write$/M / read$/M) when read$/M > 0 — cache hits needed for cumulative READ spend to equal ONE write premium; nil otherwise
}

// CacheMath compares uncached vs prompt-cached cost for one model. Any
// missing rate (input, output, cacheRead, cacheWrite) makes every field nil —
// the caller renders an explanatory empty state instead of partial math. It
// is the Go twin of cacheMath() in src/lib/cacheSavings.ts and must agree
// with it on every shared vector.
func CacheMath(m *aimodels.Model, input CacheInput) CacheMathResult {
	if m == nil || m.InputPerM == nil || m.OutputPerM == nil ||
		m.CacheReadPerM == nil || m.CacheWritePerM == nil {
		return CacheMathResult{}
	}
	ipM, opM := *m.InputPerM, *m.OutputPerM
	cr, cw := *m.CacheReadPerM, *m.CacheWritePerM

	hits := max(1, input.Hits)
	inT := float64(input.PromptTokens)
	outT := float64(input.OutputTokens)

	uncached := (float64(hits) * (inT*ipM + outT*opM)) / 1e6
	cached := (inT*cw + float64(hits)*(inT*cr+outT*opM)) / 1e6
	savings := uncached - cached
	savingsPct := 0.0
	if uncached != 0 {
		savingsPct = savings / uncached * 100
	}
	var breakEven *int
	if cr > 0 {
		n := int(math.Ceil(cw / cr))
		breakEven = &n
	}
	return CacheMathResult{
		Uncached:      &uncached,
		Cached:        &cached,
		Savings:       &savings,
		SavingsPct:    &savingsPct,
		BreakEvenHits: breakEven,
	}
}

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 →