use crate::pricing::{ModelPricing, ModelPricingMap, calculate_cost};
use crate::utils::TokenCounts;
#[derive(Debug, Clone, Copy)]
pub enum CostSource {
Litellm,
OpenCodeStored(f64),
CursorStored(f64),
HermesStored(f64),
GrokGauge,
}
pub fn resolve_model_cost(
model: &str,
counts: &TokenCounts,
pricing_map: &ModelPricingMap,
source: CostSource,
) -> (f64, Option<String>) {
let priced = |pricing: &ModelPricing| {
let token_cost = calculate_cost(counts, pricing);
token_cost + counts.web_search_requests as f64 * pricing.web_search_cost_per_query
};
match source {
CostSource::CursorStored(stored) => (stored, None),
CostSource::OpenCodeStored(stored) | CostSource::HermesStored(stored) => {
match pricing_map.get_exact(model) {
Some(pricing) => (priced(&pricing), None),
None => (stored, None),
}
}
CostSource::Litellm => {
let result = pricing_map.get(model);
(priced(&result.pricing), result.matched_model)
}
CostSource::GrokGauge => {
let result = pricing_map.get(model);
let mut pricing = result.pricing;
if pricing.cache_read_input_token_cost <= 0.0 {
pricing.cache_read_input_token_cost = pricing.input_cost_per_token;
}
(priced(&pricing), result.matched_model)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pricing::clear_pricing_cache;
use std::collections::HashMap;
fn map_with_gpt4() -> ModelPricingMap {
let mut raw = HashMap::new();
raw.insert(
"gpt-4".to_string(),
ModelPricing {
input_cost_per_token: 1e-5,
..Default::default()
},
);
ModelPricingMap::new(raw)
}
fn counts(input: i64) -> TokenCounts {
TokenCounts {
input_tokens: input,
total: input,
..Default::default()
}
}
#[test]
fn test_opencode_exact_match_computes_from_tokens() {
clear_pricing_cache();
let map = map_with_gpt4();
let (cost, matched) = resolve_model_cost(
"gpt-4",
&counts(1_000_000),
&map,
CostSource::OpenCodeStored(99.0),
);
assert!((cost - 10.0).abs() < 1e-6); assert!(matched.is_none());
}
#[test]
fn test_opencode_no_exact_match_uses_stored_cost() {
clear_pricing_cache();
let map = map_with_gpt4();
let (cost, matched) = resolve_model_cost(
"deepseek-v4-pro",
&counts(1_000_000),
&map,
CostSource::OpenCodeStored(99.0),
);
assert!((cost - 99.0).abs() < 1e-9);
assert!(matched.is_none());
}
#[test]
fn test_cursor_stored_cost_ignores_exact_match() {
clear_pricing_cache();
let map = map_with_gpt4();
let (cost, matched) = resolve_model_cost(
"gpt-4",
&counts(1_000_000),
&map,
CostSource::CursorStored(3.5),
);
assert!((cost - 3.5).abs() < 1e-9);
assert!(matched.is_none());
}
#[test]
fn test_non_opencode_keeps_existing_lookup() {
clear_pricing_cache();
let map = map_with_gpt4();
let (cost, matched) =
resolve_model_cost("gpt-4", &counts(1_000_000), &map, CostSource::Litellm);
assert!((cost - 10.0).abs() < 1e-6);
assert!(matched.is_none());
}
}