use crate::catalog::Cost;
use serde_json::Value;
const PER_MILLION: f64 = 1_000_000.0;
fn count(usage: &Value, key: &str) -> u64 {
usage.get(key).and_then(Value::as_u64).unwrap_or(0)
}
fn charge(tokens: u64, rate: Option<f64>) -> Option<f64> {
if tokens == 0 {
return Some(0.0);
}
Some(tokens as f64 * rate? / PER_MILLION)
}
fn highest_published_rate(cost: &Cost) -> Option<f64> {
[cost.input, cost.output, cost.cache_read, cost.cache_write]
.into_iter()
.flatten()
.fold(None, |best: Option<f64>, rate| {
Some(best.map_or(rate, |b| b.max(rate)))
})
}
pub fn price(usage: &Value, cost: &Cost) -> Option<f64> {
let cache_read = count(usage, "cache_read_tokens");
let cache_write = count(usage, "cache_write_tokens");
let input = usage
.get("uncached_input_tokens")
.and_then(Value::as_u64)
.unwrap_or_else(|| count(usage, "prompt_tokens"));
if input == 0 && cache_read == 0 && cache_write == 0 && count(usage, "completion_tokens") == 0 {
return None;
}
let ceiling = highest_published_rate(cost);
let rate_for = |own: Option<f64>| own.or(ceiling);
Some(
charge(input, rate_for(cost.input))?
+ charge(count(usage, "completion_tokens"), rate_for(cost.output))?
+ charge(cache_read, rate_for(cost.cache_read))?
+ charge(cache_write, rate_for(cost.cache_write))?,
)
}
pub fn for_model(model: &str) -> Option<Cost> {
crate::catalog::lookup_id(model)?.cost
}
pub fn for_target(provider: &str, model: &str) -> Option<Cost> {
for_model(&format!("{provider}/{model}")).or_else(|| for_model(model))
}
pub fn cost_usd(provider: &str, model: &str, usage: &Value) -> Option<f64> {
let info = crate::catalog::lookup_id(&format!("{provider}/{model}"))
.filter(|m| m.cost.is_some())
.or_else(|| crate::catalog::lookup_id(model))?;
let input_tokens = usage
.get("uncached_input_tokens")
.and_then(Value::as_u64)
.map(|n| {
n.saturating_add(count(usage, "cache_read_tokens"))
.saturating_add(count(usage, "cache_write_tokens"))
})
.unwrap_or_else(|| count(usage, "prompt_tokens"));
price(usage, &info.cost_for_input_tokens(input_tokens)?)
}
pub fn is_priceable(provider: &str, model: &str) -> bool {
for_target(provider, model).is_some()
}
pub const SOURCE_PROVIDER: &str = "provider";
pub const SOURCE_PROVIDER_FLOOR: &str = "provider_floor";
pub const SOURCE_CATALOG: &str = "catalog";
pub fn reported(usage: &Value) -> Option<f64> {
usage
.get("cost")
.and_then(Value::as_f64)
.filter(|usd| usd.is_finite() && *usd >= 0.0)
}
pub fn resolve(provider: &str, model: &str, usage: &Value) -> (Option<f64>, &'static str) {
match reported(usage) {
Some(usd) => (Some(usd), SOURCE_PROVIDER),
None => (cost_usd(provider, model, usage), SOURCE_CATALOG),
}
}
pub fn stamped(usage: &Value) -> Option<f64> {
usage.get("cost_usd").and_then(Value::as_f64)
}
pub fn stamped_source(usage: &Value) -> Option<&str> {
usage.get("cost_source").and_then(Value::as_str)
}
pub fn attribute(provider: &str, model: &str, usage: &Value) -> (Option<f64>, Option<String>) {
match stamped_source(usage) {
Some(source) => (stamped(usage), Some(source.to_owned())),
None => {
let (usd, source) = resolve(provider, model, usage);
(usd, Some(source.to_owned()))
}
}
}
pub fn stamp(provider: &str, model: &str, response: &mut Value) {
if !response["usage"].is_object() {
return;
}
let (mut cost, mut source) = resolve(provider, model, &response["usage"]);
let provider_floor = response["usage"]
.get("provider_cost_floor_usd")
.and_then(Value::as_f64)
.filter(|value| value.is_finite() && *value >= 0.0);
if source != SOURCE_PROVIDER
&& provider_floor.is_some_and(|floor| cost.is_none_or(|resolved| resolved < floor))
{
cost = provider_floor;
source = SOURCE_PROVIDER_FLOOR;
}
response["usage"]["cost_usd"] = match cost {
Some(usd) => serde_json::json!(usd),
None => Value::Null,
};
response["usage"]["cost_source"] = serde_json::json!(source);
}
pub fn stamp_chunk(provider: &str, model: &str, chunk: String) -> String {
if !chunk.contains("\"usage\":{") {
return chunk;
}
let mut value = match crate::json_bounds::parse_str(&chunk, crate::json_bounds::Limits::SSE) {
Ok(value) => value,
Err(crate::json_bounds::ParseError::Malformed(_)) => return chunk,
Err(crate::json_bounds::ParseError::Complexity) => {
return serde_json::json!({
"type":"error",
"message":"stream JSON exceeds complexity limit"
})
.to_string()
}
};
if !value["usage"].is_object() {
return chunk;
}
stamp(provider, model, &mut value);
value.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn grok_context_threshold_counts_cached_input_and_reprices_the_whole_response() {
for model in ["grok-4.6", "grok-4.7"] {
let mut usage = serde_json::json!({"uncached_input_tokens":50_000,"cache_read_tokens":150_000,"completion_tokens":1000});
close(cost_usd("xai", model, &usage), 0.1 + 0.075 + 0.006);
usage["cache_read_tokens"] = serde_json::json!(150_001);
close(cost_usd("xai", model, &usage), 0.2 + 0.150001 + 0.012);
}
}
use serde_json::json;
const TOLERANCE: f64 = 1e-12;
fn priced() -> Cost {
Cost {
input: Some(3.0),
output: Some(15.0),
cache_read: Some(0.3),
cache_write: Some(3.75),
}
}
fn close(actual: Option<f64>, expected: f64) {
let actual = actual.expect("a priced model must produce a cost");
assert!(
(actual - expected).abs() < TOLERANCE,
"expected {expected}, got {actual}"
);
}
#[test]
fn exact_arithmetic_on_a_known_price() {
let usage = json!({"uncached_input_tokens": 1_000_000, "completion_tokens": 1_000_000});
close(price(&usage, &priced()), 18.0);
}
#[test]
fn cache_reads_and_writes_are_priced_separately() {
let usage = json!({
"uncached_input_tokens": 1_000_000,
"completion_tokens": 0,
"cache_read_tokens": 1_000_000,
"cache_write_tokens": 1_000_000,
});
close(price(&usage, &priced()), 7.05);
}
#[test]
fn a_cached_prompt_is_not_billed_twice() {
let mut usage = json!({"prompt_tokens": 1_000_000, "completion_tokens": 0});
crate::usage::normalize_cache(
&json!({"prompt_tokens": 1_000_000, "prompt_tokens_details": {"cached_tokens": 900_000}}),
&mut usage,
);
close(price(&usage, &priced()), 0.57);
}
#[test]
fn an_exclusive_prompt_provider_keeps_its_whole_prompt() {
let mut usage = json!({"prompt_tokens": 100_000, "completion_tokens": 0});
crate::usage::normalize_cache(
&json!({"input_tokens": 100_000, "cache_read_input_tokens": 900_000}),
&mut usage,
);
assert_eq!(usage["uncached_input_tokens"], 100_000);
close(price(&usage, &priced()), 0.57);
}
#[test]
fn an_unreported_usage_object_is_unknown_not_free() {
assert_eq!(
price(
&json!({"prompt_tokens":0,"completion_tokens":0,"cache_read_tokens":0,"cache_write_tokens":0,"uncached_input_tokens":0}),
&priced()
),
None
);
assert_eq!(price(&json!({}), &priced()), None);
close(
price(&json!({"uncached_input_tokens": 1_000_000}), &priced()),
3.0,
);
}
#[test]
fn a_model_with_no_price_is_unknown_not_free() {
let usage = json!({"uncached_input_tokens": 1_000, "completion_tokens": 1_000});
assert_eq!(price(&usage, &Cost::default()), None);
}
#[test]
fn a_missing_cache_rate_is_charged_at_the_models_highest_rate() {
let partial = Cost {
input: Some(3.0),
output: Some(15.0),
cache_read: None,
cache_write: None,
};
close(
price(
&json!({"uncached_input_tokens": 1_000_000, "completion_tokens": 0, "cache_read_tokens": 0}),
&partial,
),
3.0,
);
close(
price(
&json!({"uncached_input_tokens": 1_000_000, "cache_read_tokens": 1_000_000}),
&partial,
),
18.0,
);
let complete = Cost {
cache_read: Some(0.3),
..partial
};
let usage = json!({"uncached_input_tokens": 1_000_000, "cache_read_tokens": 1_000_000});
assert!(
price(&usage, &partial).unwrap() >= price(&usage, &complete).unwrap(),
"a fallback rate must bound the real one from above, or a cap under-charges"
);
}
#[test]
fn stamp_always_writes_the_key() {
let mut response = json!({"usage": {"uncached_input_tokens": 1, "completion_tokens": 1}});
stamp("nowhere", "definitely-not-a-model", &mut response);
assert!(
response["usage"]["cost_usd"].is_null(),
"an unpriceable model must stamp null, never 0"
);
assert!(response["usage"].get("cost_usd").is_some());
}
#[test]
fn partial_provider_floor_is_distinct_from_a_terminal_provider_bill() {
let mut floor = json!({"usage": {
"prompt_tokens": 7,
"completion_tokens": 3,
"provider_cost_floor_usd": 1.0
}});
stamp("openrouter", "x-ai/grok-4.7", &mut floor);
assert_eq!(floor["usage"]["cost_usd"], 1.0);
assert_eq!(floor["usage"]["cost_source"], SOURCE_PROVIDER_FLOOR);
let mut corrected = json!({"usage": {
"cost": 0.25,
"provider_cost_floor_usd": 1.0
}});
stamp("openrouter", "x-ai/grok-4.7", &mut corrected);
assert_eq!(corrected["usage"]["cost_usd"], 0.25);
assert_eq!(corrected["usage"]["cost_source"], SOURCE_PROVIDER);
}
#[test]
fn a_response_without_usage_is_left_alone() {
let mut response = json!({"id": "r1"});
stamp("nowhere", "anything", &mut response);
assert!(response.get("usage").is_none());
}
#[test]
fn stamp_chunk_leaves_usageless_chunks_untouched() {
assert_eq!(stamp_chunk("nowhere", "m", "[DONE]".into()), "[DONE]");
let no_usage = json!({"choices": []}).to_string();
assert_eq!(stamp_chunk("nowhere", "m", no_usage.clone()), no_usage);
let stamped = stamp_chunk(
"nowhere",
"m",
json!({"usage": {"prompt_tokens": 1}}).to_string(),
);
assert!(stamped.contains("cost_usd"));
}
#[test]
fn an_openrouter_variant_suffix_prices_the_same_as_its_base_model() {
let base = for_target("openrouter", "deepseek/deepseek-v4.1-flash");
assert!(
base.is_some(),
"base id must be priced by the vendored catalog"
);
for suffix in [":nitro", ":floor", ":free", ":exacto", ":online"] {
let suffixed = for_target(
"openrouter",
&format!("deepseek/deepseek-v4.1-flash{suffix}"),
);
assert_eq!(
suffixed, base,
"suffix {suffix} must price like the base id"
);
}
}
#[test]
fn an_unrecognized_suffix_is_not_treated_as_an_openrouter_variant() {
assert_eq!(
for_target("openrouter", "deepseek/deepseek-v4.1-flash:beta"),
None
);
}
#[test]
fn an_ollama_style_colon_tag_is_not_mistaken_for_an_openrouter_suffix() {
assert_eq!(
crate::catalog::strip_variant_suffix("llama3:8b"),
"llama3:8b"
);
assert_eq!(for_target("ollama", "llama3:8b"), None);
}
}