use super::json::{VResult, ValidationError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Usage {
pub input_tokens: Option<u64>,
pub output_tokens: Option<u64>,
pub total_tokens: Option<u64>,
pub cache_read_tokens: Option<u64>,
pub cache_write_tokens: Option<u64>,
pub reasoning_tokens: Option<u64>,
pub input_audio_tokens: Option<u64>,
pub output_audio_tokens: Option<u64>,
}
impl Usage {
pub fn normalized(mut self) -> VResult<Usage> {
if self.total_tokens.is_none() {
if let (Some(input), Some(output)) = (self.input_tokens, self.output_tokens) {
self.total_tokens = Some(input.checked_add(output).ok_or_else(|| {
ValidationError::value("Usage.total_tokens overflows: input + output")
})?);
}
}
Ok(self)
}
pub fn is_empty(&self) -> bool {
*self == Usage::default()
}
pub fn validate(&self) -> VResult<()> {
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TopLogprob {
pub token: String,
pub logprob: f64,
pub bytes: Option<Vec<u64>>,
pub token_id: Option<i64>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TokenLogprob {
pub token: String,
pub logprob: f64,
pub bytes: Option<Vec<u64>>,
pub token_id: Option<i64>,
pub top: Vec<TopLogprob>,
}
impl TopLogprob {
pub fn validate(&self) -> VResult<()> {
Ok(())
}
}
impl TokenLogprob {
pub fn validate(&self) -> VResult<()> {
self.top.iter().try_for_each(TopLogprob::validate)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn inv_029_total_auto_computes_only_when_both_present() {
let both = Usage {
input_tokens: Some(1),
output_tokens: Some(2),
..Default::default()
}
.normalized()
.unwrap();
assert_eq!(both.total_tokens, Some(3));
let one = Usage {
input_tokens: Some(7),
..Default::default()
}
.normalized()
.unwrap();
assert_eq!(one.total_tokens, None);
let explicit = Usage {
input_tokens: Some(1),
output_tokens: Some(2),
total_tokens: Some(10),
..Default::default()
}
.normalized()
.unwrap();
assert_eq!(explicit.total_tokens, Some(10));
assert!(Usage::default().is_empty());
assert!(!Usage {
input_tokens: Some(0),
..Default::default()
}
.is_empty());
}
#[test]
fn inv_029_total_overflow_is_a_validation_error() {
let big = Usage {
input_tokens: Some(u64::MAX),
output_tokens: Some(1),
..Default::default()
};
let err = big.normalized().unwrap_err();
assert_eq!(err.type_name(), "ValueError");
let explicit = Usage {
total_tokens: Some(5),
..big
}
.normalized()
.unwrap();
assert_eq!(explicit.total_tokens, Some(5));
}
}