pub struct ProviderUsage {
pub provider: String,
pub api_provider: Option<String>,
pub account: Option<String>,
pub source: Option<String>,
pub account_info: Option<AccountInfo>,
pub fetched_at: Option<String>,
pub saved_resets: Option<SavedResets>,
pub usage: Option<Usage>,
pub spend: Option<Vec<Pool>>,
pub error: Option<String>,
pub error_class: Option<String>,
pub stale: Option<Stale>,
}Expand description
One provider/account’s usage entry. The /usage response is an array of
these. A fetch failure becomes an entry carrying error (silent-degrade),
never a failure of the whole array.
Fields§
§provider: StringCodexBar provider name (e.g. “codex”), which consumers map to their own id.
api_provider: Option<String>Canonical API provider identifier — the models.dev slug for the same
provider (e.g. “openai” when provider == "codex", “anthropic” for
“claude”, “google” for “gemini”, “xai” for “grok”). Present when the
producer knows the canonical name; absent for providers with no models.dev
counterpart, where consumers fall back to provider. Lets every consumer
key on one canonical name instead of each maintaining its own
CodexBar-name → canonical map.
account: Option<String>The account this entry describes, as the credential store identifies it.
Absent means the producer could not resolve an identity for this credential, not that the provider has one account. Some credentials carry no account identity at all (a bare API key), and an entry is also emitted unlabelled while an identity is still being confirmed — so an unlabelled entry is not evidence that a labelled one does not exist.
source: Option<String>Which retrieval path produced this (e.g. “oauth”) — observability only.
Per lane, not per account, and it moves. One account can be reached through more than one credential path, and which one answers is decided per fetch by whichever is healthy. So the same account can report one value on a poll and another on the next with nothing having changed about the account, the credential, or anything the consumer did. Do not key on it, branch on it, or treat a change in it as an event.
account_info: Option<AccountInfo>Display labels for the account. Absent when the upstream supplies none of them; carries no operational meaning.
fetched_at: Option<String>When this entry’s figures were last successfully fetched — producer time, per entry, never a common instant across the array.
Absent means this credential has never had a successful fetch. It keeps its old value while a failure is being retried, so it ages honestly rather than pausing; never restamp it with your own poll time.
saved_resets: Option<SavedResets>Banked quota-reset credits held by this account.
Absent means there is no credit inventory to report — which includes the inventory lookup having failed on this fetch, since it is separate from the usage fetch and may fail without degrading the entry. Absent is therefore not “zero credits held”.
usage: Option<Usage>The windows. Absent on a degraded entry, and on an entry whose credential
works but whose account reports no quota at all — read error and
error_class to tell those apart, rather than inferring from this field.
spend: Option<Vec<Pool>>Prepaid balances and credit pools on this account, when the provider reports any.
Deliberately apart from Self::usage, because a pool and a rate window
are different facts that fail in opposite directions: over-consuming a
window gets you throttled and recovers by waiting, while over-consuming a
balance gets you billed and recovers by paying. Nothing in a routing loop
can undo the second, so a balance is never expressed as a window, never
carries a reset, and never appears as a percentage — a consumer that
found one where it expects headroom would pace into a bill.
Absent means the producer has nothing to say, which is not the same as an account having no credit. Empty means it looked and the provider reports no pools.
error: Option<String>Present only on a degraded entry. The consumer skips any entry with a
truthy error.
error_class: Option<String>A stable, machine-readable name for why a degraded entry failed,
published beside the human-readable error.
error is prose with no stability promise, so consumers are told not to
branch on it — which leaves them no way to separate failures that mean
something from failures that are a permanent, correct state. A host that
never configured a provider and a host whose credential broke this
morning both produce a degraded entry, and only the second is worth
anyone’s attention.
Classes currently produced:
| Value | Meaning |
|---|---|
credential_absent | No credential was found. Permanent and correct on a host that never configured this provider; nothing to fix. |
credential_unusable | A credential was found but cannot be used as it stands (empty, incomplete, or refused by the credential store). Someone configured this and it needs fixing. |
credential_rejected | The upstream rejected the credential (401/403). Usually means logging in again. |
no_quota_reported | The credential works and the account genuinely has no quota to report. Not a failure. |
upstream_failed | The upstream could not be reached or returned an error status. Usually transient. |
decode_failed | The response arrived but was not the expected shape. |
This list will grow. A consumer must render an unrecognised class as
a degraded entry with an unknown reason — never drop the entry, and
never fold it into an existing bucket. It is a String rather than an
enum for exactly that reason: on an observability surface, meeting an
unknown value must not turn into a parse failure that makes a provider
disappear at the moment its state changed.
Absent on healthy entries, and absent from any producer that predates this field.
stale: Option<Stale>Present when this entry is a last-known-good reading served through an ongoing failure, absent when it is a fresh success.
Without it a preserved reading is byte-identical to a fresh one apart
from fetched_at, so a consumer cannot separate “this figure is old
because the producer has been unable to reach the provider” from “this
figure is old because nothing polled recently”. Those have opposite
remedies — the first is a reason to stop acting on the number, the
second is not — and a consumer with only a timestamp has to guess with a
wall-clock threshold, which denies fresh-enough data to catch stale data.
A producer serving preserved readings is behaving correctly: a brief upstream failure should not blank a window. This field discloses that it is happening rather than reporting a fault.
Implementations§
Source§impl ProviderUsage
impl ProviderUsage
Sourcepub fn healthy(
provider: &str,
account: Option<String>,
source: &str,
usage: Usage,
) -> Self
pub fn healthy( provider: &str, account: Option<String>, source: &str, usage: Usage, ) -> Self
A healthy entry with resolved windows.
Sourcepub fn degraded(provider: &str, error: impl Display) -> Self
pub fn degraded(provider: &str, error: impl Display) -> Self
A degraded entry: the provider is named so the consumer can correlate, but it carries only an error string and no windows.
Sourcepub fn degraded_with_class(
provider: &str,
error: impl Display,
error_class: impl Into<String>,
) -> Self
pub fn degraded_with_class( provider: &str, error: impl Display, error_class: impl Into<String>, ) -> Self
A degraded entry that also names why it failed.
Prefer this over Self::degraded wherever the producer knows the
class: without it a consumer can only tell an unconfigured provider from
a broken one by reading prose it has been told not to parse. See
ProviderUsage::error_class for the classes and for the rule that an
unrecognised one must still render.