pub fn api_cost_usd(model: &str, input_tokens: u64, output_tokens: u64) -> f64 {
let (input_price, output_price) = model_prices(model);
(input_tokens as f64 / 1_000_000.0) * input_price
+ (output_tokens as f64 / 1_000_000.0) * output_price
}
fn model_prices(model: &str) -> (f64, f64) {
if model.contains("fable") || model.contains("mythos") {
(10.0, 50.0)
} else if model.contains("opus-4-5") || model.contains("opus-4-6")
|| model.contains("opus-4-7") || model.contains("opus-4-8") {
(5.0, 25.0)
} else if model.contains("opus") {
(15.0, 75.0)
} else if model.contains("haiku") {
(1.0, 5.0)
} else if model.contains("sonnet") || model.is_empty() {
(3.0, 15.0)
} else if model.starts_with("gpt-4") || model.starts_with("o1") || model.starts_with("o3") || model.starts_with("gpt-5") {
(5.0, 15.0)
} else {
(3.0, 15.0)
}
}
pub fn fmt_cost(usd: f64) -> String {
if usd >= 10_000.0 {
format!("${:.0}k", usd / 1_000.0)
} else if usd >= 1_000.0 {
format!("${:.1}k", usd / 1_000.0)
} else if usd >= 1.0 {
format!("${:.2}", usd)
} else if usd >= 0.01 {
format!("${:.2}", usd)
} else if usd > 0.0 {
"<$0.01".to_owned()
} else {
"$0".to_owned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn model_prices_current_lineup() {
assert_eq!(model_prices("claude-fable-5"), (10.0, 50.0));
assert_eq!(model_prices("claude-mythos-5"), (10.0, 50.0));
assert_eq!(model_prices("claude-opus-4-8"), (5.0, 25.0));
assert_eq!(model_prices("claude-opus-4-7"), (5.0, 25.0));
assert_eq!(model_prices("claude-opus-4-6"), (5.0, 25.0));
assert_eq!(model_prices("claude-opus-4-1"), (15.0, 75.0));
assert_eq!(model_prices("claude-sonnet-4-6"), (3.0, 15.0));
assert_eq!(model_prices("claude-haiku-4-5-20251001"), (1.0, 5.0));
}
#[test]
fn api_cost_matches_rate() {
assert!((api_cost_usd("claude-opus-4-8", 1_000_000, 1_000_000) - 30.0).abs() < 1e-9);
}
}