Skip to main content

gemini_cost/
usage.rs

1//! Token usage block.
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6/// Three-field token usage as returned by Gemini's `usageMetadata`.
7///
8/// Gemini reports `promptTokenCount` as **including** cached content
9/// tokens, so the [`Usage::from_gemini`] constructor subtracts
10/// `cachedContentTokenCount` from `promptTokenCount` to get the fresh
11/// input count this struct expects.
12#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[cfg_attr(feature = "serde", serde(default))]
15pub struct Usage {
16    /// Fresh input/prompt tokens (not served from the context cache).
17    pub input_tokens: u64,
18    /// Output / candidate tokens.
19    pub output_tokens: u64,
20    /// Input tokens that were served from a context cache (cache hit).
21    pub cached_input_tokens: u64,
22}
23
24impl Usage {
25    /// True when the request hit the context cache.
26    pub fn cache_hit(&self) -> bool {
27        self.cached_input_tokens > 0
28    }
29
30    /// Total tokens billed (input + output + cached_input).
31    pub fn total_tokens(&self) -> u64 {
32        self.input_tokens + self.output_tokens + self.cached_input_tokens
33    }
34
35    /// Build a Usage from a Gemini `usageMetadata` payload.
36    ///
37    /// `prompt_token_count` includes cached tokens on the wire; this
38    /// constructor subtracts them so the struct's fields are disjoint.
39    pub fn from_gemini(
40        prompt_token_count: u64,
41        candidates_token_count: u64,
42        cached_content_token_count: u64,
43    ) -> Self {
44        Self {
45            input_tokens: prompt_token_count.saturating_sub(cached_content_token_count),
46            output_tokens: candidates_token_count,
47            cached_input_tokens: cached_content_token_count,
48        }
49    }
50}