forge_primitives/
token_usage.rs1use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15pub struct ModelPricing {
16 pub input: f64,
17 pub output: f64,
18 pub cache_write_1h: f64,
19 pub cache_write_5m: f64,
20 pub cache_read: f64,
21}
22
23#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
27pub struct UsageRow {
28 pub label: String,
29 pub input: u64,
30 pub cache_write_1h: u64,
31 pub cache_write_5m: u64,
32 pub cache_read: u64,
33 pub output: u64,
34 pub cost_usd: f64,
35}
36
37impl UsageRow {
38 pub fn tokens(&self) -> u64 {
40 self.input
41 .saturating_add(self.cache_write_1h)
42 .saturating_add(self.cache_write_5m)
43 .saturating_add(self.cache_read)
44 .saturating_add(self.output)
45 }
46}
47
48#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
51pub struct WindowUsage {
52 pub by_model: Vec<UsageRow>,
53 pub by_project: Vec<UsageRow>,
54 pub total: UsageRow,
55}
56
57#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60pub struct UsageReport {
61 pub today: WindowUsage,
62 pub week: WindowUsage,
63 pub month: WindowUsage,
64 pub lifetime: WindowUsage,
65 pub pricing_available: bool,
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 fn row(label: &str) -> UsageRow {
77 UsageRow {
78 label: label.to_owned(),
79 input: 1,
80 cache_write_1h: 2,
81 cache_write_5m: 4,
82 cache_read: 8,
83 output: 16,
84 cost_usd: 1.5,
85 }
86 }
87
88 #[test]
89 fn tokens_sums_the_five_fields() {
90 assert_eq!(row("opus").tokens(), 1 + 2 + 4 + 8 + 16);
91 }
92
93 #[test]
94 fn usage_report_round_trips_through_serde() {
95 let window = WindowUsage {
96 by_model: vec![row("opus"), row("sonnet")],
97 by_project: vec![row("forge")],
98 total: row("TOTAL"),
99 };
100 let report = UsageReport {
101 today: window.clone(),
102 week: window.clone(),
103 month: window.clone(),
104 lifetime: window,
105 pricing_available: true,
106 };
107 let json = serde_json::to_string(&report).expect("serialize");
108 let back: UsageReport = serde_json::from_str(&json).expect("deserialize");
109 assert_eq!(report, back);
110 }
111}