use super::{feed_index, override_pricing, price_feed, ModelPricing};
use crate::model_catalog::AGENT_MODELS;
use crate::types::{provider_for_cli, AgentKind, MeteringShape};
pub(super) fn resolve_model_pricing(model: &str, agent: AgentKind) -> Option<ModelPricing> {
override_pricing(model, agent)
.or_else(|| subscription_pricing(agent))
.or_else(|| static_catalog_pricing(model, agent))
.or_else(|| exact_feed_pricing(model, agent))
}
pub(super) fn subscription_pricing(agent: AgentKind) -> Option<ModelPricing> {
matches!(provider_for_cli(agent).1, MeteringShape::Subscription).then_some(ModelPricing {
input_per_m: 0.0,
output_per_m: 0.0,
cached_input_per_m: Some(0.0),
cache_creation_per_m: Some(0.0),
})
}
fn exact_feed_pricing(model: &str, agent: AgentKind) -> Option<ModelPricing> {
let vendor_cli = matches!(
agent,
AgentKind::Codex | AgentKind::Claude | AgentKind::Gemini | AgentKind::Grok
);
if !vendor_cli {
return None;
}
let (feed, index) = feed_index()?;
let entry = price_feed::feed_lookup(&feed, &index, model)?;
Some(ModelPricing {
input_per_m: entry.input_per_mtok,
output_per_m: entry.output_per_mtok,
cached_input_per_m: entry.cached_input_per_mtok,
cache_creation_per_m: None,
})
}
fn static_catalog_pricing(model: &str, agent: AgentKind) -> Option<ModelPricing> {
let row = AGENT_MODELS
.iter()
.find(|known| known.agent == agent && known.model.eq_ignore_ascii_case(model))?;
if row.input_per_m == 0.0 && row.output_per_m == 0.0 && row.tier != "free" {
return None;
}
Some(ModelPricing {
input_per_m: row.input_per_m,
output_per_m: row.output_per_m,
cached_input_per_m: (row.tier == "free").then_some(0.0),
cache_creation_per_m: (row.tier == "free").then_some(0.0),
})
}
#[cfg(test)]
#[path = "pricing_resolution_tests.rs"]
mod tests;