Skip to main content

codex_helper_core/
usage_format.rs

1use crate::usage::UsageMetrics;
2
3pub fn tokens_short(value: i64) -> String {
4    let value = value.max(0) as f64;
5    if value >= 1_000_000_000.0 {
6        format!("{:.1}b", value / 1_000_000_000.0)
7    } else if value >= 1_000_000.0 {
8        format!("{:.1}m", value / 1_000_000.0)
9    } else if value >= 1_000.0 {
10        format!("{:.1}k", value / 1_000.0)
11    } else {
12        format!("{value:.0}")
13    }
14}
15
16pub fn tokens_per_second(value: Option<f64>) -> String {
17    value
18        .filter(|value| value.is_finite() && *value > 0.0)
19        .map(|value| format!("{value:.1}"))
20        .unwrap_or_else(|| "-".to_string())
21}
22
23pub fn usage_line_with_labels(
24    usage: &UsageMetrics,
25    usage_label: &str,
26    cache_label: &str,
27) -> String {
28    let mut line = format!(
29        "{usage_label}: {}/{}/{}/{}",
30        tokens_short(usage.input_tokens),
31        tokens_short(usage.output_tokens),
32        tokens_short(usage.reasoning_output_tokens_total()),
33        tokens_short(usage.total_tokens)
34    );
35    if usage.has_cache_tokens() {
36        line.push_str(&format!(
37            " {cache_label}: {}/{}",
38            tokens_short(usage.cache_read_tokens_total()),
39            tokens_short(usage.cache_creation_tokens_total())
40        ));
41    }
42    line
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn tokens_short_scales_counts() {
51        assert_eq!(tokens_short(-1), "0");
52        assert_eq!(tokens_short(999), "999");
53        assert_eq!(tokens_short(1_500), "1.5k");
54        assert_eq!(tokens_short(1_500_000), "1.5m");
55        assert_eq!(tokens_short(1_500_000_000), "1.5b");
56    }
57
58    #[test]
59    fn tokens_per_second_formats_positive_finite_values() {
60        assert_eq!(tokens_per_second(None), "-");
61        assert_eq!(tokens_per_second(Some(0.0)), "-");
62        assert_eq!(tokens_per_second(Some(f64::INFINITY)), "-");
63        assert_eq!(tokens_per_second(Some(12.34)), "12.3");
64    }
65
66    #[test]
67    fn usage_line_with_labels_includes_cache_when_present() {
68        let usage = UsageMetrics {
69            input_tokens: 1_000,
70            output_tokens: 20,
71            reasoning_output_tokens: 3,
72            total_tokens: 1_023,
73            cached_input_tokens: 10,
74            cache_creation_input_tokens: 5,
75            ..UsageMetrics::default()
76        };
77
78        assert_eq!(
79            usage_line_with_labels(&usage, "tok in/out/rsn/ttl", "cache read/create"),
80            "tok in/out/rsn/ttl: 1.0k/20/3/1.0k cache read/create: 10/5"
81        );
82    }
83}