Compute cost from stream usage using model-specific pricing rates. Falls back to Claude-like pricing ($3/$15 per million) if no rates provided.
(usage: &MessageUsage, rates: Option<&CostRates>, provider_id: &str)
| 1198 | /// Compute cost from stream usage using model-specific pricing rates. |
| 1199 | /// Falls back to Claude-like pricing ($3/$15 per million) if no rates provided. |
| 1200 | fn compute_cost(usage: &MessageUsage, rates: Option<&CostRates>, provider_id: &str) -> f64 { |
| 1201 | let rates = rates.cloned().unwrap_or(CostRates { |
| 1202 | input: 3.0, |
| 1203 | output: 15.0, |
| 1204 | cache_read: 0.3, |
| 1205 | cache_write: 3.75, |
| 1206 | over_200k: None, |
| 1207 | }); |
| 1208 | |
| 1209 | let total_tokens = usage.input_tokens |
| 1210 | + usage.output_tokens |
| 1211 | + usage.reasoning_tokens |
| 1212 | + usage.cache_read_tokens |
| 1213 | + usage.cache_write_tokens; |
| 1214 | let use_over_200k = total_tokens > 200_000; |
| 1215 | |
| 1216 | let (input_rate, output_rate, cache_read_rate, cache_write_rate) = if use_over_200k { |
| 1217 | if let Some(tier) = rates.over_200k.as_ref() { |
| 1218 | (tier.input, tier.output, tier.cache_read, tier.cache_write) |
| 1219 | } else { |
| 1220 | ( |
| 1221 | rates.input, |
| 1222 | rates.output, |
| 1223 | rates.cache_read, |
| 1224 | rates.cache_write, |
| 1225 | ) |
| 1226 | } |
| 1227 | } else { |
| 1228 | ( |
| 1229 | rates.input, |
| 1230 | rates.output, |
| 1231 | rates.cache_read, |
| 1232 | rates.cache_write, |
| 1233 | ) |
| 1234 | }; |
| 1235 | |
| 1236 | // Keep input billing resilient for providers where cache tokens may be folded into input. |
| 1237 | let billable_input_tokens = if is_anthropic_or_bedrock_provider(provider_id) { |
| 1238 | usage |
| 1239 | .input_tokens |
| 1240 | .saturating_sub(usage.cache_read_tokens + usage.cache_write_tokens) |
| 1241 | } else { |
| 1242 | usage.input_tokens |
| 1243 | }; |
| 1244 | |
| 1245 | // Reasoning tokens are billed at output-token rates. |
| 1246 | let billable_output_tokens = usage.output_tokens + usage.reasoning_tokens; |
| 1247 | |
| 1248 | let input_cost = (billable_input_tokens as f64) * input_rate / 1_000_000.0; |
| 1249 | let output_cost = (billable_output_tokens as f64) * output_rate / 1_000_000.0; |
| 1250 | let cache_read_cost = (usage.cache_read_tokens as f64) * cache_read_rate / 1_000_000.0; |
| 1251 | let cache_write_cost = (usage.cache_write_tokens as f64) * cache_write_rate / 1_000_000.0; |
| 1252 | |
| 1253 | input_cost + output_cost + cache_read_cost + cache_write_cost |
| 1254 | } |
| 1255 | |
| 1256 | fn is_anthropic_or_bedrock_provider(provider_id: &str) -> bool { |
| 1257 | let lower = provider_id.to_ascii_lowercase(); |