pub struct ProviderCall {
pub step: u32,
pub attempt: u32,
pub provider: String,
pub model: Option<String>,
pub usage: Option<Usage>,
pub latency_ms: u64,
pub ttft_ms: Option<u64>,
pub finish_reason: Option<String>,
pub failure: Option<String>,
}Expand description
One call to a provider: what it cost, which model served it, and how long it took (0.18.0).
Per call, not per step. A step that failed twice and then answered is
three of these, and the two that failed are kept because a model that
produced tokens before erroring was still billed for them. steps.tokens
holds one integer per step and cannot express any of that.
use io_harness::{ProviderCall, Store, Usage};
// The first attempt died after the model had already produced tokens...
store.record_provider_call(run_id, &ProviderCall {
step: 1,
attempt: 0,
provider: "anthropic".into(),
model: Some("claude-sonnet-4".into()),
usage: Some(Usage { prompt_tokens: 900, completion_tokens: 20, total_tokens: 920,
..Default::default() }),
latency_ms: 4_100,
failure: Some("Server (HTTP 529)".into()),
..Default::default()
})?;
// ...and the retry answered.
store.record_provider_call(run_id, &ProviderCall {
step: 1,
attempt: 1,
provider: "anthropic".into(),
model: Some("claude-sonnet-4".into()),
usage: Some(Usage { prompt_tokens: 900, completion_tokens: 310, total_tokens: 1_210,
cache_read_tokens: 850, ..Default::default() }),
latency_ms: 2_300,
ttft_ms: Some(420),
finish_reason: Some("end_turn".into()),
..Default::default()
})?;
let calls = store.provider_calls(run_id)?;
assert_eq!(calls.len(), 2);
// What the step actually cost is the sum over its calls — including the one
// that failed, which a trace keeping only the winner would have hidden.
let billed: u64 = calls.iter().filter_map(|c| c.usage).map(|u| u.total_tokens).sum();
assert_eq!(billed, 2_130);
assert_eq!(calls[0].failure.as_deref(), Some("Server (HTTP 529)"));Fields§
§step: u32The step this call was made for.
attempt: u32Which attempt at that step this was, counting from 0.
provider: StringThe provider that was asked, by Provider::name.
For a Fallback this is the combined label;
ProviderCall::model is what identifies who actually answered.
model: Option<String>The model that served the call, as the provider named it. None when it
did not say — a custom provider, or a wire that omits it.
usage: Option<Usage>Tokens, as reported. None when the provider reported none, which is not
the same fact as zero.
latency_ms: u64Milliseconds the whole call took, measured by the harness around
Provider::complete — so it includes the
crate’s own request building and stream consumption, not only the
provider’s part.
ttft_ms: Option<u64>Milliseconds to the first content-bearing chunk, where the path streamed
and measured it. None, never zero, when nothing measured it.
finish_reason: Option<String>The provider’s own word for why the model stopped, verbatim.
failure: Option<String>None when the call answered; the failure’s short name when it did not.
Trait Implementations§
Source§impl Clone for ProviderCall
impl Clone for ProviderCall
Source§fn clone(&self) -> ProviderCall
fn clone(&self) -> ProviderCall
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ProviderCall
impl Debug for ProviderCall
Source§impl Default for ProviderCall
impl Default for ProviderCall
Source§fn default() -> ProviderCall
fn default() -> ProviderCall
impl Eq for ProviderCall
Source§impl PartialEq for ProviderCall
impl PartialEq for ProviderCall
impl StructuralPartialEq for ProviderCall
Auto Trait Implementations§
impl Freeze for ProviderCall
impl RefUnwindSafe for ProviderCall
impl Send for ProviderCall
impl Sync for ProviderCall
impl Unpin for ProviderCall
impl UnsafeUnpin for ProviderCall
impl UnwindSafe for ProviderCall
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
ReadEndian::read_from_little_endian().impl<T> Scalar for T
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.