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}