ferrin_spec/language_model/
usage.rs1use serde::Deserialize;
4use serde::Serialize;
5
6use crate::json::JsonObject;
7
8#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
14pub struct Usage {
15 #[serde(default)]
17 pub input: InputTokens,
18 #[serde(default)]
20 pub output: OutputTokens,
21 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub raw: Option<JsonObject>,
24}
25
26#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
28pub struct InputTokens {
29 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub total: Option<u64>,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub no_cache: Option<u64>,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub cache_read: Option<u64>,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub cache_write: Option<u64>,
41}
42
43#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
45pub struct OutputTokens {
46 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub total: Option<u64>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub text: Option<u64>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub reasoning: Option<u64>,
55}
56
57#[must_use]
59pub fn add_token_counts(a: Option<u64>, b: Option<u64>) -> Option<u64> {
60 match (a, b) {
61 (None, None) => None,
62 (Some(a), None) => Some(a),
63 (None, Some(b)) => Some(b),
64 (Some(a), Some(b)) => Some(a.saturating_add(b)),
65 }
66}
67
68impl Usage {
69 #[must_use]
71 pub fn totals(input: u64, output: u64) -> Self {
72 Self {
73 input: InputTokens {
74 total: Some(input),
75 ..InputTokens::default()
76 },
77 output: OutputTokens {
78 total: Some(output),
79 ..OutputTokens::default()
80 },
81 raw: None,
82 }
83 }
84
85 #[must_use]
90 pub fn add(&self, other: &Usage) -> Usage {
91 Usage {
92 input: InputTokens {
93 total: add_token_counts(self.input.total, other.input.total),
94 no_cache: add_token_counts(self.input.no_cache, other.input.no_cache),
95 cache_read: add_token_counts(self.input.cache_read, other.input.cache_read),
96 cache_write: add_token_counts(self.input.cache_write, other.input.cache_write),
97 },
98 output: OutputTokens {
99 total: add_token_counts(self.output.total, other.output.total),
100 text: add_token_counts(self.output.text, other.output.text),
101 reasoning: add_token_counts(self.output.reasoning, other.output.reasoning),
102 },
103 raw: None,
104 }
105 }
106
107 #[must_use]
109 pub fn total_tokens(&self) -> Option<u64> {
110 add_token_counts(self.input.total, self.output.total)
111 }
112}