Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions codex-rs/protocol/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,33 @@ impl TokenUsage {
self.total_tokens
.saturating_sub(self.reasoning_output_tokens.unwrap_or(0))
}

/// Estimate the remaining user-controllable percentage of the model's context window.
///
/// `context_window` is the total size of the model's context window.
/// `baseline_used_tokens` should capture tokens that are always present in
/// the context (e.g., system prompt and fixed tool instructions) so that
/// the percentage reflects the portion the user can influence.
///
/// This normalizes both the numerator and denominator by subtracting the
/// baseline, so immediately after the first prompt the UI shows 100% left
/// and trends toward 0% as the user fills the effective window.
pub fn percent_of_context_window_remaining(
&self,
context_window: u64,
baseline_used_tokens: u64,
) -> u8 {
if context_window <= baseline_used_tokens {
return 0;
}

let effective_window = context_window - baseline_used_tokens;
let used = self
.tokens_in_context_window()
.saturating_sub(baseline_used_tokens);
let remaining = effective_window.saturating_sub(used);
((remaining as f32 / effective_window as f32) * 100.0).clamp(0.0, 100.0) as u8
}
}

#[derive(Debug, Clone, Deserialize, Serialize)]
Expand Down
25 changes: 20 additions & 5 deletions codex-rs/tui/src/bottom_pane/chat_composer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ struct TokenUsageInfo {
total_token_usage: TokenUsage,
last_token_usage: TokenUsage,
model_context_window: Option<u64>,
/// Baseline token count present in the context before the user's first
/// message content is considered. This is used to normalize the
/// "context left" percentage so it reflects the portion the user can
/// influence rather than fixed prompt overhead (system prompt, tool
/// instructions, etc.).
///
/// Preferred source is `cached_input_tokens` from the first turn (when
/// available), otherwise we fall back to 0.
initial_prompt_tokens: u64,
Comment thread
ae-openai marked this conversation as resolved.
}

pub(crate) struct ChatComposer {
Expand Down Expand Up @@ -134,10 +143,17 @@ impl ChatComposer {
last_token_usage: TokenUsage,
model_context_window: Option<u64>,
) {
let initial_prompt_tokens = self
.token_usage_info
.as_ref()
.map(|info| info.initial_prompt_tokens)
.unwrap_or_else(|| last_token_usage.cached_input_tokens.unwrap_or(0));

self.token_usage_info = Some(TokenUsageInfo {
total_token_usage,
last_token_usage,
model_context_window,
initial_prompt_tokens,
});
}

Expand Down Expand Up @@ -673,11 +689,10 @@ impl WidgetRef for &ChatComposer {
let last_token_usage = &token_usage_info.last_token_usage;
if let Some(context_window) = token_usage_info.model_context_window {
let percent_remaining: u8 = if context_window > 0 {
let percent = 100.0
- (last_token_usage.tokens_in_context_window() as f32
/ context_window as f32
* 100.0);
percent.clamp(0.0, 100.0) as u8
last_token_usage.percent_of_context_window_remaining(
context_window,
token_usage_info.initial_prompt_tokens,
)
Comment thread
ae-openai marked this conversation as resolved.
} else {
100
};
Expand Down