Codex Handbook
tui/src/token_usage.rs 89 lines
//! TUI token usage models and display formatting.use std::fmt;use codex_protocol::num_format::format_with_separators;use serde::Deserialize;use serde::Serialize;const BASELINE_TOKENS: i64 = 12000;#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]pub struct TokenUsage {    pub input_tokens: i64,    pub cached_input_tokens: i64,    pub output_tokens: i64,    pub reasoning_output_tokens: i64,    pub total_tokens: i64,}impl TokenUsage {    pub fn is_zero(&self) -> bool {        self.total_tokens == 0    }    pub(crate) fn cached_input(&self) -> i64 {        self.cached_input_tokens.max(0)    }    pub(crate) fn non_cached_input(&self) -> i64 {        (self.input_tokens - self.cached_input()).max(0)    }    pub(crate) fn blended_total(&self) -> i64 {        (self.non_cached_input() + self.output_tokens.max(0)).max(0)    }    /// Returns the raw `total_tokens` value. For `last_token_usage`, this is the latest active    /// context size; for `total_token_usage`, this is the accumulated session total.    pub(crate) fn tokens_in_context_window(&self) -> i64 {        self.total_tokens    }    pub(crate) fn percent_of_context_window_remaining(&self, context_window: i64) -> i64 {        if context_window <= BASELINE_TOKENS {            return 0;        }        let effective_window = context_window - BASELINE_TOKENS;        let used = (self.tokens_in_context_window() - BASELINE_TOKENS).max(0);        let remaining = (effective_window - used).max(0);        ((remaining as f64 / effective_window as f64) * 100.0)            .clamp(0.0, 100.0)            .round() as i64    }}#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub(crate) struct TokenUsageInfo {    pub(crate) total_token_usage: TokenUsage,    pub(crate) last_token_usage: TokenUsage,    pub(crate) model_context_window: Option<i64>,}impl fmt::Display for TokenUsage {    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {        write!(            f,            "Token usage: total={} input={}{} output={}{}",            format_with_separators(self.blended_total()),            format_with_separators(self.non_cached_input()),            if self.cached_input() > 0 {                format!(                    " (+ {} cached)",                    format_with_separators(self.cached_input())                )            } else {                String::new()            },            format_with_separators(self.output_tokens),            if self.reasoning_output_tokens > 0 {                format!(                    " (reasoning {})",                    format_with_separators(self.reasoning_output_tokens)                )            } else {                String::new()            }        )    }}