package ledger import ( "math" "testing" "textmachine/backend/internal/llm" ) func approx(t *testing.T, got, want float64, msg string) { t.Helper() if math.Abs(got-want) > 1e-12 { t.Fatalf("%s: got %v, want %v", msg, got, want) } } // The money formula — the core of the unit economics; we check every term, including // the new one (cache write), and the degradation to the vojo formula without cache fields. func TestCostUSD(t *testing.T) { price := ModelPrice{InputPerM: 2.0, CachedPerM: 0.2, CacheWritePerM: 2.5, OutputPerM: 10.0} // Anthropic-style call: 10k of total input = 6k uncached + 3k cache-read + // 1k cache-write; 2k output. u := llm.Usage{PromptTokens: 10_000, CachedTokens: 3_000, CacheCreationTokens: 1_000, CompletionTokens: 2_000} want := 6_000*2.0/1e6 + 3_000*0.2/1e6 + 1_000*2.5/1e6 + 2_000*10.0/1e6 approx(t, CostUSD(price, u), want, "anthropic-style usage") // OpenAI-compatible call without cache fields — the vojo formula. u2 := llm.Usage{PromptTokens: 10_000, CompletionTokens: 2_000} approx(t, CostUSD(price, u2), 10_000*2.0/1e6+2_000*10.0/1e6, "plain usage") // Reasoning tokens are billed at the output rate ON TOP OF completion (xAI). u3 := llm.Usage{PromptTokens: 1_000, CompletionTokens: 500, ReasoningTokens: 1_500} approx(t, CostUSD(price, u3), 1_000*2.0/1e6+2_000*10.0/1e6, "additive reasoning") // Guard against a provider that reported cached > prompt: the uncached term // is clamped to 0 rather than going negative. u4 := llm.Usage{PromptTokens: 100, CachedTokens: 500, CompletionTokens: 10} want4 := 0*2.0/1e6 + 500*0.2/1e6 + 10*10.0/1e6 approx(t, CostUSD(price, u4), want4, "cached > prompt guard") // Negative usage numbers (a provider accounting bug) must not REDUCE // the cost — every term is clamped to ≥0. u5 := llm.Usage{PromptTokens: -1000, CachedTokens: -50, CompletionTokens: -200, ReasoningTokens: -5} if got := CostUSD(price, u5); got != 0 { t.Fatalf("negative usage must never reduce cost below 0, got %v", got) } } func TestPriceForResponse(t *testing.T) { def := ModelPrice{InputPerM: 0.14, OutputPerM: 0.28} // cheap anchor p, err := NewPricer(map[string]ModelPrice{ "deepseek-v4-flash": def, "claude-sonnet-5": {InputPerM: 2.0, OutputPerM: 10.0}, }, def) if err != nil { t.Fatal(err) } // The model that actually responded is known — price by it. if p.PriceForResponse("claude-sonnet-5", "claude-sonnet-5").InputPerM != 2.0 { t.Fatal("known actual model must price by actual") } // Provider returned a dated slug of a premium model — NOT the cheap anchor, but // the price of the REQUESTED model (otherwise a systematic under-accounting of the premium). if got := p.PriceForResponse("claude-sonnet-5", "claude-sonnet-5-20260101").InputPerM; got != 2.0 { t.Fatalf("unknown actual must fall back to requested model price (2.0), got %v", got) } // Both unknown — the default anchor (never $0). if got := p.PriceForResponse("mystery", "also-mystery").InputPerM; got != 0.14 { t.Fatalf("both unknown must fall back to default anchor, got %v", got) } } func TestPricerNeverZero(t *testing.T) { def := ModelPrice{InputPerM: 1, OutputPerM: 2} p, err := NewPricer(map[string]ModelPrice{"known": {InputPerM: 5, CachedPerM: 1, OutputPerM: 10}}, def) if err != nil { t.Fatal(err) } if got := p.PriceFor("known").InputPerM; got != 5 { t.Fatalf("known model price: got %v", got) } // Unknown model (the actual one after a failover) — the default anchor, not $0. if got := p.PriceFor("mystery-model").InputPerM; got != 1 { t.Fatalf("unknown model must fall back to default price, got %v", got) } // A $0 default would blind the ceilings — the constructor must reject it. if _, err := NewPricer(nil, ModelPrice{}); err == nil { t.Fatal("zero default price must be rejected") } } func TestEstimateUSDIsPessimistic(t *testing.T) { price := ModelPrice{InputPerM: 2.0, CachedPerM: 0.2, OutputPerM: 10.0} est := EstimateUSD(price, 1_000, 2_000, 0) approx(t, est, 1_000*2.0/1e6+2_000*10.0/1e6, "estimate") // A real call with a cache hit must cost no more than the estimate — otherwise // settle would inflate committed above a ceiling that was let through on the tolerance. real := CostUSD(price, llm.Usage{PromptTokens: 1_000, CachedTokens: 900, CompletionTokens: 2_000}) if real > est { t.Fatalf("real cost %v exceeds reservation estimate %v", real, est) } } // TestEstimateUSDAdditiveReasoningBuffer covers D13.6: the additive-reasoning buffer is // reserved as output tokens, so an xAI-style think-ON call whose reasoning lands on top of // completion no longer overshoots the reservation and blinds the ceiling. func TestEstimateUSDAdditiveReasoningBuffer(t *testing.T) { price := ModelPrice{InputPerM: 2.0, OutputPerM: 10.0} base := EstimateUSD(price, 1_000, 2_000, 0) withBuf := EstimateUSD(price, 1_000, 2_000, 3_000) // The buffer adds exactly 3_000 output-priced tokens over the base estimate. approx(t, withBuf-base, 3_000*10.0/1e6, "reasoning buffer term") // A real additive call (completion 2_000 + reasoning 2_500 ≤ buffer 3_000) stays within. real := CostUSD(price, llm.Usage{PromptTokens: 1_000, CompletionTokens: 2_000, ReasoningTokens: 2_500}) if real > withBuf { t.Fatalf("additive real cost %v exceeds buffered reservation %v", real, withBuf) } if real <= base { t.Fatalf("without the buffer the reservation %v would be blind to the reasoning spend (real %v)", base, real) } }