package llm import ( "fmt" "strconv" ) // Price is per-million-token list pricing. type Price struct { InputPerMTok float64 OutputPerMTok float64 } // Cache multipliers relative to the input rate. const ( cacheReadMultiplier = 0.1 cacheWriteMultiplier = 1.25 ) // prices covers the models this harness is likely to be pointed at. A model // that is not listed reports no cost rather than a wrong one. var prices = map[string]Price{ "claude-opus-5": {InputPerMTok: 5, OutputPerMTok: 25}, "claude-opus-4-8": {InputPerMTok: 5, OutputPerMTok: 25}, "claude-opus-4-7": {InputPerMTok: 5, OutputPerMTok: 25}, "claude-sonnet-5": {InputPerMTok: 2, OutputPerMTok: 10}, "claude-haiku-4-5": {InputPerMTok: 1, OutputPerMTok: 5}, "claude-fable-5-1": {InputPerMTok: 10, OutputPerMTok: 50}, "claude-fable-5": {InputPerMTok: 10, OutputPerMTok: 50}, } // Cost prices one usage record. The boolean reports whether the model's // pricing is known; a made-up number in METRICS.md is worse than a blank. func (u Usage) Cost(model string) (float64, bool) { p, ok := prices[model] if !ok { return 0, false } return (float64(u.InputTokens)*p.InputPerMTok + float64(u.OutputTokens)*p.OutputPerMTok + float64(u.CacheReadInputTokens)*p.InputPerMTok*cacheReadMultiplier + float64(u.CacheCreationInputTokens)*p.InputPerMTok*cacheWriteMultiplier) / 1_000_000, true } // PriceLabel renders a model's list pricing as the wizard shows it: // "$5 / $25 per Mtok", input then output. A model with no known pricing gets // an empty label rather than a zero, because "$0" beside a frontier model is // a worse answer than saying nothing. func PriceLabel(model string) string { p, ok := prices[model] if !ok { return "" } // Shortest representation that round-trips: the whole-dollar rates read // as "$5" rather than "$5.00", and a fractional one keeps its decimals. num := func(f float64) string { return strconv.FormatFloat(f, 'f', -1, 64) } return fmt.Sprintf("$%s / $%s per Mtok", num(p.InputPerMTok), num(p.OutputPerMTok)) }