//! Append-only usage observations and the derived footer projection. //! //! Decision 0002 requires every usage value to carry its provenance, and the //! product footer must answer five questions without inventing precision: //! which profile is active, how much of the window is consumed, when the window //! resets, what the burn rate is, and how trustworthy those numbers are. //! //! This module stores facts, derives views from them, and reports what it //! cannot know. Nothing here reads a clock: callers supply timestamps so the //! projection is deterministic and testable. All arithmetic is integer //! arithmetic; fractions are carried as permille so a rendered percentage never //! drifts from the stored fact. //! //! ``` //! use lumbridge_core::{ //! AccountProfile, FooterUsage, UsageLedger, UsageObservation, UsageProvenance, UsageUnit, //! UsageWindow, //! }; //! //! let profile = AccountProfile::new("codex-plus", "Codex", "ChatGPT", "gpt-5.2", "work")?; //! let window = UsageWindow::new(0, 18_000_000)?; //! let mut ledger = UsageLedger::new(); //! //! for (at_ms, consumed) in [(0, 0), (3_600_000, 620_000)] { //! ledger.record( //! UsageObservation::counted( //! profile.id().clone(), //! UsageUnit::Tokens, //! consumed, //! UsageProvenance::ProviderReported, //! at_ms, //! )? //! .with_limit(1_000_000)? //! .with_window(window)?, //! )?; //! } //! //! let projection = ledger.project(profile.id(), 3_600_000); //! assert_eq!(projection.consumed_permille(), Some(620)); //! assert_eq!(projection.burn_per_hour(), Some(620_000)); //! let footer = FooterUsage::new(&profile, &projection); //! assert_eq!(footer.window(), "62.0% used"); //! # Ok::<(), lumbridge_core::UsageError>(()) //! ``` use std::collections::{BTreeMap, VecDeque}; use std::fmt; use thiserror::Error; const MAX_IDENTITY_BYTES: usize = 256; const DEFAULT_RETENTION_PER_PROFILE: usize = 64; const MIN_RETENTION_PER_PROFILE: usize = 2; const DEFAULT_STALENESS_BUDGET_MS: u64 = 300_000; const MIN_BURN_SAMPLE_MS: u64 = 1_000; const MILLIS_PER_HOUR: u128 = 3_600_000; const MILLIS_PER_MINUTE: u64 = 60_000; const MILLIS_PER_SECOND: u64 = 1_000; const MINUTES_PER_HOUR: u64 = 60; const HOURS_PER_DAY: u64 = 24; const PERMILLE_SCALE: u128 = 1_000; const MAX_PERMILLE: u64 = 1_000; /// Failures produced while recording or shaping usage facts. #[derive(Clone, Debug, Eq, Error, PartialEq)] pub enum UsageError { #[error("invalid {0}")] InvalidIdentifier(&'static str), #[error("a usage window must reset after it starts")] EmptyWindow, #[error("a usage limit must be greater than zero")] ZeroLimit, #[error("observation at {observed_at_ms} ms precedes the newest observation at {newest_ms} ms")] OutOfOrderObservation { observed_at_ms: u64, newest_ms: u64 }, #[error("observation at {observed_at_ms} ms falls outside its declared window")] ObservationOutsideWindow { observed_at_ms: u64 }, #[error("retention must keep at least two observations per profile")] RetentionTooSmall, #[error("an unavailable observation cannot carry a counted value")] UnavailableWithValue, } /// Where a usage number came from. /// /// The ordering is deliberate: variants are declared from most to least /// authoritative so [`UsageProvenance::weakest`] can take the maximum. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum UsageProvenance { /// The provider's own account or quota API reported this value. ProviderReported, /// The upstream harness reported it through a documented surface. HarnessReported, /// Lumbridge counted it locally from traffic it owns. LocallyMeasured, /// Lumbridge derived it from other observations. Estimated, /// No usable value exists. This is a fact, not a zero. Unavailable, } impl UsageProvenance { /// The label kept in detail views and exports. #[must_use] pub const fn label(self) -> &'static str { match self { Self::ProviderReported => "provider-reported", Self::HarnessReported => "harness-reported", Self::LocallyMeasured => "locally measured", Self::Estimated => "estimated", Self::Unavailable => "unavailable", } } /// The short label a compact surface such as the footer may substitute. #[must_use] pub const fn compact_label(self) -> &'static str { match self { Self::ProviderReported => "provider", Self::HarnessReported => "harness", Self::LocallyMeasured => "local", Self::Estimated => "est", Self::Unavailable => "none", } } /// Whether an external party stated this value rather than Lumbridge. #[must_use] pub const fn is_reported(self) -> bool { matches!(self, Self::ProviderReported | Self::HarnessReported) } #[must_use] pub const fn is_available(self) -> bool { !matches!(self, Self::Unavailable) } #[must_use] pub const fn confidence(self) -> UsageConfidence { match self { Self::ProviderReported => UsageConfidence::Exact, Self::HarnessReported | Self::LocallyMeasured => UsageConfidence::Approximate, Self::Estimated => UsageConfidence::Projected, Self::Unavailable => UsageConfidence::Unknown, } } /// The less authoritative of two provenances. /// /// A derived value is only as trustworthy as its weakest input. #[must_use] pub fn weakest(self, other: Self) -> Self { self.max(other) } } /// How much a displayed number can be trusted. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum UsageConfidence { Exact, Approximate, Projected, Unknown, } impl UsageConfidence { #[must_use] pub const fn label(self) -> &'static str { match self { Self::Exact => "exact", Self::Approximate => "approximate", Self::Projected => "projected", Self::Unknown => "unknown", } } } /// What a usage number counts. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum UsageUnit { Tokens, Requests, /// A window fraction the provider reports directly, in permille. WindowPermille, } impl UsageUnit { #[must_use] pub const fn label(self) -> &'static str { match self { Self::Tokens => "tokens", Self::Requests => "requests", Self::WindowPermille => "window", } } /// Renders an amount in this unit without inflating its precision. #[must_use] pub fn format_amount(self, amount: u64) -> String { match self { Self::Tokens => format_count(amount, "tokens"), Self::Requests => format_count(amount, "requests"), Self::WindowPermille => format_permille(amount), } } } fn format_count(amount: u64, suffix: &str) -> String { if amount >= 1_000_000 { format!( "{}.{}M {suffix}", amount / 1_000_000, (amount % 1_000_000) / 100_000 ) } else if amount >= 1_000 { format!("{}.{}k {suffix}", amount / 1_000, (amount % 1_000) / 100) } else { format!("{amount} {suffix}") } } fn format_permille(permille: u64) -> String { format!("{}.{}%", permille / 10, permille % 10) } /// Renders a duration for a compact surface. Never rounds up to a false zero. #[must_use] pub fn format_duration_ms(duration_ms: u64) -> String { if duration_ms < MILLIS_PER_MINUTE { return format!("{}s", duration_ms / MILLIS_PER_SECOND); } let minutes = duration_ms / MILLIS_PER_MINUTE; if minutes < MINUTES_PER_HOUR { return format!("{minutes}m"); } let hours = minutes / MINUTES_PER_HOUR; if hours < HOURS_PER_DAY { return format!("{hours}h {}m", minutes % MINUTES_PER_HOUR); } // Weekly and monthly quota windows are common, and "151h 30m" is not a // duration anyone reads at a glance. format!("{}d {}h", hours / HOURS_PER_DAY, hours % HOURS_PER_DAY) } fn validate_identity(value: &str, label: &'static str) -> Result<(), UsageError> { if value.trim().is_empty() || value.len() > MAX_IDENTITY_BYTES || value.chars().any(char::is_control) { return Err(UsageError::InvalidIdentifier(label)); } Ok(()) } /// A stable identifier for one harness/provider/account combination. #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AccountProfileId(String); impl AccountProfileId { /// Creates a stable, non-empty account profile identifier. /// /// # Errors /// /// Returns [`UsageError::InvalidIdentifier`] for an empty, oversized, or /// control-bearing identifier. pub fn new(value: impl Into) -> Result { let value = value.into(); validate_identity(&value, "account profile ID")?; Ok(Self(value)) } #[must_use] pub fn as_str(&self) -> &str { &self.0 } } impl fmt::Display for AccountProfileId { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(&self.0) } } /// The active harness, provider, model, and account label shown in the footer. /// /// These are display names only. Credentials stay in the operating-system /// credential store and are never part of this type. #[derive(Clone, Debug, Eq, PartialEq)] pub struct AccountProfile { id: AccountProfileId, harness: String, provider: String, model: String, account: String, } impl AccountProfile { /// Creates a footer-ready account profile. /// /// # Errors /// /// Returns [`UsageError::InvalidIdentifier`] when any field is empty, /// oversized, or contains control characters. pub fn new( id: impl Into, harness: impl Into, provider: impl Into, model: impl Into, account: impl Into, ) -> Result { let harness = harness.into(); let provider = provider.into(); let model = model.into(); let account = account.into(); validate_identity(&harness, "harness name")?; validate_identity(&provider, "provider name")?; validate_identity(&model, "model name")?; validate_identity(&account, "account label")?; Ok(Self { id: AccountProfileId::new(id)?, harness, provider, model, account, }) } #[must_use] pub const fn id(&self) -> &AccountProfileId { &self.id } #[must_use] pub fn harness(&self) -> &str { &self.harness } #[must_use] pub fn provider(&self) -> &str { &self.provider } #[must_use] pub fn model(&self) -> &str { &self.model } #[must_use] pub fn account(&self) -> &str { &self.account } /// The footer's first answer: which harness, provider, and model is active. #[must_use] pub fn identity_line(&self) -> String { format!("{} · {} · {}", self.harness, self.provider, self.model) } } /// A provider-declared usage window. /// /// The start is optional because providers report these fields independently: /// a reset time can arrive without a window length. Knowing when a window ends /// is the useful half — it bounds the exhaustion forecast and identifies which /// window an observation belongs to — so discarding it for want of a start /// would throw away a fact the provider actually stated. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct UsageWindow { started_at_ms: Option, resets_at_ms: u64, } impl UsageWindow { /// Creates a window that resets strictly after it starts. /// /// # Errors /// /// Returns [`UsageError::EmptyWindow`] when the reset is not after the start. pub const fn new(started_at_ms: u64, resets_at_ms: u64) -> Result { if resets_at_ms <= started_at_ms { return Err(UsageError::EmptyWindow); } Ok(Self { started_at_ms: Some(started_at_ms), resets_at_ms, }) } /// Creates a window whose reset is known and whose start is not. /// /// Two of these compare equal when they reset at the same moment, which is /// what lets a burn rate be scoped to one window without inventing a start. #[must_use] pub const fn until(resets_at_ms: u64) -> Self { Self { started_at_ms: None, resets_at_ms, } } /// When the window opened, if the provider said. #[must_use] pub const fn started_at_ms(self) -> Option { self.started_at_ms } #[must_use] pub const fn resets_at_ms(self) -> u64 { self.resets_at_ms } #[must_use] pub const fn contains(self, at_ms: u64) -> bool { if at_ms >= self.resets_at_ms { return false; } match self.started_at_ms { Some(started_at_ms) => at_ms >= started_at_ms, None => true, } } #[must_use] pub const fn has_expired(self, now_ms: u64) -> bool { now_ms >= self.resets_at_ms } #[must_use] pub const fn remaining_ms(self, now_ms: u64) -> u64 { self.resets_at_ms.saturating_sub(now_ms) } } /// One recorded usage fact. Observations are never edited, only appended. #[derive(Clone, Debug, Eq, PartialEq)] pub struct UsageObservation { profile: AccountProfileId, unit: Option, consumed: u64, limit: Option, window: Option, provenance: UsageProvenance, observed_at_ms: u64, } impl UsageObservation { /// Records a counted usage value with its provenance. /// /// # Errors /// /// Returns [`UsageError::UnavailableWithValue`] when the caller labels a /// counted value as unavailable. Use [`UsageObservation::unavailable`] to /// record a genuine gap. pub fn counted( profile: AccountProfileId, unit: UsageUnit, consumed: u64, provenance: UsageProvenance, observed_at_ms: u64, ) -> Result { if !provenance.is_available() { return Err(UsageError::UnavailableWithValue); } Ok(Self { profile, unit: Some(unit), consumed, limit: None, window: None, provenance, observed_at_ms, }) } /// Records that no usage value could be obtained at this moment. /// /// This is deliberately a first-class observation. Recording it stops the /// projection from presenting an older reading as current. #[must_use] pub const fn unavailable(profile: AccountProfileId, observed_at_ms: u64) -> Self { Self { profile, unit: None, consumed: 0, limit: None, window: None, provenance: UsageProvenance::Unavailable, observed_at_ms, } } /// Attaches the quota ceiling this observation was measured against. /// /// # Errors /// /// Returns [`UsageError::ZeroLimit`] for a zero limit and /// [`UsageError::UnavailableWithValue`] for an unavailable observation. pub fn with_limit(mut self, limit: u64) -> Result { if !self.provenance.is_available() { return Err(UsageError::UnavailableWithValue); } if limit == 0 { return Err(UsageError::ZeroLimit); } self.limit = Some(limit); Ok(self) } /// Attaches the provider window this observation belongs to. /// /// # Errors /// /// Returns [`UsageError::ObservationOutsideWindow`] when the observation /// time is not inside the window, and [`UsageError::UnavailableWithValue`] /// for an unavailable observation. pub fn with_window(mut self, window: UsageWindow) -> Result { if !self.provenance.is_available() { return Err(UsageError::UnavailableWithValue); } if !window.contains(self.observed_at_ms) { return Err(UsageError::ObservationOutsideWindow { observed_at_ms: self.observed_at_ms, }); } self.window = Some(window); Ok(self) } #[must_use] pub const fn profile(&self) -> &AccountProfileId { &self.profile } #[must_use] pub const fn unit(&self) -> Option { self.unit } #[must_use] pub const fn consumed(&self) -> u64 { self.consumed } #[must_use] pub const fn limit(&self) -> Option { self.limit } #[must_use] pub const fn window(&self) -> Option { self.window } #[must_use] pub const fn provenance(&self) -> UsageProvenance { self.provenance } #[must_use] pub const fn observed_at_ms(&self) -> u64 { self.observed_at_ms } /// Units left before the ceiling, when a ceiling is known. #[must_use] pub fn remaining(&self) -> Option { self.limit.map(|limit| limit.saturating_sub(self.consumed)) } } /// A bounded, append-only observation stream per account profile. #[derive(Clone, Debug)] pub struct UsageLedger { retention_per_profile: usize, staleness_budget_ms: u64, observations: BTreeMap>, } impl Default for UsageLedger { fn default() -> Self { Self::new() } } impl UsageLedger { #[must_use] pub fn new() -> Self { Self { retention_per_profile: DEFAULT_RETENTION_PER_PROFILE, staleness_budget_ms: DEFAULT_STALENESS_BUDGET_MS, observations: BTreeMap::new(), } } /// Creates a ledger that retains a bounded history per profile. /// /// # Errors /// /// Returns [`UsageError::RetentionTooSmall`] below two observations, which /// is the minimum a burn rate can be derived from. pub fn with_retention(retention_per_profile: usize) -> Result { if retention_per_profile < MIN_RETENTION_PER_PROFILE { return Err(UsageError::RetentionTooSmall); } Ok(Self { retention_per_profile, ..Self::new() }) } /// Sets how old the newest observation may be before it reads as stale. #[must_use] pub const fn with_staleness_budget_ms(mut self, staleness_budget_ms: u64) -> Self { self.staleness_budget_ms = staleness_budget_ms; self } /// Appends an observation to its profile's history. /// /// # Errors /// /// Returns [`UsageError::OutOfOrderObservation`] when the observation is /// older than the newest one already recorded for that profile. The stream /// is append-only, so time must not move backwards. pub fn record(&mut self, observation: UsageObservation) -> Result<(), UsageError> { let history = self .observations .entry(observation.profile.clone()) .or_default(); if let Some(newest) = history.back() && observation.observed_at_ms < newest.observed_at_ms { return Err(UsageError::OutOfOrderObservation { observed_at_ms: observation.observed_at_ms, newest_ms: newest.observed_at_ms, }); } history.push_back(observation); while history.len() > self.retention_per_profile { history.pop_front(); } Ok(()) } #[must_use] pub fn latest(&self, profile: &AccountProfileId) -> Option<&UsageObservation> { self.observations.get(profile).and_then(VecDeque::back) } #[must_use] pub fn observation_count(&self, profile: &AccountProfileId) -> usize { self.observations.get(profile).map_or(0, VecDeque::len) } pub fn profiles(&self) -> impl Iterator { self.observations.keys() } /// Derives the current view of a profile's usage. /// /// Reported facts keep their own provenance. Anything this method computes /// is labelled [`UsageProvenance::Estimated`], even when every input was /// provider-reported: a derived rate is not a reported fact. #[must_use] pub fn project(&self, profile: &AccountProfileId, now_ms: u64) -> UsageProjection { let Some(history) = self.observations.get(profile) else { return UsageProjection::unavailable(None); }; let Some(latest) = history.back() else { return UsageProjection::unavailable(None); }; if !latest.provenance.is_available() { return UsageProjection::unavailable(Some(latest.observed_at_ms)); } if let Some(window) = latest.window && window.has_expired(now_ms) { return UsageProjection::window_rolled(latest.observed_at_ms); } let consumed_permille = latest .limit .map(|limit| permille_of(latest.consumed, limit)) .or_else(|| { matches!(latest.unit, Some(UsageUnit::WindowPermille)) .then_some(latest.consumed.min(MAX_PERMILLE)) }); let burn_per_hour = burn_per_hour(history, latest); let resets_in_ms = latest.window.map(|window| window.remaining_ms(now_ms)); let exhaustion_in_ms = burn_per_hour .and_then(|rate| exhaustion_in_ms(latest.remaining()?, rate, resets_in_ms)); UsageProjection { unit: latest.unit, consumed: Some(latest.consumed), limit: latest.limit, consumed_permille, resets_in_ms, burn_per_hour, exhaustion_in_ms, provenance: latest.provenance, stale: now_ms.saturating_sub(latest.observed_at_ms) > self.staleness_budget_ms, window_rolled: false, observed_at_ms: Some(latest.observed_at_ms), } } } fn permille_of(consumed: u64, limit: u64) -> u64 { if limit == 0 { return 0; } let permille = u128::from(consumed) * PERMILLE_SCALE / u128::from(limit); u64::try_from(permille) .unwrap_or(MAX_PERMILLE) .min(MAX_PERMILLE) } /// Averages consumption across the newest observation's window. /// /// Returns `None` unless two observations share a window and a unit, are far /// enough apart to divide by, and did not move backwards. A single reading /// yields no rate at all rather than a rate of zero. fn burn_per_hour(history: &VecDeque, latest: &UsageObservation) -> Option { let baseline = history.iter().find(|candidate| { candidate.window == latest.window && candidate.unit == latest.unit && candidate.provenance.is_available() && candidate.observed_at_ms < latest.observed_at_ms })?; if latest.consumed < baseline.consumed { return None; } let elapsed_ms = latest.observed_at_ms - baseline.observed_at_ms; if elapsed_ms < MIN_BURN_SAMPLE_MS { return None; } let consumed_delta = u128::from(latest.consumed - baseline.consumed); let rate = consumed_delta * MILLIS_PER_HOUR / u128::from(elapsed_ms); u64::try_from(rate).ok() } /// Projects when a ceiling is reached, unless the window resets first. fn exhaustion_in_ms(remaining: u64, burn_per_hour: u64, resets_in_ms: Option) -> Option { if burn_per_hour == 0 { return None; } let millis = u128::from(remaining) * MILLIS_PER_HOUR / u128::from(burn_per_hour); let millis = u64::try_from(millis).ok()?; match resets_in_ms { Some(resets_in_ms) if millis >= resets_in_ms => None, _ => Some(millis), } } /// The derived view a footer or detail panel renders. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct UsageProjection { unit: Option, consumed: Option, limit: Option, consumed_permille: Option, resets_in_ms: Option, burn_per_hour: Option, exhaustion_in_ms: Option, provenance: UsageProvenance, stale: bool, window_rolled: bool, observed_at_ms: Option, } impl UsageProjection { const fn unavailable(observed_at_ms: Option) -> Self { Self { unit: None, consumed: None, limit: None, consumed_permille: None, resets_in_ms: None, burn_per_hour: None, exhaustion_in_ms: None, provenance: UsageProvenance::Unavailable, stale: false, window_rolled: false, observed_at_ms, } } const fn window_rolled(observed_at_ms: u64) -> Self { Self { stale: true, window_rolled: true, ..Self::unavailable(Some(observed_at_ms)) } } #[must_use] pub const fn unit(self) -> Option { self.unit } #[must_use] pub const fn consumed(self) -> Option { self.consumed } #[must_use] pub const fn limit(self) -> Option { self.limit } /// Share of the window consumed, in permille, when a ceiling is known. #[must_use] pub const fn consumed_permille(self) -> Option { self.consumed_permille } #[must_use] pub const fn resets_in_ms(self) -> Option { self.resets_in_ms } /// Units per hour averaged over the current window. #[must_use] pub const fn burn_per_hour(self) -> Option { self.burn_per_hour } /// Time until the ceiling is reached, absent unless it precedes the reset. #[must_use] pub const fn exhaustion_in_ms(self) -> Option { self.exhaustion_in_ms } /// The provenance of the reported facts in this projection. #[must_use] pub const fn provenance(self) -> UsageProvenance { self.provenance } /// The provenance of the burn rate, which is always derived. #[must_use] pub const fn burn_provenance(self) -> UsageProvenance { match self.burn_per_hour { Some(_) => UsageProvenance::Estimated, None => UsageProvenance::Unavailable, } } /// The trust classification a detail view must keep. #[must_use] pub const fn confidence(self) -> UsageConfidence { if self.stale { return UsageConfidence::Unknown; } self.provenance.confidence() } /// Whether the newest observation is older than the staleness budget. #[must_use] pub const fn is_stale(self) -> bool { self.stale } /// Whether the last known window ended before this projection was taken. #[must_use] pub const fn window_has_rolled(self) -> bool { self.window_rolled } #[must_use] pub const fn observed_at_ms(self) -> Option { self.observed_at_ms } #[must_use] pub const fn is_available(self) -> bool { self.provenance.is_available() } } /// Renders the five footer answers for one profile. /// /// Every method returns an explicit unavailable phrase instead of a placeholder /// number, so a missing fact reads as missing rather than as zero. #[derive(Clone, Copy, Debug)] pub struct FooterUsage<'a> { profile: &'a AccountProfile, projection: &'a UsageProjection, } impl<'a> FooterUsage<'a> { #[must_use] pub const fn new(profile: &'a AccountProfile, projection: &'a UsageProjection) -> Self { Self { profile, projection, } } /// Which harness, provider, model, and account profile is active. #[must_use] pub fn identity(&self) -> String { format!( "{} · {}", self.profile.identity_line(), self.profile.account() ) } /// How much of the current usage window has been consumed. #[must_use] pub fn window(&self) -> String { match ( self.projection.consumed_permille(), self.projection.consumed(), self.projection.unit(), ) { (Some(permille), _, _) => format!("{} used", format_permille(permille)), (None, Some(consumed), Some(unit)) => { format!( "{} used · no ceiling reported", unit.format_amount(consumed) ) } _ if self.projection.window_has_rolled() => "window rolled over".to_owned(), _ => "usage unavailable".to_owned(), } } /// When the window resets. #[must_use] pub fn reset(&self) -> String { self.projection.resets_in_ms().map_or_else( || "reset time unavailable".to_owned(), |remaining| format!("resets in {}", format_duration_ms(remaining)), ) } /// The recent burn rate and, when meaningful, the projected exhaustion. #[must_use] pub fn burn(&self) -> String { let Some(rate) = self.projection.burn_per_hour() else { return "burn rate unavailable".to_owned(); }; let unit = self.projection.unit().unwrap_or(UsageUnit::Tokens); let rate = format!("~{}/hr est", unit.format_amount(rate)); self.projection .exhaustion_in_ms() .map_or(rate.clone(), |exhaustion| { format!("{rate} · empty in {}", format_duration_ms(exhaustion)) }) } /// How trustworthy the numbers above are. #[must_use] pub fn trust(&self) -> String { let provenance = self.projection.provenance(); if !provenance.is_available() { return if self.projection.window_has_rolled() { "awaiting a new window reading".to_owned() } else { "no usage source".to_owned() }; } let base = format!( "{} · {}", provenance.compact_label(), self.projection.confidence().label() ); if self.projection.is_stale() { return format!("{base} · stale"); } base } } impl fmt::Display for FooterUsage<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!( formatter, "{} · {} · {} · {} · {}", self.identity(), self.window(), self.reset(), self.burn(), self.trust() ) } } #[cfg(test)] mod tests { use super::{ AccountProfile, AccountProfileId, FooterUsage, UsageConfidence, UsageError, UsageLedger, UsageObservation, UsageProvenance, UsageUnit, UsageWindow, format_duration_ms, }; const HOUR_MS: u64 = 3_600_000; fn profile() -> AccountProfile { AccountProfile::new("codex-plus", "Codex", "ChatGPT", "gpt-5.2", "work") .expect("the fixture profile is valid") } fn window() -> UsageWindow { UsageWindow::new(0, 5 * HOUR_MS).expect("the fixture window is valid") } fn observation(consumed: u64, at_ms: u64, provenance: UsageProvenance) -> UsageObservation { UsageObservation::counted( AccountProfileId::new("codex-plus").expect("valid ID"), UsageUnit::Tokens, consumed, provenance, at_ms, ) .expect("a counted observation is available") .with_limit(1_000_000) .expect("a positive limit is valid") .with_window(window()) .expect("the observation is inside the window") } fn ledger_with(samples: &[(u64, u64)]) -> UsageLedger { let mut ledger = UsageLedger::new(); for &(consumed, at_ms) in samples { ledger .record(observation( consumed, at_ms, UsageProvenance::ProviderReported, )) .expect("fixture observations are ordered"); } ledger } #[test] fn provenance_labels_and_confidence_are_stable() { assert_eq!( UsageProvenance::ProviderReported.label(), "provider-reported" ); assert_eq!(UsageProvenance::Estimated.compact_label(), "est"); assert!(UsageProvenance::HarnessReported.is_reported()); assert!(!UsageProvenance::LocallyMeasured.is_reported()); assert_eq!( UsageProvenance::ProviderReported.confidence(), UsageConfidence::Exact ); assert_eq!( UsageProvenance::Unavailable.confidence(), UsageConfidence::Unknown ); } #[test] fn the_weakest_provenance_wins() { assert_eq!( UsageProvenance::ProviderReported.weakest(UsageProvenance::Estimated), UsageProvenance::Estimated ); assert_eq!( UsageProvenance::Estimated.weakest(UsageProvenance::Unavailable), UsageProvenance::Unavailable ); } #[test] fn a_counted_value_cannot_be_labelled_unavailable() { let error = UsageObservation::counted( AccountProfileId::new("codex-plus").expect("valid ID"), UsageUnit::Tokens, 10, UsageProvenance::Unavailable, 0, ) .expect_err("unavailable counted values are rejected"); assert_eq!(error, UsageError::UnavailableWithValue); } #[test] fn windows_and_limits_are_validated() { assert_eq!(UsageWindow::new(10, 10), Err(UsageError::EmptyWindow)); let observation = UsageObservation::counted( AccountProfileId::new("codex-plus").expect("valid ID"), UsageUnit::Tokens, 10, UsageProvenance::ProviderReported, 9 * HOUR_MS, ) .expect("a counted observation is available"); assert_eq!( observation.clone().with_limit(0), Err(UsageError::ZeroLimit) ); assert!(matches!( observation.with_window(window()), Err(UsageError::ObservationOutsideWindow { .. }) )); } #[test] fn the_ledger_refuses_to_move_backwards() { let mut ledger = ledger_with(&[(0, 0), (100, HOUR_MS)]); let error = ledger .record(observation( 50, HOUR_MS / 2, UsageProvenance::ProviderReported, )) .expect_err("an older observation is rejected"); assert!(matches!( error, UsageError::OutOfOrderObservation { newest_ms, .. } if newest_ms == HOUR_MS )); } #[test] fn retention_is_bounded_and_keeps_the_newest_samples() { let mut ledger = UsageLedger::with_retention(2).expect("two samples are allowed"); for step in 0..5 { ledger .record(observation( step * 1_000, step * HOUR_MS / 4, UsageProvenance::ProviderReported, )) .expect("ordered observations are accepted"); } let id = AccountProfileId::new("codex-plus").expect("valid ID"); assert_eq!(ledger.observation_count(&id), 2); assert_eq!( ledger.latest(&id).map(UsageObservation::consumed), Some(4_000) ); assert!(matches!( UsageLedger::with_retention(1), Err(UsageError::RetentionTooSmall) )); } #[test] fn a_single_observation_yields_no_burn_rate() { let ledger = ledger_with(&[(620_000, 0)]); let projection = ledger.project(profile().id(), 0); assert_eq!(projection.consumed_permille(), Some(620)); assert_eq!(projection.burn_per_hour(), None, "a rate needs two samples"); assert_eq!(projection.exhaustion_in_ms(), None); } #[test] fn burn_and_exhaustion_are_derived_from_the_window() { let ledger = ledger_with(&[(0, 0), (300_000, HOUR_MS)]); let projection = ledger.project(profile().id(), HOUR_MS); assert_eq!(projection.burn_per_hour(), Some(300_000)); assert_eq!(projection.consumed_permille(), Some(300)); assert_eq!(projection.resets_in_ms(), Some(4 * HOUR_MS)); assert_eq!( projection.exhaustion_in_ms(), Some(2 * HOUR_MS + 20 * 60_000), "700k remaining at 300k/hr is reached before the four-hour reset" ); assert_eq!(projection.burn_provenance(), UsageProvenance::Estimated); assert_eq!(projection.provenance(), UsageProvenance::ProviderReported); } #[test] fn exhaustion_is_withheld_when_the_window_resets_first() { let ledger = ledger_with(&[(0, 0), (10_000, HOUR_MS)]); let projection = ledger.project(profile().id(), HOUR_MS); assert_eq!(projection.burn_per_hour(), Some(10_000)); assert_eq!( projection.exhaustion_in_ms(), None, "the window resets long before the ceiling is reached" ); } #[test] fn an_unavailable_observation_invalidates_older_facts() { let mut ledger = ledger_with(&[(0, 0), (200_000, HOUR_MS)]); ledger .record(UsageObservation::unavailable( AccountProfileId::new("codex-plus").expect("valid ID"), 2 * HOUR_MS, )) .expect("a gap is recordable"); let projection = ledger.project(profile().id(), 2 * HOUR_MS); assert!(!projection.is_available()); assert_eq!(projection.consumed_permille(), None); assert_eq!(projection.burn_per_hour(), None); assert_eq!(projection.confidence(), UsageConfidence::Unknown); } #[test] fn an_expired_window_is_not_presented_as_current() { let ledger = ledger_with(&[(0, 0), (200_000, HOUR_MS)]); let projection = ledger.project(profile().id(), 6 * HOUR_MS); assert!(projection.window_has_rolled()); assert!(projection.is_stale()); assert_eq!(projection.consumed_permille(), None); assert_eq!(projection.burn_per_hour(), None); } #[test] fn an_unknown_profile_projects_as_unavailable() { let ledger = UsageLedger::new(); let projection = ledger.project(profile().id(), 0); assert!(!projection.is_available()); assert_eq!(projection.observed_at_ms(), None); } #[test] fn staleness_demotes_confidence_without_erasing_the_fact() { let ledger = UsageLedger::new().with_staleness_budget_ms(1_000); let mut ledger = ledger; ledger .record(observation(200_000, 0, UsageProvenance::ProviderReported)) .expect("ordered"); ledger .record(observation( 400_000, HOUR_MS, UsageProvenance::ProviderReported, )) .expect("ordered"); let projection = ledger.project(profile().id(), HOUR_MS + 5_000); assert!(projection.is_stale()); assert_eq!(projection.consumed_permille(), Some(400)); assert_eq!(projection.confidence(), UsageConfidence::Unknown); } #[test] fn the_footer_answers_five_questions() { let ledger = ledger_with(&[(0, 0), (620_000, HOUR_MS)]); let projection = ledger.project(profile().id(), HOUR_MS); let profile = profile(); let footer = FooterUsage::new(&profile, &projection); assert_eq!(footer.identity(), "Codex · ChatGPT · gpt-5.2 · work"); assert_eq!(footer.window(), "62.0% used"); assert_eq!(footer.reset(), "resets in 4h 0m"); assert!(footer.burn().starts_with("~620.0k tokens/hr est")); assert_eq!(footer.trust(), "provider · exact"); } #[test] fn the_footer_says_unavailable_instead_of_zero() { let ledger = UsageLedger::new(); let projection = ledger.project(profile().id(), 0); let profile = profile(); let footer = FooterUsage::new(&profile, &projection); assert_eq!(footer.window(), "usage unavailable"); assert_eq!(footer.reset(), "reset time unavailable"); assert_eq!(footer.burn(), "burn rate unavailable"); assert_eq!(footer.trust(), "no usage source"); assert!(!footer.to_string().contains('%')); } #[test] fn a_locally_measured_count_without_a_ceiling_says_so() { let mut ledger = UsageLedger::new(); ledger .record( UsageObservation::counted( AccountProfileId::new("codex-plus").expect("valid ID"), UsageUnit::Requests, 42, UsageProvenance::LocallyMeasured, 0, ) .expect("available"), ) .expect("ordered"); let projection = ledger.project(profile().id(), 0); let profile = profile(); let footer = FooterUsage::new(&profile, &projection); assert_eq!(footer.window(), "42 requests used · no ceiling reported"); assert_eq!(footer.trust(), "local · approximate"); } #[test] fn durations_render_without_false_precision() { assert_eq!(format_duration_ms(45_000), "45s"); assert_eq!(format_duration_ms(14 * 60_000), "14m"); assert_eq!(format_duration_ms(2 * HOUR_MS + 14 * 60_000), "2h 14m"); // A weekly quota window is a real provider shape, not a hypothetical. assert_eq!(format_duration_ms(10_080 * 60_000), "7d 0h"); assert_eq!(format_duration_ms(151 * HOUR_MS + 30 * 60_000), "6d 7h"); } #[test] fn a_window_can_know_its_reset_without_knowing_its_start() { let window = UsageWindow::until(5 * HOUR_MS); assert_eq!(window.started_at_ms(), None); assert_eq!(window.resets_at_ms(), 5 * HOUR_MS); assert!(window.contains(0), "an unknown start bounds nothing below"); assert!(window.contains(5 * HOUR_MS - 1)); assert!( !window.contains(5 * HOUR_MS), "the reset still bounds it above" ); assert!(window.has_expired(5 * HOUR_MS)); assert_eq!(window.remaining_ms(HOUR_MS), 4 * HOUR_MS); } #[test] fn windows_with_the_same_reset_share_an_identity() { assert_eq!(UsageWindow::until(HOUR_MS), UsageWindow::until(HOUR_MS)); assert_ne!(UsageWindow::until(HOUR_MS), UsageWindow::until(2 * HOUR_MS)); assert_ne!( UsageWindow::until(HOUR_MS), UsageWindow::new(0, HOUR_MS).expect("a bounded window is valid"), "a known start is a different fact from an unknown one" ); } #[test] fn a_start_only_window_still_scopes_a_burn_rate_to_one_period() { // The provider reported a reset but no window length, then the quota // rolled. A baseline must not be drawn across the two periods. let id = AccountProfileId::new("codex-plus").expect("valid ID"); let mut ledger = UsageLedger::new(); for (consumed, at_ms, resets_at_ms) in [ (800, 0, 5 * HOUR_MS), (50, 5 * HOUR_MS + 1_000, 10 * HOUR_MS), (850, 6 * HOUR_MS, 10 * HOUR_MS), ] { ledger .record( UsageObservation::counted( id.clone(), UsageUnit::WindowPermille, consumed, UsageProvenance::ProviderReported, at_ms, ) .expect("available") .with_limit(1_000) .expect("a positive limit") .with_window(UsageWindow::until(resets_at_ms)) .expect("the observation precedes its reset"), ) .expect("ordered"); } let projection = ledger.project(&id, 6 * HOUR_MS); assert_eq!(projection.resets_in_ms(), Some(4 * HOUR_MS)); assert_eq!( projection.burn_per_hour(), Some(800), "the baseline must be the reading from this window, not the last one" ); assert_eq!( projection.exhaustion_in_ms(), Some(675_000), "150 permille left at 800 per hour is about eleven minutes" ); } #[test] fn identities_reject_empty_and_control_values() { assert!(matches!( AccountProfile::new("id", "", "p", "m", "a"), Err(UsageError::InvalidIdentifier("harness name")) )); assert!(matches!( AccountProfileId::new("bad\nid"), Err(UsageError::InvalidIdentifier("account profile ID")) )); } }