#[derive(Debug, Clone, Copy)]
pub struct GeminiPricing {
pub input: f64,
pub input_long: f64,
pub output: f64,
pub output_long: f64,
pub cache_input: f64,
pub cache_input_long: f64,
pub cache_storage_per_hour: f64,
}
impl GeminiPricing {
pub const GEMINI_31_PRO_PREVIEW: Self = Self {
input: 2.00,
input_long: 4.00,
output: 12.00,
output_long: 18.00,
cache_input: 0.20,
cache_input_long: 0.40,
cache_storage_per_hour: 4.50,
};
pub const GEMINI_31_FLASH_LITE: Self = Self {
input: 0.25,
input_long: 0.25,
output: 1.50,
output_long: 1.50,
cache_input: 0.025,
cache_input_long: 0.025,
cache_storage_per_hour: 1.00,
};
pub const GEMINI_3_FLASH_PREVIEW: Self = Self {
input: 0.50,
input_long: 0.50,
output: 3.00,
output_long: 3.00,
cache_input: 0.05,
cache_input_long: 0.10,
cache_storage_per_hour: 1.00,
};
pub const GEMINI_25_PRO: Self = Self {
input: 1.25,
input_long: 2.50,
output: 10.00,
output_long: 15.00,
cache_input: 0.125,
cache_input_long: 0.25,
cache_storage_per_hour: 4.50,
};
pub const GEMINI_25_FLASH: Self = Self {
input: 0.30,
input_long: 0.30,
output: 2.50,
output_long: 2.50,
cache_input: 0.03,
cache_input_long: 0.10,
cache_storage_per_hour: 1.00,
};
pub const GEMINI_25_FLASH_LITE: Self = Self {
input: 0.10,
input_long: 0.10,
output: 0.40,
output_long: 0.40,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_20_FLASH: Self = Self {
input: 0.10,
input_long: 0.10,
output: 0.40,
output_long: 0.40,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_31_FLASH_LIVE: Self = Self {
input: 0.75,
input_long: 0.75,
output: 4.50,
output_long: 4.50,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_25_FLASH_NATIVE_AUDIO: Self = Self {
input: 0.50,
input_long: 0.50,
output: 2.00,
output_long: 2.00,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_31_FLASH_IMAGE: Self = Self {
input: 0.50,
input_long: 0.50,
output: 3.00,
output_long: 3.00,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_25_FLASH_IMAGE: Self = Self {
input: 0.30,
input_long: 0.30,
output: 30.00,
output_long: 30.00,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_3_PRO_IMAGE: Self = Self {
input: 2.00,
input_long: 2.00,
output: 12.00,
output_long: 12.00,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_25_COMPUTER_USE: Self = Self {
input: 1.25,
input_long: 2.50,
output: 10.00,
output_long: 15.00,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_25_FLASH_TTS: Self = Self {
input: 0.50,
input_long: 0.50,
output: 10.00,
output_long: 10.00,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_25_PRO_TTS: Self = Self {
input: 1.00,
input_long: 1.00,
output: 20.00,
output_long: 20.00,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_EMBEDDING: Self = Self {
input: 0.15,
input_long: 0.15,
output: 0.0,
output_long: 0.0,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
pub const GEMINI_EMBEDDING_2: Self = Self {
input: 0.20,
input_long: 0.20,
output: 0.0,
output_long: 0.0,
cache_input: 0.0,
cache_input_long: 0.0,
cache_storage_per_hour: 0.0,
};
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CostBreakdown {
pub input_cost: f64,
pub cache_cost: f64,
pub output_cost: f64,
}
impl CostBreakdown {
pub fn total(&self) -> f64 {
self.input_cost + self.cache_cost + self.output_cost
}
}
impl std::fmt::Display for CostBreakdown {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"${:.6} (in=${:.6} cache=${:.6} out=${:.6})",
self.total(),
self.input_cost,
self.cache_cost,
self.output_cost
)
}
}
pub fn estimate_cost(
pricing: &GeminiPricing,
input_tokens: u64,
output_tokens: u64,
cached_tokens: u64,
) -> CostBreakdown {
let mtok = 1_000_000.0;
CostBreakdown {
input_cost: input_tokens as f64 / mtok * pricing.input,
cache_cost: cached_tokens as f64 / mtok * pricing.cache_input,
output_cost: output_tokens as f64 / mtok * pricing.output,
}
}
pub fn estimate_cost_long(
pricing: &GeminiPricing,
input_tokens: u64,
output_tokens: u64,
cached_tokens: u64,
) -> CostBreakdown {
let mtok = 1_000_000.0;
CostBreakdown {
input_cost: input_tokens as f64 / mtok * pricing.input_long,
cache_cost: cached_tokens as f64 / mtok * pricing.cache_input_long,
output_cost: output_tokens as f64 / mtok * pricing.output_long,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gemini_25_flash_basic_cost() {
let cost = estimate_cost(&GeminiPricing::GEMINI_25_FLASH, 1_000_000, 1_000_000, 0);
assert!((cost.input_cost - 0.30).abs() < 1e-9);
assert!((cost.output_cost - 2.50).abs() < 1e-9);
assert!((cost.total() - 2.80).abs() < 1e-9);
}
#[test]
fn gemini_25_pro_with_cache() {
let cost = estimate_cost(&GeminiPricing::GEMINI_25_PRO, 500_000, 100_000, 200_000);
assert!((cost.input_cost - 0.625).abs() < 1e-9);
assert!((cost.cache_cost - 0.025).abs() < 1e-9);
assert!((cost.output_cost - 1.00).abs() < 1e-9);
assert!((cost.total() - 1.65).abs() < 1e-9);
}
#[test]
fn gemini_25_pro_long_context() {
let cost = estimate_cost_long(&GeminiPricing::GEMINI_25_PRO, 1_000_000, 1_000_000, 0);
assert!((cost.input_cost - 2.50).abs() < 1e-9);
assert!((cost.output_cost - 15.00).abs() < 1e-9);
assert!((cost.total() - 17.50).abs() < 1e-9);
}
#[test]
fn no_cache_model_zero_cache_cost() {
let cost = estimate_cost(&GeminiPricing::GEMINI_20_FLASH, 1_000_000, 1_000_000, 500_000);
assert!((cost.cache_cost - 0.0).abs() < 1e-9);
assert!((cost.input_cost - 0.10).abs() < 1e-9);
assert!((cost.output_cost - 0.40).abs() < 1e-9);
}
#[test]
fn zero_tokens_zero_cost() {
let cost = estimate_cost(&GeminiPricing::GEMINI_25_PRO, 0, 0, 0);
assert!((cost.total() - 0.0).abs() < 1e-9);
}
#[test]
fn display_format() {
let cost = CostBreakdown { input_cost: 0.003, cache_cost: 0.001, output_cost: 0.0075 };
let s = cost.to_string();
assert!(s.starts_with('$'));
assert!(s.contains("in="));
assert!(s.contains("cache="));
assert!(s.contains("out="));
}
}