use crate::prelude::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelPricing {
pub model_pattern: String,
pub input_per_mtok: f64,
pub output_per_mtok: f64,
pub cache_creation_per_mtok: f64,
pub cache_read_per_mtok: f64,
}
pub const FALLBACK_VERSION: &str = "2026-04-13";
pub fn fallback_pricing() -> Vec<ModelPricing> {
vec![
ModelPricing {
model_pattern: "claude-sonnet-4-6".to_string(),
input_per_mtok: 3.0,
output_per_mtok: 15.0,
cache_creation_per_mtok: 3.75,
cache_read_per_mtok: 0.30,
},
ModelPricing {
model_pattern: "claude-opus-4-6".to_string(),
input_per_mtok: 5.0,
output_per_mtok: 25.0,
cache_creation_per_mtok: 6.25,
cache_read_per_mtok: 0.50,
},
ModelPricing {
model_pattern: "claude-haiku-4-5-20251001".to_string(),
input_per_mtok: 1.0,
output_per_mtok: 5.0,
cache_creation_per_mtok: 1.25,
cache_read_per_mtok: 0.10,
},
ModelPricing {
model_pattern: "gpt-5.4".to_string(),
input_per_mtok: 2.50,
output_per_mtok: 15.0,
cache_creation_per_mtok: 0.0,
cache_read_per_mtok: 0.0,
},
ModelPricing {
model_pattern: "gpt-5.2-codex".to_string(),
input_per_mtok: 1.75,
output_per_mtok: 14.0,
cache_creation_per_mtok: 0.0,
cache_read_per_mtok: 0.0,
},
ModelPricing {
model_pattern: "gpt-5.3-codex".to_string(),
input_per_mtok: 1.75,
output_per_mtok: 14.0,
cache_creation_per_mtok: 0.0,
cache_read_per_mtok: 0.0,
},
ModelPricing {
model_pattern: "gpt-5.4-mini".to_string(),
input_per_mtok: 0.75,
output_per_mtok: 4.50,
cache_creation_per_mtok: 0.0,
cache_read_per_mtok: 0.0,
},
ModelPricing {
model_pattern: "gpt-5.4-nano".to_string(),
input_per_mtok: 0.20,
output_per_mtok: 1.25,
cache_creation_per_mtok: 0.0,
cache_read_per_mtok: 0.0,
},
ModelPricing {
model_pattern: "claude-sonnet-4-20250514".to_string(),
input_per_mtok: 3.0,
output_per_mtok: 15.0,
cache_creation_per_mtok: 3.75,
cache_read_per_mtok: 0.30,
},
ModelPricing {
model_pattern: "claude-sonnet-4-5-20250514".to_string(),
input_per_mtok: 3.0,
output_per_mtok: 15.0,
cache_creation_per_mtok: 3.75,
cache_read_per_mtok: 0.30,
},
ModelPricing {
model_pattern: "claude-opus-4-20250514".to_string(),
input_per_mtok: 15.0,
output_per_mtok: 75.0,
cache_creation_per_mtok: 18.75,
cache_read_per_mtok: 1.50,
},
ModelPricing {
model_pattern: "claude-haiku-3-5-20241022".to_string(),
input_per_mtok: 0.80,
output_per_mtok: 4.0,
cache_creation_per_mtok: 1.0,
cache_read_per_mtok: 0.08,
},
]
}
pub fn find_pricing<'a>(model: &str, prices: &'a [ModelPricing]) -> Option<&'a ModelPricing> {
for pricing in prices {
if model.contains(&pricing.model_pattern) {
return Some(pricing);
}
let pattern_lower = pricing.model_pattern.to_lowercase();
let model_lower = model.to_lowercase();
if model_lower.contains(&pattern_lower) {
return Some(pricing);
}
}
None
}
pub fn calculate_cost(
input_tokens: i64,
output_tokens: i64,
cache_creation_tokens: i64,
cache_read_tokens: i64,
pricing: &ModelPricing,
) -> f64 {
let input_cost = (input_tokens as f64 / 1_000_000.0) * pricing.input_per_mtok;
let output_cost = (output_tokens as f64 / 1_000_000.0) * pricing.output_per_mtok;
let cache_creation_cost =
(cache_creation_tokens as f64 / 1_000_000.0) * pricing.cache_creation_per_mtok;
let cache_read_cost = (cache_read_tokens as f64 / 1_000_000.0) * pricing.cache_read_per_mtok;
input_cost + output_cost + cache_creation_cost + cache_read_cost
}