use gateway_core::catalog::{ModelPrice, Usage};
use crate::backends::catalog::CatalogContentId;
use crate::config::{Model, Target};
use crate::desired_state::pricing::PricingSnapshot;
use crate::desired_state::{Checksum, ResourceRef};
use crate::state::ConfigSnapshot;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PriceIdentity {
book: ResourceRef,
checksum: Checksum,
catalog: CatalogContentId,
catalog_version: u64,
}
impl PriceIdentity {
pub const fn of(pricing: &PricingSnapshot) -> Self {
Self {
book: pricing.book(),
checksum: pricing.checksum(),
catalog: pricing.catalog(),
catalog_version: match pricing.catalog_version() {
Some(version) => version.get(),
None => 0,
},
}
}
pub fn book(&self) -> String {
self.book.to_string()
}
#[allow(dead_code)]
pub const fn version(&self) -> u64 {
self.book.version.get()
}
pub const fn catalog_version(&self) -> u64 {
self.catalog_version
}
pub fn checksum(&self) -> String {
self.checksum.to_string()
}
pub fn catalog(&self) -> String {
self.catalog.to_string()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RequestPrice {
rates: ModelPrice,
identity: Option<PriceIdentity>,
}
impl RequestPrice {
pub const fn configured(rates: ModelPrice) -> Self {
Self {
rates,
identity: None,
}
}
pub const fn approved(rates: ModelPrice, identity: PriceIdentity) -> Self {
Self {
rates,
identity: Some(identity),
}
}
pub fn cost_microdollars(&self, usage: Usage) -> u64 {
self.rates.cost_microdollars(usage)
}
#[cfg(test)]
pub const fn rates(&self) -> ModelPrice {
self.rates
}
pub const fn identity(&self) -> Option<PriceIdentity> {
self.identity
}
pub const fn catalog_version(&self) -> u64 {
match &self.identity {
None => 0,
Some(identity) => identity.catalog_version(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum Ineligible {
#[error("no price is in force for this model")]
Unpriced {
provider: String,
model: String,
book: String,
approval: &'static str,
},
}
impl Ineligible {
pub const fn reason(&self) -> &'static str {
match self {
Self::Unpriced { .. } => "no price is in force for this model",
}
}
pub fn detail(&self) -> String {
match self {
Self::Unpriced {
provider,
model,
book,
approval,
} => format!(
"catalogue offering `{provider}`/`{model}` has no approved price in {book} ({approval})"
),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AliasPrices {
priced: Vec<Result<RequestPrice, Ineligible>>,
}
impl AliasPrices {
pub fn resolve(snapshot: &ConfigSnapshot, model: &Model) -> Self {
Self {
priced: model
.targets
.iter()
.map(|target| price_of(snapshot.pricing(), target))
.collect(),
}
}
pub fn get(&self, index: usize) -> Option<RequestPrice> {
self.priced
.get(index)
.and_then(|priced| priced.as_ref().ok().copied())
}
pub fn estimate(&self) -> Option<RequestPrice> {
self.priced
.iter()
.find_map(|priced| priced.as_ref().ok().copied())
}
pub fn ineligible(&self, index: usize) -> Option<&Ineligible> {
self.priced
.get(index)
.and_then(|priced| priced.as_ref().err())
}
pub fn refusal(&self) -> Option<&Ineligible> {
if self.estimate().is_some() {
return None;
}
self.priced.iter().find_map(|priced| priced.as_ref().err())
}
}
fn price_of(
pricing: Option<&PricingSnapshot>,
target: &Target,
) -> Result<RequestPrice, Ineligible> {
let Some(pricing) = pricing else {
return Ok(RequestPrice::configured(target.price));
};
let Some(catalog) = &target.catalog else {
return Ok(RequestPrice::configured(target.price));
};
match pricing.price(&catalog.provider, &catalog.model) {
Some(rates) => Ok(RequestPrice::approved(rates, PriceIdentity::of(pricing))),
None => Err(Ineligible::Unpriced {
provider: catalog.provider.to_string(),
model: catalog.model.clone(),
book: pricing.book().to_string(),
approval: pricing.approval().state(),
}),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::CatalogBinding;
use crate::desired_state::fixtures;
use crate::desired_state::ids::Slug;
use crate::desired_state::pricing::{
Approval, ApprovedRate, ApprovedRates, EffectiveInstant, EffectiveInterval, PriceBookBody,
PriceBooks, PriceOrigin, PriceProvenance, PriceRule, RulePrecedence,
};
use crate::desired_state::resource::ResourceVersionNumber;
fn configured() -> ModelPrice {
ModelPrice {
input_microdollars_per_million: 7,
output_microdollars_per_million: 9,
reasoning_microdollars_per_million: None,
cache_read_microdollars_per_million: None,
cache_write_microdollars_per_million: None,
}
}
fn target(catalog: Option<CatalogBinding>) -> Target {
Target {
provider: "primary".to_owned(),
model: "gpt-4o".to_owned(),
price: configured(),
catalog,
}
}
fn binding(provider: &str, model: &str) -> CatalogBinding {
CatalogBinding::new(provider, model).expect("a catalogue binding")
}
fn pricing(body: &PriceBookBody, version: u64) -> PricingSnapshot {
let mut state = fixtures::state();
state
.insert(body.version_at(
fixtures::resource_id(7),
Slug::parse("baseline").expect("fixture slug"),
ResourceVersionNumber::new(version).expect("a version is not zero"),
))
.expect("a distinct reference");
PriceBooks::of(&state)
.expect("the book is servable state")
.snapshot_at(EffectiveInstant::EPOCH)
.expect("the state holds a book")
}
fn book(input_nanos: u64, output_nanos: u64) -> PriceBookBody {
PriceBookBody::new(
fixtures::catalog_content_id(),
fixtures::catalog_version(),
Approval::Approved {
by: fixtures::actor(),
at: EffectiveInstant::EPOCH,
citation: Some(fixtures::display_name("CHG-1")),
},
)
.with_rule(fixtures::price_rule(
fixtures::priced_target("openai", "gpt-4o"),
RulePrecedence::Baseline,
EffectiveInterval::from(EffectiveInstant::EPOCH),
input_nanos,
output_nanos,
))
}
fn bound() -> Target {
target(Some(binding("openai", "gpt-4o")))
}
fn usage(input_tokens: u64, output_tokens: u64) -> Usage {
Usage {
input_tokens,
output_tokens,
reasoning_tokens: 0,
cache_read_tokens: 0,
cache_write_tokens: 0,
}
}
#[test]
fn a_deployment_with_no_price_book_is_priced_by_its_file() {
let resolved = price_of(None, &target(Some(binding("openai", "gpt-4o"))))
.expect("a file-priced target is chargeable");
assert_eq!(resolved.rates(), configured());
assert_eq!(resolved.catalog_version(), 0);
assert!(resolved.identity().is_none());
}
#[test]
fn a_target_outside_the_books_vocabulary_keeps_its_declared_rates() {
let pricing = fixtures::approved_pricing_snapshot();
let resolved =
price_of(Some(&pricing), &target(None)).expect("an unbound target is chargeable");
assert_eq!(resolved.rates(), configured());
assert!(resolved.identity().is_none());
}
#[test]
fn an_approved_book_prices_a_bound_target_and_names_its_identity() {
let pricing = fixtures::approved_pricing_snapshot();
let resolved = price_of(Some(&pricing), &target(Some(binding("openai", "gpt-4o"))))
.expect("an approved target is chargeable");
assert_eq!(
resolved.rates(),
pricing
.price(&binding("openai", "gpt-4o").provider, "gpt-4o")
.expect("the fixture book prices it")
);
let identity = resolved.identity().expect("the charge names its book");
assert_eq!(identity.version(), pricing.book().version.get());
assert_eq!(
resolved.catalog_version(),
fixtures::catalog_version().get()
);
assert_eq!(
identity.catalog_version(),
fixtures::catalog_version().get()
);
assert_eq!(identity.catalog(), pricing.catalog().to_string());
assert_eq!(identity.checksum(), pricing.checksum().to_string());
}
#[test]
fn an_offering_the_book_does_not_price_is_ineligible_rather_than_free() {
let pricing = fixtures::approved_pricing_snapshot();
let refusal = price_of(Some(&pricing), &target(Some(binding("openai", "o3"))))
.expect_err("an unpriced offering cannot be charged");
let Ineligible::Unpriced { model, .. } = &refusal;
assert_eq!(model, "o3");
}
#[test]
fn a_draft_book_prices_nothing_it_covers() {
let body = PriceBookBody::new(
fixtures::catalog_content_id(),
fixtures::catalog_version(),
Approval::Draft,
)
.with_rule(fixtures::price_rule(
fixtures::priced_target("openai", "gpt-4o"),
RulePrecedence::Baseline,
EffectiveInterval::from(EffectiveInstant::EPOCH),
2_500_000,
10_000_000,
));
let pricing = PriceBooks::of(&fixtures::state_with_price_book(&body))
.expect("a draft book is servable state")
.snapshot_at(EffectiveInstant::EPOCH)
.expect("the state holds a book");
let refusal = price_of(Some(&pricing), &target(Some(binding("openai", "gpt-4o"))))
.expect_err("a draft book activates no price");
let Ineligible::Unpriced { approval, .. } = &refusal;
assert_eq!(*approval, "draft");
}
#[test]
fn a_refusal_names_no_price_book_to_the_caller_and_all_of_it_to_the_log() {
let approved = fixtures::approved_pricing_snapshot();
let draft_body = PriceBookBody::new(
fixtures::catalog_content_id(),
fixtures::catalog_version(),
Approval::Draft,
)
.with_rule(fixtures::price_rule(
fixtures::priced_target("openai", "gpt-4o"),
RulePrecedence::Baseline,
EffectiveInterval::from(EffectiveInstant::EPOCH),
2_500_000,
10_000_000,
));
let draft = PriceBooks::of(&fixtures::state_with_price_book(&draft_body))
.expect("a draft book is servable state")
.snapshot_at(EffectiveInstant::EPOCH)
.expect("the state holds a book");
for (snapshot, model) in [(&approved, "o3"), (&draft, "gpt-4o")] {
let refusal = price_of(Some(snapshot), &target(Some(binding("openai", model))))
.expect_err("neither snapshot has an approved price for it");
assert_eq!(refusal.reason(), "no price is in force for this model");
let public = refusal.to_string();
assert_eq!(public, refusal.reason());
for leak in [
snapshot.book().to_string(),
snapshot.book().id.to_string(),
snapshot.checksum().to_string(),
snapshot.catalog().to_string(),
snapshot.approval().state().to_owned(),
] {
assert!(
!public.contains(&leak),
"refusal `{public}` discloses `{leak}`"
);
}
let detail = refusal.detail();
assert!(detail.contains(&snapshot.book().to_string()), "{detail}");
assert!(detail.contains(snapshot.approval().state()), "{detail}");
assert!(detail.contains(model), "{detail}");
}
}
#[test]
fn a_charges_price_book_renders_as_the_resource_reference_it_is() {
let pricing = fixtures::approved_pricing_snapshot();
let identity = price_of(Some(&pricing), &target(Some(binding("openai", "gpt-4o"))))
.expect("the fixture book prices it")
.identity()
.expect("an approved charge names its book")
.book();
assert_eq!(identity, pricing.book().to_string());
let (kind, version) = identity
.split_once('/')
.and_then(|(kind, rest)| rest.split_once('@').map(|(_, version)| (kind, version)))
.expect("a reference renders as `<kind>/<id>@<version>`");
assert_eq!(kind, "price");
assert!(version.starts_with('v'), "{identity}");
assert!(identity.contains("/res_"), "{identity}");
}
#[test]
fn a_publication_cannot_change_what_an_open_request_settles_at() {
let opened = price_of(Some(&pricing(&book(2_000_000, 4_000_000), 1)), &bound())
.expect("the request opened under an approved price");
let published = pricing(&book(4_000_000, 8_000_000), 2);
let later = price_of(Some(&published), &bound()).expect("a later request is priced too");
assert_eq!(opened.cost_microdollars(usage(1_000_000, 1_000_000)), 6_000);
assert_eq!(later.cost_microdollars(usage(1_000_000, 1_000_000)), 12_000);
assert_eq!(opened.catalog_version(), 3);
assert_eq!(later.catalog_version(), 3);
}
#[test]
fn a_rollback_republishes_the_earlier_rates_under_a_new_version() {
let original = book(2_000_000, 4_000_000);
let amended = book(4_000_000, 8_000_000);
let before = price_of(Some(&pricing(&original, 1)), &bound()).expect("priced");
let after = price_of(Some(&pricing(&amended, 2)), &bound()).expect("priced");
let rolled_back = price_of(Some(&pricing(&original, 3)), &bound()).expect("priced");
assert_eq!(rolled_back.rates(), before.rates());
assert_ne!(rolled_back.rates(), after.rates());
let identity = rolled_back.identity().expect("a rollback names its book");
assert_eq!(identity.version(), 3);
assert_eq!(
identity.checksum(),
before
.identity()
.expect("the original names its book")
.checksum(),
"republishing one body must reproduce its checksum, or a diff of the \
two publications would not show them as the same rates"
);
}
#[test]
fn reasoning_and_cache_rates_bill_their_own_tokens() {
let rates = ApprovedRates {
reasoning: Some(ApprovedRate::from_nanos(20_000_000)),
cache_read: Some(ApprovedRate::from_nanos(1_000_000)),
cache_write: Some(ApprovedRate::from_nanos(3_000_000)),
..ApprovedRates::new(
ApprovedRate::from_nanos(2_000_000),
ApprovedRate::from_nanos(4_000_000),
)
};
let body = PriceBookBody::new(
fixtures::catalog_content_id(),
fixtures::catalog_version(),
Approval::Approved {
by: fixtures::actor(),
at: EffectiveInstant::EPOCH,
citation: Some(fixtures::display_name("CHG-2")),
},
)
.with_rule(
PriceRule::new(
fixtures::priced_target("openai", "gpt-4o"),
RulePrecedence::Baseline,
EffectiveInterval::from(EffectiveInstant::EPOCH),
rates,
PriceProvenance::stated(PriceOrigin::Catalogue),
)
.expect("whole micro-dollar rates convert"),
);
let resolved = price_of(Some(&pricing(&body, 1)), &bound()).expect("priced");
let cost = resolved.cost_microdollars(Usage {
input_tokens: 1_000_000,
output_tokens: 1_000_000,
reasoning_tokens: 1_000_000,
cache_read_tokens: 1_000_000,
cache_write_tokens: 1_000_000,
});
assert_eq!(cost, 26_000);
}
#[test]
fn a_charge_truncates_the_micro_dollar_it_did_not_reach() {
let resolved =
price_of(Some(&pricing(&book(2_000_000, 4_000_000), 1)), &bound()).expect("priced");
assert_eq!(resolved.cost_microdollars(usage(499, 0)), 0);
assert_eq!(resolved.cost_microdollars(usage(500, 0)), 1);
assert_eq!(resolved.cost_microdollars(usage(999, 0)), 1);
}
#[test]
fn an_alias_is_refused_only_when_no_target_of_it_can_be_charged() {
let snapshot = pricing(&book(2_000_000, 4_000_000), 1);
let priced = AliasPrices {
priced: vec![
price_of(Some(&snapshot), &target(Some(binding("openai", "o3")))),
price_of(Some(&snapshot), &bound()),
],
};
assert!(priced.refusal().is_none());
assert!(priced.get(0).is_none());
assert_eq!(priced.get(1), priced.estimate());
let all_unpriced = AliasPrices {
priced: vec![price_of(
Some(&snapshot),
&target(Some(binding("openai", "o3"))),
)],
};
assert!(all_unpriced.estimate().is_none());
assert!(all_unpriced.refusal().is_some());
}
}