use systemprompt_models::services::{GatewayConfig, ModelPricing, ProviderRegistry};
pub fn resolve(
provider: &str,
candidates: &[&str],
gateway: Option<&GatewayConfig>,
registry: &ProviderRegistry,
) -> ModelPricing {
for model in candidates.iter().filter(|m| !m.is_empty()) {
if let Some(p) = lookup(model, gateway, registry) {
return p;
}
}
tracing::warn!(
provider = provider,
candidates = ?candidates,
"Gateway pricing lookup: no override and no registry entry — cost_microdollars will be 0"
);
ModelPricing::default()
}
fn lookup(
model: &str,
gateway: Option<&GatewayConfig>,
registry: &ProviderRegistry,
) -> Option<ModelPricing> {
if let Some(gw) = gateway
&& let Some(route) = gw.find_route(model)
&& let Some(p) = route.pricing
{
return Some(p);
}
registry_pricing(registry, gateway, model)
}
fn registry_pricing(
registry: &ProviderRegistry,
gateway: Option<&GatewayConfig>,
model: &str,
) -> Option<ModelPricing> {
if let Some(route) = gateway.and_then(|gw| gw.find_route(model))
&& let Some(m) = route
.resolve(registry)
.and_then(|entry| entry.find_model(model))
{
return Some(m.pricing);
}
registry
.providers
.iter()
.find_map(|entry| entry.find_model(model))
.map(|m| m.pricing)
}
#[must_use]
pub fn cost_microdollars(pricing: ModelPricing, tokens: CostTokens) -> i64 {
let rate = |count: u32, per_million: f64| (f64::from(count) / 1_000_000.0) * per_million;
let total = rate(tokens.input, pricing.input_per_million)
+ rate(tokens.output, pricing.output_per_million)
+ rate(tokens.cache_read, pricing.cache_read_per_million)
+ rate(tokens.cache_creation, pricing.cache_write_per_million);
(total * 1_000_000.0).round() as i64
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CostTokens {
pub input: u32,
pub output: u32,
pub cache_read: u32,
pub cache_creation: u32,
}