systemprompt_api/services/gateway/
pricing.rs1use systemprompt_models::profile::{GatewayConfig, ProviderRegistry};
22use systemprompt_models::services::ModelPricing;
23
24pub fn resolve(
25 provider: &str,
26 candidates: &[&str],
27 gateway: Option<&GatewayConfig>,
28 registry: &ProviderRegistry,
29) -> ModelPricing {
30 for model in candidates.iter().filter(|m| !m.is_empty()) {
31 if let Some(p) = lookup(model, gateway, registry) {
32 return p;
33 }
34 }
35
36 tracing::warn!(
37 provider = provider,
38 candidates = ?candidates,
39 "Gateway pricing lookup: no override and no registry entry — cost_microdollars will be 0"
40 );
41 ModelPricing::default()
42}
43
44fn lookup(
45 model: &str,
46 gateway: Option<&GatewayConfig>,
47 registry: &ProviderRegistry,
48) -> Option<ModelPricing> {
49 if let Some(gw) = gateway
50 && let Some(route) = gw.find_route(model)
51 && let Some(p) = route.pricing
52 {
53 return Some(p);
54 }
55 registry_pricing(registry, gateway, model)
56}
57
58fn registry_pricing(
59 registry: &ProviderRegistry,
60 gateway: Option<&GatewayConfig>,
61 model: &str,
62) -> Option<ModelPricing> {
63 if let Some(route) = gateway.and_then(|gw| gw.find_route(model))
64 && let Some(m) = route
65 .resolve(registry)
66 .and_then(|entry| entry.find_model(model))
67 {
68 return Some(m.pricing);
69 }
70 registry
71 .providers
72 .iter()
73 .find_map(|entry| entry.find_model(model))
74 .map(|m| m.pricing)
75}
76
77#[must_use]
84pub fn cost_microdollars(pricing: ModelPricing, tokens: CostTokens) -> i64 {
85 let rate = |count: u32, per_million: f64| (f64::from(count) / 1_000_000.0) * per_million;
86 let total = rate(tokens.input, pricing.input_per_million)
87 + rate(tokens.output, pricing.output_per_million)
88 + rate(tokens.cache_read, pricing.cache_read_per_million)
89 + rate(tokens.cache_creation, pricing.cache_write_per_million);
90 (total * 1_000_000.0).round() as i64
91}
92
93#[derive(Debug, Clone, Copy, Default)]
96pub struct CostTokens {
97 pub input: u32,
98 pub output: u32,
99 pub cache_read: u32,
100 pub cache_creation: u32,
101}