diff --git a/crates/tui/src/pricing.rs b/crates/tui/src/pricing.rs index 750f9830b..c4bfebe4b 100644 --- a/crates/tui/src/pricing.rs +++ b/crates/tui/src/pricing.rs @@ -2,7 +2,7 @@ //! //! Pricing based on DeepSeek's published rates (per million tokens). -use chrono::{DateTime, TimeZone, Utc}; +use chrono::{DateTime, Utc}; use crate::models::Usage; @@ -70,18 +70,12 @@ struct ModelPricing { cny: CurrencyPricing, } -fn v4_pro_discount_ends_at() -> DateTime { - Utc.with_ymd_and_hms(2026, 5, 31, 15, 59, 0) - .single() - .expect("valid DeepSeek V4 Pro discount end timestamp") -} - /// Look up pricing for a model name. fn pricing_for_model(model: &str) -> Option { pricing_for_model_at(model, Utc::now()) } -fn pricing_for_model_at(model: &str, now: DateTime) -> Option { +fn pricing_for_model_at(model: &str, _now: DateTime) -> Option { let lower = model.to_lowercase(); if lower.starts_with("deepseek-ai/") { // NVIDIA NIM-hosted DeepSeek uses NVIDIA's catalog/account terms, not @@ -92,32 +86,17 @@ fn pricing_for_model_at(model: &str, now: DateTime) -> Option return None; } if lower.contains("v4-pro") || lower.contains("v4pro") { - if now <= v4_pro_discount_ends_at() { - // DeepSeek lists these as a limited-time 75% discount through - // 2026-05-31 15:59 UTC. - return Some(ModelPricing { - usd: CurrencyPricing { - input_cache_hit_per_million: 0.003625, - input_cache_miss_per_million: 0.435, - output_per_million: 0.87, - }, - cny: CurrencyPricing { - input_cache_hit_per_million: 0.025, - input_cache_miss_per_million: 3.0, - output_per_million: 6.0, - }, - }); - } + // DeepSeek lists these as a permanent 75% discount. Some(ModelPricing { usd: CurrencyPricing { - input_cache_hit_per_million: 0.0145, - input_cache_miss_per_million: 1.74, - output_per_million: 3.48, + input_cache_hit_per_million: 0.003625, + input_cache_miss_per_million: 0.435, + output_per_million: 0.87, }, cny: CurrencyPricing { - input_cache_hit_per_million: 0.1, - input_cache_miss_per_million: 12.0, - output_per_million: 24.0, + input_cache_hit_per_million: 0.025, + input_cache_miss_per_million: 3.0, + output_per_million: 6.0, }, }) } else { @@ -241,6 +220,7 @@ pub fn format_cost_estimate(estimate: CostEstimate, currency: CostCurrency) -> S #[cfg(test)] mod tests { use super::*; + use chrono::TimeZone; #[test] fn nvidia_nim_deepseek_model_does_not_use_deepseek_platform_pricing() { @@ -248,12 +228,10 @@ mod tests { } #[test] - fn v4_pro_uses_limited_time_discount_before_expiry() { - let before_expiry = Utc - .with_ymd_and_hms(2026, 5, 31, 15, 58, 59) - .single() - .unwrap(); - let pricing = pricing_for_model_at("deepseek-v4-pro", before_expiry).unwrap(); + fn v4_pro_always_uses_permanent_discount() { + // Discount is now permanent, should apply at any time. + let now = Utc::now(); + let pricing = pricing_for_model_at("deepseek-v4-pro", now).unwrap(); assert_eq!(pricing.usd.input_cache_hit_per_million, 0.003625); assert_eq!(pricing.usd.input_cache_miss_per_million, 0.435); @@ -264,30 +242,33 @@ mod tests { } #[test] - fn v4_pro_returns_to_base_rates_after_discount_expiry() { - let after_expiry = Utc - .with_ymd_and_hms(2026, 5, 31, 16, 0, 0) - .single() - .unwrap(); - let pricing = pricing_for_model_at("deepseek-v4-pro", after_expiry).unwrap(); - - assert_eq!(pricing.usd.input_cache_hit_per_million, 0.0145); - assert_eq!(pricing.usd.input_cache_miss_per_million, 1.74); - assert_eq!(pricing.usd.output_per_million, 3.48); - assert_eq!(pricing.cny.input_cache_hit_per_million, 0.1); - assert_eq!(pricing.cny.input_cache_miss_per_million, 12.0); - assert_eq!(pricing.cny.output_per_million, 24.0); - } - - #[test] - fn v4_pro_discount_still_applies_just_before_old_may5_expiry() { - // Regression for #267: extension to 2026-05-31 15:59 UTC. - let after_old_expiry = Utc.with_ymd_and_hms(2026, 5, 6, 0, 0, 0).single().unwrap(); - let pricing = pricing_for_model_at("deepseek-v4-pro", after_old_expiry).unwrap(); + fn v4_pro_discount_does_not_revert_to_base_rates_in_future() { + // Permanent discount should still apply far in the future. + let far_future = Utc.with_ymd_and_hms(2030, 1, 1, 0, 0, 0).single().unwrap(); + let pricing = pricing_for_model_at("deepseek-v4-pro", far_future).unwrap(); assert_eq!(pricing.usd.input_cache_hit_per_million, 0.003625); assert_eq!(pricing.usd.input_cache_miss_per_million, 0.435); assert_eq!(pricing.usd.output_per_million, 0.87); + assert_eq!(pricing.cny.input_cache_hit_per_million, 0.025); + assert_eq!(pricing.cny.input_cache_miss_per_million, 3.0); + assert_eq!(pricing.cny.output_per_million, 6.0); + } + + #[test] + fn v4_pro_permanent_discount_applies_consistently() { + // Permanent discount applies at various points in time. + let dates = [ + Utc.with_ymd_and_hms(2026, 5, 6, 0, 0, 0).single().unwrap(), + Utc.with_ymd_and_hms(2026, 6, 1, 0, 0, 0).single().unwrap(), + Utc.with_ymd_and_hms(2027, 1, 1, 0, 0, 0).single().unwrap(), + ]; + for date in dates { + let pricing = pricing_for_model_at("deepseek-v4-pro", date).unwrap(); + assert_eq!(pricing.cny.input_cache_hit_per_million, 0.025); + assert_eq!(pricing.cny.input_cache_miss_per_million, 3.0); + assert_eq!(pricing.cny.output_per_million, 6.0); + } } #[test]