use crate::core::cost::types::{CostError, ModelPricing};
mod regional;
pub(super) use regional::{get_minimax_pricing, get_zhipu_pricing};
pub(super) fn get_openai_pricing(model: &str) -> Result<ModelPricing, CostError> {
use chrono::Utc;
let pricing = match model.to_lowercase().as_str() {
m if m.contains("gpt-5.5-pro") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.030,
output_cost_per_1k_tokens: 0.180,
cache_read_input_token_cost: Some(0.030),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.5") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.030,
cache_read_input_token_cost: Some(0.0005),
tiered_pricing: Some(std::collections::HashMap::from([
("input_cost_per_token_above_272k_tokens".to_string(), 0.010),
("output_cost_per_token_above_272k_tokens".to_string(), 0.045),
(
"cache_read_input_token_cost_above_272k_tokens".to_string(),
0.001,
),
])),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.4-pro") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.030,
output_cost_per_1k_tokens: 0.180,
cache_read_input_token_cost: Some(0.030),
tiered_pricing: Some(std::collections::HashMap::from([
("input_cost_per_token_above_272k_tokens".to_string(), 0.060),
("output_cost_per_token_above_272k_tokens".to_string(), 0.270),
(
"cache_read_input_token_cost_above_272k_tokens".to_string(),
0.060,
),
])),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.4-mini") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00075,
output_cost_per_1k_tokens: 0.0045,
cache_read_input_token_cost: Some(0.000075),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.4-nano") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0002,
output_cost_per_1k_tokens: 0.00125,
cache_read_input_token_cost: Some(0.00002),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.4") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0025,
output_cost_per_1k_tokens: 0.015,
cache_read_input_token_cost: Some(0.00025),
tiered_pricing: Some(std::collections::HashMap::from([
("input_cost_per_token_above_272k_tokens".to_string(), 0.005),
(
"output_cost_per_token_above_272k_tokens".to_string(),
0.0225,
),
(
"cache_read_input_token_cost_above_272k_tokens".to_string(),
0.0005,
),
])),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.2-pro") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.021,
output_cost_per_1k_tokens: 0.168,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.2-codex") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00175,
output_cost_per_1k_tokens: 0.014,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5-codex") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00125,
output_cost_per_1k_tokens: 0.010,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.2") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00175,
output_cost_per_1k_tokens: 0.014,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.1-thinking") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0025,
output_cost_per_1k_tokens: 0.020,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5.1") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00125,
output_cost_per_1k_tokens: 0.010,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5-mini") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00025,
output_cost_per_1k_tokens: 0.002,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-5-nano") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00005,
output_cost_per_1k_tokens: 0.0004,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-image-2") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.030,
image_cost_per_token: Some(0.000008),
cache_read_input_token_cost: Some(0.00125),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-image-1-mini") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.002,
output_cost_per_1k_tokens: 0.008,
image_cost_per_token: Some(0.0000025),
cache_read_input_token_cost: Some(0.0002),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-image-1.5") || m.contains("chatgpt-image-latest") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.032,
image_cost_per_token: Some(0.000008),
cache_read_input_token_cost: Some(0.00125),
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-image-1") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.020,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("computer-use-preview") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.003,
output_cost_per_1k_tokens: 0.012,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("o3-pro") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.020,
output_cost_per_1k_tokens: 0.080,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("o3-deep-research") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.010,
output_cost_per_1k_tokens: 0.040,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("o3-mini") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0011,
output_cost_per_1k_tokens: 0.0044,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("o3") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.002,
output_cost_per_1k_tokens: 0.008,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("o4-mini-deep-research") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.002,
output_cost_per_1k_tokens: 0.008,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("o4-mini") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0011,
output_cost_per_1k_tokens: 0.0044,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-audio-1.5") || m == "gpt-audio" => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0025,
output_cost_per_1k_tokens: 0.010,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-audio-mini") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0006,
output_cost_per_1k_tokens: 0.0024,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-realtime-1.5") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.004,
output_cost_per_1k_tokens: 0.016,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-realtime-mini") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0006,
output_cost_per_1k_tokens: 0.0024,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-4.1") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.002,
output_cost_per_1k_tokens: 0.008,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-4o-mini") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00015,
output_cost_per_1k_tokens: 0.0006,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-4o") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.015,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-4-turbo") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.01,
output_cost_per_1k_tokens: 0.03,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gpt-3.5-turbo") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0005,
output_cost_per_1k_tokens: 0.0015,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
_ => {
return Err(CostError::ModelNotSupported {
model: model.to_string(),
provider: "openai".to_string(),
});
}
};
Ok(pricing)
}
pub(super) fn get_anthropic_pricing(model: &str) -> Result<ModelPricing, CostError> {
use chrono::Utc;
let pricing = match model.to_lowercase().as_str() {
m if m.contains("claude-opus-4-8") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.025,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-opus-4-7") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.025,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-opus-4-6") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.025,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-opus-4-5") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.005,
output_cost_per_1k_tokens: 0.025,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-opus-4-1") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.015,
output_cost_per_1k_tokens: 0.075,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-opus-4") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.015,
output_cost_per_1k_tokens: 0.075,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-sonnet-4-5") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.003,
output_cost_per_1k_tokens: 0.015,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-sonnet-4") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.003,
output_cost_per_1k_tokens: 0.015,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-3-5-sonnet") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.003,
output_cost_per_1k_tokens: 0.015,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-3-5-haiku") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.001,
output_cost_per_1k_tokens: 0.005,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-haiku-4-5") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.001,
output_cost_per_1k_tokens: 0.005,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-3-opus") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.015,
output_cost_per_1k_tokens: 0.075,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-3-sonnet") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.003,
output_cost_per_1k_tokens: 0.015,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-3-haiku") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00025,
output_cost_per_1k_tokens: 0.00125,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-2.1") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.008,
output_cost_per_1k_tokens: 0.024,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("claude-instant") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0008,
output_cost_per_1k_tokens: 0.0024,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
_ => {
return Err(CostError::ModelNotSupported {
model: model.to_string(),
provider: "anthropic".to_string(),
});
}
};
Ok(pricing)
}
pub(crate) fn get_azure_pricing(model: &str) -> Result<ModelPricing, CostError> {
get_openai_pricing(model).map(|mut pricing| {
pricing.model = model.to_string();
pricing
})
}
pub(super) fn get_vertex_ai_pricing(model: &str) -> Result<ModelPricing, CostError> {
use chrono::Utc;
let pricing = match model.to_lowercase().as_str() {
m if m.contains("gemini-pro") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00125,
output_cost_per_1k_tokens: 0.00375,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
m if m.contains("gemini-flash") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.000075,
output_cost_per_1k_tokens: 0.0003,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
},
_ => {
return Err(CostError::ModelNotSupported {
model: model.to_string(),
provider: "vertex_ai".to_string(),
});
}
};
Ok(pricing)
}
pub(super) fn get_deepseek_pricing(model: &str) -> Result<ModelPricing, CostError> {
get_deepseek_pricing_at(model, chrono::Utc::now())
}
pub(super) fn get_deepseek_pricing_at(
model: &str,
pricing_time: chrono::DateTime<chrono::Utc>,
) -> Result<ModelPricing, CostError> {
let multiplier = if crate::core::pricing::time_of_use::is_deepseek_v4_peak_at(pricing_time) {
2.0
} else {
1.0
};
let pricing = match model.to_lowercase().as_str() {
m if m.contains("deepseek-v4-pro") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00066 * multiplier,
output_cost_per_1k_tokens: 0.00198 * multiplier,
cache_read_input_token_cost: Some(0.000022 * multiplier),
currency: "USD".to_string(),
updated_at: pricing_time,
..Default::default()
},
m if m.contains("deepseek-chat") || m.contains("deepseek-reasoner") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00022 * multiplier,
output_cost_per_1k_tokens: 0.00066 * multiplier,
cache_read_input_token_cost: Some(0.000007 * multiplier),
currency: "USD".to_string(),
updated_at: pricing_time,
..Default::default()
},
m if m == "deepseek-flash" || m.contains("deepseek-v4-flash") => ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00015 * multiplier,
output_cost_per_1k_tokens: 0.00060 * multiplier,
cache_read_input_token_cost: Some(0.000003 * multiplier),
currency: "USD".to_string(),
updated_at: pricing_time,
..Default::default()
},
_ => {
return Err(CostError::ModelNotSupported {
model: model.to_string(),
provider: "deepseek".to_string(),
});
}
};
Ok(pricing)
}
pub(super) fn get_moonshot_pricing(model: &str) -> Result<ModelPricing, CostError> {
use chrono::Utc;
let normalized_model = model.to_lowercase();
let pricing = if normalized_model.contains("kimi-k2.6") {
ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00095,
output_cost_per_1k_tokens: 0.004,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
}
} else if normalized_model.contains("kimi-k2.5") {
ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0006,
output_cost_per_1k_tokens: 0.003,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
}
} else if normalized_model.contains("kimi-k2-thinking-turbo")
|| normalized_model.contains("kimi-k2-turbo-preview")
{
ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.00115,
output_cost_per_1k_tokens: 0.008,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
}
} else if normalized_model.contains("kimi-k2-thinking")
|| normalized_model.contains("kimi-k2-0905-preview")
|| normalized_model.contains("kimi-k2-0711-preview")
{
ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0006,
output_cost_per_1k_tokens: 0.0025,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
}
} else if normalized_model.contains("moonshot-v1-8k") {
ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.0002,
output_cost_per_1k_tokens: 0.002,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
}
} else if normalized_model.contains("moonshot-v1-32k") {
ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.001,
output_cost_per_1k_tokens: 0.003,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
}
} else if normalized_model.contains("moonshot-v1-128k") {
ModelPricing {
model: model.to_string(),
input_cost_per_1k_tokens: 0.002,
output_cost_per_1k_tokens: 0.005,
currency: "USD".to_string(),
updated_at: Utc::now(),
..Default::default()
}
} else {
return Err(CostError::ModelNotSupported {
model: model.to_string(),
provider: "moonshot".to_string(),
});
};
Ok(pricing)
}
#[cfg(test)]
mod deepseek_time_of_use_tests {
use super::*;
use chrono::{TimeZone, Utc};
#[test]
fn hardcoded_fallback_uses_the_documented_utc_schedule() {
let off_peak = Utc.with_ymd_and_hms(2026, 8, 29, 2, 0, 0).unwrap();
let peak = Utc.with_ymd_and_hms(2026, 8, 24, 2, 0, 0).unwrap();
for model in ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat"] {
let off_peak_pricing = get_deepseek_pricing_at(model, off_peak).unwrap();
let peak_pricing = get_deepseek_pricing_at(model, peak).unwrap();
assert_eq!(
peak_pricing.input_cost_per_1k_tokens,
off_peak_pricing.input_cost_per_1k_tokens * 2.0
);
assert_eq!(
peak_pricing.output_cost_per_1k_tokens,
off_peak_pricing.output_cost_per_1k_tokens * 2.0
);
assert_eq!(
peak_pricing.cache_read_input_token_cost,
off_peak_pricing
.cache_read_input_token_cost
.map(|rate| rate * 2.0)
);
}
}
}